From c675720b42219451ffdb925dbf1ccd41ea01069e Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 27 Jul 2026 15:17:06 -0400 Subject: [PATCH 01/18] emerald legendary run away config options --- .../PokemonRSE/PokemonRSE_Navigation.cpp | 77 +++++++--- .../Source/PokemonRSE/PokemonRSE_Settings.cpp | 2 +- .../PokemonRSE_LegendaryHunt-Emerald.cpp | 145 ++++++++++++------ .../PokemonRSE_LegendaryHunt-Emerald.h | 11 ++ 4 files changed, 162 insertions(+), 73 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index 590a2779b3..c5b88e6246 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -28,6 +28,61 @@ namespace NintendoSwitch{ namespace PokemonRSE{ + +void home_black_border_check(ConsoleHandle& console, ProControllerContext& context){ + if (GameSettings::instance().DEVICE == GameSettings::Device::switch_1_2){ + console.log("Switch 1 or 2 selected in Settings."); + + console.log("Checking for min 720p and 16:9."); + assert_16_9_720p_min(console, console); + + console.log("Going to home to check for black border."); + + // Connect the controller. + require_player(console, context, BUTTON_ZL); + + pbf_press_button(context, BUTTON_HOME, 120ms, 880ms); + try{ + ensure_at_home(console, context, 2); + }catch (OperationFailedException&){ + ControllerPlayerNumber current = context->get_player_number(context); + if (current == ControllerPlayerNumber::UNKNOWN){ + throw UserSetupError( + console, + "Unable to find Home menu.\n\n" + "Either your controller isn't connected or your screen size to not " + "set to 100% in the TV Settings on your Nintendo Switch.\n\n" + "If your Switch entered the Home screen and re-entered the game, then your " + "controller is connected but your screen size is not set to 100%.\n\n" + "If nothing happened at all, then your controller is not connected. " + "Please disconnect all other controllers and try again.\n\n" + "We recommend changing the controller to \"NS1: Wired Pro Controller\" " + "as that will be able self-diagnose controller connection issues." + ); + }else{ + throw UserSetupError( + console, + "Unable to find Home menu.\n\n" + "It is likely your screen size to not set to 100% in the TV Settings on your Nintendo Switch." + ); + } + } + +// context.wait_for_all_requests(); + StartProgramChecks::check_border(console); + console.log("Returning to game."); + resume_game_from_home(console, context); + context.wait_for_all_requests(); + console.log("Entered game."); + }else{ + console.log("Non-Switch device selected in Settings."); + console.log("Skipping black border check.", COLOR_BLUE); + } +} + + + + bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ // A + B + Select + Start pbf_press_button(context, BUTTON_B | BUTTON_A | BUTTON_MINUS | BUTTON_PLUS, 360ms, 1440ms); @@ -210,28 +265,6 @@ bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, boo return false; } -void home_black_border_check(ConsoleHandle& console, ProControllerContext& context){ - if (GameSettings::instance().DEVICE == GameSettings::Device::switch_1_2){ - console.log("Switch 1 or 2 selected in Settings."); - - console.log("Checking for min 720p and 16:9."); - assert_16_9_720p_min(console, console); - - console.log("Going to home to check for black border."); - pbf_press_button(context, BUTTON_ZL, 120ms, 880ms); // Connect the controller. - pbf_press_button(context, BUTTON_HOME, 120ms, 880ms); - context.wait_for_all_requests(); - StartProgramChecks::check_border(console); - console.log("Returning to game."); - resume_game_from_home(console, context); - context.wait_for_all_requests(); - console.log("Entered game."); - }else{ - console.log("Non-Switch device selected in Settings."); - console.log("Skipping black border and aspect ratio checks.", COLOR_BLUE); - } -} - } } diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp index 4097ab15eb..b842247228 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp @@ -36,7 +36,7 @@ GameSettings::GameSettings() "Refer to the documentation for specific setups.", { {Device::switch_1_2, "switch_1_2", "Nintendo Switch 1 and 2"}, - //{Device::rg35xx, "rg35xx", "RG35XX"}, + {Device::rg35xx, "rg35xx", "RG35XX"}, //{Device::custom, "custom", "Custom"}, }, LockMode::LOCK_WHILE_RUNNING, diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp index 36981f541c..4aff1c3db9 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp @@ -66,7 +66,7 @@ LegendaryHuntEmerald::LegendaryHuntEmerald() LockMode::LOCK_WHILE_RUNNING, Target::regis ) - , TAKE_VIDEO("Take Video:
Record a video when the shiny starter is found.", LockMode::UNLOCK_WHILE_RUNNING, true) + , TAKE_VIDEO("Take Video:
Record a video when the shiny is found.", LockMode::UNLOCK_WHILE_RUNNING, true) , GO_HOME_WHEN_DONE(true) , NOTIFICATION_SHINY( "Shiny Found", @@ -79,12 +79,58 @@ LegendaryHuntEmerald::LegendaryHuntEmerald() &NOTIFICATION_STATUS_UPDATE, &NOTIFICATION_PROGRAM_FINISH, }) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , KYOGRE_RIGHT_FIRST( + "Kyogre first left/right time:
Time it takes to turn and run 9 steps right/left after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "1280 ms" + ) + , KYOGRE_UP_FIRST( + "Kyogre first up/down time:
Time it takes to turn and run 10 steps up/down after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "1440 ms" + ) + , KYOGRE_RIGHT_SECOND( + "Kyogre second left/right time:
Time it takes to turn and run 6 steps right/left after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "840 ms" + ) + , HOOH_UP_DOWN( + "Ho-Oh up/down time:
Time it takes to run up to Ho-Oh or down away to reset.", + LockMode::LOCK_WHILE_RUNNING, + "1440 ms" + ) + , HOOH_LEFT_RIGHT( + "Ho-Oh left/right time:
Time it takes when facing the same direction to take one step left or right.", + LockMode::LOCK_WHILE_RUNNING, + "240 ms" + ) + , LUGIA_UP_DOWN0( + "Lugia up/down time:
Time it takes to run up to Lugia or down away to reset.", + LockMode::LOCK_WHILE_RUNNING, + "720 ms" + ) + , LUGIA_LEFT_RIGHT0( + "Lugia left time:
Time it takes walk three steps left after entering Lugia's room.", + LockMode::LOCK_WHILE_RUNNING, + "520 ms" + ) { PA_ADD_STATIC(SHINY_REQUIRES_AUDIO); PA_ADD_OPTION(TARGET); PA_ADD_OPTION(TAKE_VIDEO); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); + PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(KYOGRE_RIGHT_FIRST); + PA_ADD_OPTION(KYOGRE_RIGHT_SECOND); + PA_ADD_OPTION(KYOGRE_UP_FIRST); + PA_ADD_OPTION(HOOH_UP_DOWN); + PA_ADD_OPTION(HOOH_LEFT_RIGHT); + PA_ADD_OPTION(LUGIA_UP_DOWN0); + PA_ADD_OPTION(LUGIA_LEFT_RIGHT0); } void LegendaryHuntEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ @@ -249,29 +295,29 @@ void LegendaryHuntEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, Pr void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); - //Turn down. Take 1 step. - ssf_press_button(context, BUTTON_B, 0ms, 160ms); - pbf_press_dpad(context, DPAD_DOWN, 160ms, 0ms); + //Turn down. Take 1 step and hit the wall. No config needed. + ssf_press_button(context, BUTTON_B, 0ms, 200ms); + pbf_press_dpad(context, DPAD_DOWN, 200ms, 0ms); //Turn right. Take 9 steps. - ssf_press_button(context, BUTTON_B, 0ms, 1280ms); - pbf_press_dpad(context, DPAD_RIGHT, 1280ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_FIRST); + pbf_press_dpad(context, DPAD_RIGHT, KYOGRE_RIGHT_FIRST, 0ms); - //Turn up. 13 steps. Wall. - ssf_press_button(context, BUTTON_B, 0ms, 1760ms); - pbf_press_dpad(context, DPAD_UP, 1760ms, 0ms); + //Turn up. 13 steps. Wall. No config needed. + ssf_press_button(context, BUTTON_B, 0ms, 1800ms); + pbf_press_dpad(context, DPAD_UP, 1800ms, 0ms); - //Turn left. 4 steps. Wall. - ssf_press_button(context, BUTTON_B, 0ms, 640ms); - pbf_press_dpad(context, DPAD_LEFT, 640ms, 0ms); + //Turn left. 4 steps. Wall. No config needed. + ssf_press_button(context, BUTTON_B, 0ms, 680ms); + pbf_press_dpad(context, DPAD_LEFT, 680ms, 0ms); //Turn up. 10 steps. - ssf_press_button(context, BUTTON_B, 0ms, 1440ms); - pbf_press_dpad(context, DPAD_UP, 1440ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_UP_FIRST); + pbf_press_dpad(context, DPAD_UP, KYOGRE_UP_FIRST, 0ms); //Turn right. 6 steps. - ssf_press_button(context, BUTTON_B, 0ms, 880ms); - pbf_press_dpad(context, DPAD_RIGHT, 880ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_SECOND); + pbf_press_dpad(context, DPAD_RIGHT, KYOGRE_RIGHT_SECOND, 0ms); //Turn down. Exit. Black screen over. BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); @@ -326,23 +372,23 @@ void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, Pro pbf_wait(context, 500ms); context.wait_for_all_requests(); - ssf_press_button(context, BUTTON_B, 0ms, 880ms); - pbf_press_dpad(context, DPAD_LEFT, 880ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_SECOND); + pbf_press_dpad(context, DPAD_LEFT, KYOGRE_RIGHT_SECOND, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 1440ms); - pbf_press_dpad(context, DPAD_DOWN, 1440ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_UP_FIRST); + pbf_press_dpad(context, DPAD_DOWN, KYOGRE_UP_FIRST, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 640ms); - pbf_press_dpad(context, DPAD_RIGHT, 640ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 680ms); + pbf_press_dpad(context, DPAD_RIGHT, 680ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 1760ms); - pbf_press_dpad(context, DPAD_DOWN, 1760ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 1800ms); + pbf_press_dpad(context, DPAD_DOWN, 1800ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 1280ms); - pbf_press_dpad(context, DPAD_LEFT, 1280ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_FIRST); + pbf_press_dpad(context, DPAD_LEFT, KYOGRE_RIGHT_FIRST, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 160ms); - pbf_press_dpad(context, DPAD_UP, 160ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 200ms); + pbf_press_dpad(context, DPAD_UP, 200ms, 0ms); context.wait_for_all_requests(); } @@ -352,16 +398,16 @@ void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProCo BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); //Turn around, 10 steps down - ssf_press_button(context, BUTTON_B, 0ms, 1440ms); - pbf_press_dpad(context, DPAD_DOWN, 1440ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, HOOH_UP_DOWN); + pbf_press_dpad(context, DPAD_DOWN, HOOH_UP_DOWN, 0ms); context.wait_for_all_requests(); //Turn right, take 1 step. Wait for black screen over. int ret = run_until( env.console, context, [](ProControllerContext& context){ - ssf_press_button(context, BUTTON_B, 0ms, 240ms); - pbf_press_dpad(context, DPAD_RIGHT, 240ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_RIGHT, 500ms, 0ms); pbf_wait(context, 2400ms); }, {exit_area} @@ -387,11 +433,11 @@ void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProCo int ret2 = run_until( env.console, context, [](ProControllerContext& context){ - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_LEFT, 320ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_LEFT, 500ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_RIGHT, 320ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_RIGHT, 500ms, 0ms); pbf_wait(context, 2400ms); }, {enter_area} @@ -414,11 +460,10 @@ void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProCo //reverse above steps, but only take 9 steps up //doesn't really matter since we want to trigger the encounter anyway - ssf_press_button(context, BUTTON_B, 0ms, 240ms); - pbf_press_dpad(context, DPAD_LEFT, 240ms, 160ms); + pbf_press_dpad(context, DPAD_LEFT, HOOH_LEFT_RIGHT, 160ms); - ssf_press_button(context, BUTTON_B, 0ms, 1360ms); - pbf_press_dpad(context, DPAD_UP, 1360ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, HOOH_UP_DOWN); + pbf_press_dpad(context, DPAD_UP, HOOH_UP_DOWN, 160ms); context.wait_for_all_requests(); } @@ -428,8 +473,8 @@ void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProC BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); //Turn around, 5 steps down - ssf_press_button(context, BUTTON_B, 0ms, 720ms); - pbf_press_dpad(context, DPAD_DOWN, 720ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_UP_DOWN0); + pbf_press_dpad(context, DPAD_DOWN, LUGIA_UP_DOWN0, 0ms); context.wait_for_all_requests(); //Turn right, 3 steps right. Wait for black screen over. @@ -463,11 +508,11 @@ void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProC int ret2 = run_until( env.console, context, [](ProControllerContext& context){ - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_UP, 320ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_UP, 500ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_DOWN, 320ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_DOWN, 500ms, 0ms); pbf_wait(context, 2400ms); }, {enter_area} @@ -489,11 +534,11 @@ void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProC context.wait_for_all_requests(); //reverse above steps - ssf_press_button(context, BUTTON_B, 0ms, 560ms); - pbf_press_dpad(context, DPAD_LEFT, 560ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_LEFT_RIGHT0); + pbf_press_dpad(context, DPAD_LEFT, LUGIA_LEFT_RIGHT0, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 720ms); - pbf_press_dpad(context, DPAD_UP, 720ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_UP_DOWN0); + pbf_press_dpad(context, DPAD_UP, LUGIA_UP_DOWN0, 0ms); context.wait_for_all_requests(); } diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h index 09c1f7562c..15eead60a4 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h @@ -53,6 +53,17 @@ class LegendaryHuntEmerald : public SingleSwitchProgramInstance{ EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS; + //For easier testing after release + //regis are up/down only and don't really need + SectionDividerOption m_advanced_options; + MillisecondsOption KYOGRE_RIGHT_FIRST; + MillisecondsOption KYOGRE_UP_FIRST; + MillisecondsOption KYOGRE_RIGHT_SECOND; + MillisecondsOption HOOH_UP_DOWN; + MillisecondsOption HOOH_LEFT_RIGHT; + MillisecondsOption LUGIA_UP_DOWN0; + MillisecondsOption LUGIA_LEFT_RIGHT0; + void reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void reset_groudon(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context); From 1a5cbc4a8b7baf4bbd21788bae22ac1645003879 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Wed, 29 Jul 2026 13:57:06 -0400 Subject: [PATCH 02/18] rename run away, finish config options --- .../Source/PokemonRSE/PokemonRSE_Panels.cpp | 4 +- ...> PokemonRSE_LegendaryRunAway-Emerald.cpp} | 122 ++++++++++++------ ... => PokemonRSE_LegendaryRunAway-Emerald.h} | 15 ++- SerialPrograms/cmake/SourceFiles.cmake | 4 +- 4 files changed, 92 insertions(+), 53 deletions(-) rename SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/{PokemonRSE_LegendaryHunt-Emerald.cpp => PokemonRSE_LegendaryRunAway-Emerald.cpp} (80%) rename SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/{PokemonRSE_LegendaryHunt-Emerald.h => PokemonRSE_LegendaryRunAway-Emerald.h} (79%) diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp index 5e25ea7cc7..aaa8875a6b 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp @@ -11,7 +11,7 @@ #include "PokemonRSE_Settings.h" #include "Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h" -#include "Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h" +#include "Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h" @@ -43,7 +43,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Shiny Hunting (Emerald) ----"); - ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp similarity index 80% rename from SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp rename to SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp index 4aff1c3db9..ff6bc889a4 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp @@ -1,4 +1,4 @@ -/* Legendary Hunt - Emerald +/* Legendary Run Away - Emerald * * From: https://github.com/PokemonAutomation/ * @@ -17,17 +17,17 @@ #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" -#include "PokemonRSE_LegendaryHunt-Emerald.h" +#include "PokemonRSE_LegendaryRunAway-Emerald.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ -LegendaryHuntEmerald_Descriptor::LegendaryHuntEmerald_Descriptor() +LegendaryRunAwayEmerald_Descriptor::LegendaryRunAwayEmerald_Descriptor() : SingleSwitchProgramDescriptor( - "PokemonRSE:LegendaryHuntEmerald", - Pokemon::STRING_POKEMON + " RSE", "Legendary Hunt (Emerald)", - "Programs/PokemonRSE/LegendaryHuntEmerald.html", + "PokemonRSE:LegendaryRunAwayEmerald", + Pokemon::STRING_POKEMON + " RSE", "Legendary Run Away (Emerald)", + "Programs/PokemonRSE/LegendaryRunAwayEmerald.html", "Use the Run Away method to shiny hunt legendaries in Emerald.", ProgramControllerClass::StandardController_NoRestrictions, FeedbackType::VIDEO_AUDIO, @@ -35,7 +35,7 @@ LegendaryHuntEmerald_Descriptor::LegendaryHuntEmerald_Descriptor() ) {} -struct LegendaryHuntEmerald_Descriptor::Stats : public StatsTracker{ +struct LegendaryRunAwayEmerald_Descriptor::Stats : public StatsTracker{ Stats() : resets(m_stats["Resets"]) , shinies(m_stats["Shinies"]) @@ -49,11 +49,11 @@ struct LegendaryHuntEmerald_Descriptor::Stats : public StatsTracker{ std::atomic& shinies; std::atomic& errors; }; -std::unique_ptr LegendaryHuntEmerald_Descriptor::make_stats() const{ +std::unique_ptr LegendaryRunAwayEmerald_Descriptor::make_stats() const{ return std::unique_ptr(new Stats()); } -LegendaryHuntEmerald::LegendaryHuntEmerald() +LegendaryRunAwayEmerald::LegendaryRunAwayEmerald() : TARGET( "Target:
", { @@ -82,20 +82,35 @@ LegendaryHuntEmerald::LegendaryHuntEmerald() , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) + , GROUDON_LEFT_FIRST( + "Groudon first left/right time:
Time it takes to turn and run 10 steps left/right after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "1440 ms" + ) + , GROUDON_RIGHT_FIRST( + "Groudon second right/left time:
Time it takes to turn and run 2 steps right/left after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "280 ms" + ) + , GROUDON_LEFT_SECOND( + "Groudon third left/right time:
Time it takes to turn and run 4 steps left/right after/before the encounter.", + LockMode::LOCK_WHILE_RUNNING, + "600 ms" + ) , KYOGRE_RIGHT_FIRST( "Kyogre first left/right time:
Time it takes to turn and run 9 steps right/left after/before the encounter.", LockMode::LOCK_WHILE_RUNNING, - "1280 ms" + "1180 ms" ) , KYOGRE_UP_FIRST( - "Kyogre first up/down time:
Time it takes to turn and run 10 steps up/down after/before the encounter.", + "Kyogre up/down time:
Time it takes to turn and run 10 steps up/down after/before the encounter.", LockMode::LOCK_WHILE_RUNNING, - "1440 ms" + "1400 ms" ) , KYOGRE_RIGHT_SECOND( "Kyogre second left/right time:
Time it takes to turn and run 6 steps right/left after/before the encounter.", LockMode::LOCK_WHILE_RUNNING, - "840 ms" + "830 ms" ) , HOOH_UP_DOWN( "Ho-Oh up/down time:
Time it takes to run up to Ho-Oh or down away to reset.", @@ -124,17 +139,20 @@ LegendaryHuntEmerald::LegendaryHuntEmerald() PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(GROUDON_LEFT_FIRST); + PA_ADD_OPTION(GROUDON_RIGHT_FIRST); + PA_ADD_OPTION(GROUDON_LEFT_SECOND); PA_ADD_OPTION(KYOGRE_RIGHT_FIRST); - PA_ADD_OPTION(KYOGRE_RIGHT_SECOND); PA_ADD_OPTION(KYOGRE_UP_FIRST); + PA_ADD_OPTION(KYOGRE_RIGHT_SECOND); PA_ADD_OPTION(HOOH_UP_DOWN); PA_ADD_OPTION(HOOH_LEFT_RIGHT); PA_ADD_OPTION(LUGIA_UP_DOWN0); PA_ADD_OPTION(LUGIA_LEFT_RIGHT0); } -void LegendaryHuntEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); //turn around, walk down 4/until black screen over BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); @@ -161,7 +179,7 @@ void LegendaryHuntEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProCo }else{ env.log("Left area."); } - pbf_wait(context, 400ms); + pbf_wait(context, 500ms); context.wait_for_all_requests(); //turn around, up one/black screen over @@ -196,28 +214,32 @@ void LegendaryHuntEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProCo context.wait_for_all_requests(); } -void LegendaryHuntEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); //Turn left. Take 10 steps. - ssf_press_button(context, BUTTON_B, 0ms, 1440ms); - pbf_press_dpad(context, DPAD_LEFT, 1440ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_LEFT_FIRST); + pbf_press_dpad(context, DPAD_LEFT, GROUDON_LEFT_FIRST, 0ms); + context.wait_for_all_requests(); //Turn up. Take 14 steps. (Bump into wall.) ssf_press_button(context, BUTTON_B, 0ms, 1920ms); pbf_press_dpad(context, DPAD_UP, 1920ms, 0ms); + context.wait_for_all_requests(); //Turn right. Take 2 steps. - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_RIGHT, 320ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_RIGHT_FIRST); + pbf_press_dpad(context, DPAD_RIGHT, GROUDON_RIGHT_FIRST, 0ms); + context.wait_for_all_requests(); //Turn up. Take 8 steps (Bump into wall.) ssf_press_button(context, BUTTON_B, 0ms, 1120ms); pbf_press_dpad(context, DPAD_UP, 1120ms, 0ms); + context.wait_for_all_requests(); //Turn left. Take 4 steps. - ssf_press_button(context, BUTTON_B, 0ms, 640ms); - pbf_press_dpad(context, DPAD_LEFT, 640ms, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_LEFT_SECOND); + pbf_press_dpad(context, DPAD_LEFT, GROUDON_LEFT_SECOND, 0ms); context.wait_for_all_requests(); //Turn down. Exit. Black screen over. @@ -244,7 +266,7 @@ void LegendaryHuntEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, Pr }else{ env.log("Left area."); } - pbf_wait(context, 500ms); + pbf_wait(context, 1000ms); context.wait_for_all_requests(); //Reverse above steps. @@ -274,50 +296,59 @@ void LegendaryHuntEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, Pr pbf_wait(context, 500ms); context.wait_for_all_requests(); - ssf_press_button(context, BUTTON_B, 0ms, 640ms); - pbf_press_dpad(context, DPAD_RIGHT, 640ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_LEFT_SECOND); + pbf_press_dpad(context, DPAD_RIGHT, GROUDON_LEFT_SECOND, 160ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, 1120ms); pbf_press_dpad(context, DPAD_DOWN, 1120ms, 160ms); + context.wait_for_all_requests(); - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_LEFT, 320ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_RIGHT_FIRST); + pbf_press_dpad(context, DPAD_LEFT, GROUDON_RIGHT_FIRST, 160ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, 1920ms); pbf_press_dpad(context, DPAD_DOWN, 1920ms, 160ms); + context.wait_for_all_requests(); - ssf_press_button(context, BUTTON_B, 0ms, 1440ms); - pbf_press_dpad(context, DPAD_RIGHT, 1440ms, 160ms); - + ssf_press_button(context, BUTTON_B, 0ms, GROUDON_LEFT_FIRST); + pbf_press_dpad(context, DPAD_RIGHT, GROUDON_LEFT_FIRST, 160ms); context.wait_for_all_requests(); } -void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); //Turn down. Take 1 step and hit the wall. No config needed. ssf_press_button(context, BUTTON_B, 0ms, 200ms); pbf_press_dpad(context, DPAD_DOWN, 200ms, 0ms); + context.wait_for_all_requests(); //Turn right. Take 9 steps. ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_FIRST); pbf_press_dpad(context, DPAD_RIGHT, KYOGRE_RIGHT_FIRST, 0ms); + context.wait_for_all_requests(); //Turn up. 13 steps. Wall. No config needed. ssf_press_button(context, BUTTON_B, 0ms, 1800ms); pbf_press_dpad(context, DPAD_UP, 1800ms, 0ms); + context.wait_for_all_requests(); //Turn left. 4 steps. Wall. No config needed. ssf_press_button(context, BUTTON_B, 0ms, 680ms); pbf_press_dpad(context, DPAD_LEFT, 680ms, 0ms); + context.wait_for_all_requests(); //Turn up. 10 steps. ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_UP_FIRST); pbf_press_dpad(context, DPAD_UP, KYOGRE_UP_FIRST, 0ms); + context.wait_for_all_requests(); //Turn right. 6 steps. ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_SECOND); pbf_press_dpad(context, DPAD_RIGHT, KYOGRE_RIGHT_SECOND, 0ms); + context.wait_for_all_requests(); //Turn down. Exit. Black screen over. BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); @@ -343,7 +374,7 @@ void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, Pro }else{ env.log("Left area."); } - pbf_wait(context, 500ms); + pbf_wait(context, 1000ms); context.wait_for_all_requests(); BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); @@ -356,6 +387,7 @@ void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, Pro }, {enter_area} ); + pbf_wait(context, 500ms); context.wait_for_all_requests(); if (ret2 != 0){ env.log("Failed to enter area.", COLOR_RED); @@ -374,27 +406,31 @@ void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, Pro ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_SECOND); pbf_press_dpad(context, DPAD_LEFT, KYOGRE_RIGHT_SECOND, 0ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_UP_FIRST); pbf_press_dpad(context, DPAD_DOWN, KYOGRE_UP_FIRST, 0ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, 680ms); pbf_press_dpad(context, DPAD_RIGHT, 680ms, 0ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, 1800ms); pbf_press_dpad(context, DPAD_DOWN, 1800ms, 0ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, KYOGRE_RIGHT_FIRST); pbf_press_dpad(context, DPAD_LEFT, KYOGRE_RIGHT_FIRST, 0ms); + context.wait_for_all_requests(); ssf_press_button(context, BUTTON_B, 0ms, 200ms); pbf_press_dpad(context, DPAD_UP, 200ms, 0ms); - context.wait_for_all_requests(); } -void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); //Turn around, 10 steps down @@ -468,8 +504,8 @@ void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProCo context.wait_for_all_requests(); } -void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); //Turn around, 5 steps down @@ -543,8 +579,8 @@ void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProC context.wait_for_all_requests(); } -void LegendaryHuntEmerald::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); +void LegendaryRunAwayEmerald::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); home_black_border_check(env.console, context); diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h similarity index 79% rename from SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h rename to SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h index 15eead60a4..667b8de794 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h @@ -4,8 +4,8 @@ * */ -#ifndef PokemonAutomation_PokemonRSE_LegendaryHuntEmerald_H -#define PokemonAutomation_PokemonRSE_LegendaryHuntEmerald_H +#ifndef PokemonAutomation_PokemonRSE_LegendaryRunAwayEmerald_H +#define PokemonAutomation_PokemonRSE_LegendaryRunAwayEmerald_H #include "CommonFramework/Notifications/EventNotificationsTable.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" @@ -17,16 +17,16 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ -class LegendaryHuntEmerald_Descriptor : public SingleSwitchProgramDescriptor{ +class LegendaryRunAwayEmerald_Descriptor : public SingleSwitchProgramDescriptor{ public: - LegendaryHuntEmerald_Descriptor(); + LegendaryRunAwayEmerald_Descriptor(); struct Stats; virtual std::unique_ptr make_stats() const override; }; -class LegendaryHuntEmerald : public SingleSwitchProgramInstance{ +class LegendaryRunAwayEmerald : public SingleSwitchProgramInstance{ public: - LegendaryHuntEmerald(); + LegendaryRunAwayEmerald(); virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; virtual void start_program_border_check( @@ -56,6 +56,9 @@ class LegendaryHuntEmerald : public SingleSwitchProgramInstance{ //For easier testing after release //regis are up/down only and don't really need SectionDividerOption m_advanced_options; + MillisecondsOption GROUDON_LEFT_FIRST; + MillisecondsOption GROUDON_RIGHT_FIRST; + MillisecondsOption GROUDON_LEFT_SECOND; MillisecondsOption KYOGRE_RIGHT_FIRST; MillisecondsOption KYOGRE_UP_FIRST; MillisecondsOption KYOGRE_RIGHT_SECOND; diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index d615f05689..c0581b3725 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2076,8 +2076,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/PokemonRSE_Settings.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h - Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp - Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp From f5aaf6064269560695d7eb1d6b386e274473974c Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Wed, 29 Jul 2026 19:41:34 -0400 Subject: [PATCH 03/18] RSE load menu detector --- .../Menus/PokemonRSE_LoadMenuDetector.cpp | 57 +++++++++++++++++++ .../Menus/PokemonRSE_LoadMenuDetector.h | 50 ++++++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 3 files changed, 109 insertions(+) create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp new file mode 100644 index 0000000000..e8c159da80 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp @@ -0,0 +1,57 @@ +/* Load Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "PokemonRSE/PokemonRSE_Settings.h" +#include "PokemonRSE_LoadMenuDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +LoadMenuDetector::LoadMenuDetector(Color color) + : m_right_box(0.975, 0.02, 0.015, 0.96) + , m_left_box(0.01, 0.02, 0.015, 0.96) + , m_save_box(0.751685, 0.053648, 0.167558, 0.05736) +{} +void LoadMenuDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_BLUE, GAME_BOX.inner_to_outer(m_right_box)); + items.add(COLOR_BLUE, GAME_BOX.inner_to_outer(m_left_box)); + items.add(COLOR_BLUE, GAME_BOX.inner_to_outer(m_save_box)); +} +bool LoadMenuDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 right_image = extract_box_reference(game_screen, m_right_box); + ImageViewRGB32 left_image = extract_box_reference(game_screen, m_left_box); + ImageViewRGB32 save_image = extract_box_reference(game_screen, m_save_box); + + //Ruby and Sapphire + if ((is_solid(right_image, { 0.254, 0.274576, 0.471186 }, 0.25, 2) //75, 81, 139 + && is_solid(left_image, { 0.254, 0.274576, 0.471186 }, 0.25, 2) + && is_white(save_image)) //white + || + (is_solid(right_image, { 0.259259, 0.27037, 0.47037 }, 0.25, 2) //Emerald + && is_solid(right_image, { 0.259259, 0.27037, 0.47037 }, 0.25, 2) //140, 146, 254 + && is_white(save_image) + ) + ){ + return true; + } + return false; +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h new file mode 100644 index 0000000000..1ab7aa4aae --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h @@ -0,0 +1,50 @@ +/* Load Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_LoadMenuDetector_H +#define PokemonAutomation_PokemonRSE_LoadMenuDetector_H + +#include +#include +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonTools/VisualDetector.h" +#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonRSE{ + +// Detect save load menu by looking for the blue on the sides and the white of the save file +// Untested on new game, assumes there's a save. +class LoadMenuDetector : public StaticScreenDetector{ +public: + LoadMenuDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_right_box; + ImageFloatBox m_left_box; + ImageFloatBox m_save_box; +}; +class LoadMenuWatcher : public DetectorToFinder{ +public: + LoadMenuWatcher(Color color) + : DetectorToFinder("LoadMenuWatcher", std::chrono::milliseconds(250), color) + {} +}; + + +} +} +} + +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index c0581b3725..db69005e63 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2064,6 +2064,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonPokopia/Programs/PokemonPokopia_PCNavigation.h Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h + Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp + Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp From c9522fbd0491f6ad38d26c5d4767e88718834477 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 11:42:25 -0400 Subject: [PATCH 04/18] RSE party menu detector no slot detection yet --- .../Menus/PokemonRSE_PartyMenuDetector.cpp | 113 ++++++++++++++++++ .../Menus/PokemonRSE_PartyMenuDetector.h | 85 +++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 3 files changed, 200 insertions(+) create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp new file mode 100644 index 0000000000..06ea1d83c5 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp @@ -0,0 +1,113 @@ +/* Party Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Exceptions.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTypes/ImageRGB32.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "PokemonRSE_PartyMenuDetector.h" + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + + +PartyMenuDetector::PartyMenuDetector(Color color) + : m_dialog_box(0.706471, 0.85002, 0.023937, 0.096045) //Choose a Pokemon - position works for all games + , m_background_box_e(0.015847, 0.591232, 0.038122, 0.133396) //solid color in the background for emerald, similar to FRLG + , m_top_bar_rs(0.028666, 0.035895, 0.942495, 0.004067) //both rs have a red bar on top (was this intended for s?) +{} +void PartyMenuDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_background_box_e)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_top_bar_rs)); +} +bool PartyMenuDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + //Dialog is white + ImageViewRGB32 dialog_image = extract_box_reference(game_screen, m_dialog_box); + + //Menu background is dark yellow/gold-ish + ImageViewRGB32 background_image = extract_box_reference(game_screen, m_background_box_e); + + //The top bar is red + ImageViewRGB32 top_bar_image = extract_box_reference(game_screen, m_top_bar_rs); + + if ((is_white(dialog_image) //Ruby and Sapphire, all languages + && is_solid(top_bar_image, { 0.71282, 0.16923, 0.1179487 }, 0.25, 20)) //139, 33, 23 + || + (is_white(dialog_image) //Emerald, all languages + && is_solid(background_image, { 0.41566, 0.46084, 0.12349 }, 0.25, 20)) //138, 153, 41 + ){ + return true; + } + return false; +} + +/* +ImageFloatBox PartySlotDetector::party_slot_boxes(PartySlot position){ + switch (position){ + case PartySlot::ONE: + return ImageFloatBox(0.035000, 0.376617, 0.003280, 0.070000); + case PartySlot::TWO: + return ImageFloatBox(0.985000, 0.080000, 0.003280, 0.099663); + case PartySlot::THREE: + return ImageFloatBox(0.985000, 0.230000, 0.003280, 0.099663); + case PartySlot::FOUR: + return ImageFloatBox(0.985000, 0.380000, 0.003280, 0.099663); + case PartySlot::FIVE: + return ImageFloatBox(0.985000, 0.530000, 0.003280, 0.099663); + case PartySlot::SIX: + return ImageFloatBox(0.985000, 0.675000, 0.003280, 0.099663); + default: + break; + } + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid FRLG Party Slot Position"); +} +PartySlotDetector::PartySlotDetector( + Color color, + const ImageFloatBox& box +) + : m_color(color) + , m_party_box(box) +{} +PartySlotDetector::PartySlotDetector( + Color color, + PartySlot position +) + : m_color(color) + , m_party_box(party_slot_boxes(position)) +{} +void PartySlotDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(m_color, GAME_BOX.inner_to_outer(m_party_box)); +} +bool PartySlotDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 target_box_party = extract_box_reference(game_screen, m_party_box); + + //orange FF701C border. light/dark blues in the selected box are close to each other. + if (is_solid(target_box_party, { 0.6455696, 0.2835, 0.070886 }, 0.25, 20) + ){ + return true; + } + return false; +} +*/ + + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h new file mode 100644 index 0000000000..a26244b714 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h @@ -0,0 +1,85 @@ +/* Party Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_PartyMenuDetector_H +#define PokemonAutomation_PokemonRSE_PartyMenuDetector_H + +#include +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/VisualDetector.h" +#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" +#include "PokemonRSE/PokemonRSE_Settings.h" +//#include "PokemonFRLG_PartySlot.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonRSE{ + +// Detect the Party Menu (Start Menu -> Pokemon). +class PartyMenuDetector : public StaticScreenDetector{ +public: + PartyMenuDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_dialog_box; + ImageFloatBox m_background_box_e; + ImageFloatBox m_top_bar_rs; //red, dark red, black bar on top +}; +class PartyMenuWatcher : public DetectorToFinder{ +public: + PartyMenuWatcher(Color color) + : DetectorToFinder("PartyMenuWatcher", std::chrono::milliseconds(250), color) + {} +}; + +/* +class PartySlotDetector : public StaticScreenDetector{ +public: + PartySlotDetector( + Color color, + const ImageFloatBox& box + ); + + PartySlotDetector( + Color color, + PartySlot position + ); + + static ImageFloatBox party_slot_boxes(PartySlot position); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + const Color m_color; + const ImageFloatBox m_party_box; +}; +class PartySlotWatcher : public DetectorToFinder{ +public: + PartySlotWatcher( + Color color, + PartySlot position, + std::chrono::milliseconds hold_duration = std::chrono::milliseconds(250) + ) + : DetectorToFinder("PartySlotWatcher", hold_duration, color, party_slot_boxes(position)) + { + } +}; + +*/ + +} +} +} + +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index db69005e63..d2c352ccf6 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2066,6 +2066,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h + Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp + Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp From 18bc5db1e38ff5aa8d87e57867a38796d54ab7f5 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 12:34:19 -0400 Subject: [PATCH 05/18] Pokenav detection --- .../Dialogs/PokemonRSE_DialogDetector.cpp | 26 +++++++++++++++++++ .../Dialogs/PokemonRSE_DialogDetector.h | 24 +++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp index 42d8035415..5348abe934 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp @@ -175,6 +175,32 @@ bool AdvanceBattleDialogDetector::detect(const ImageViewRGB32& screen){ +PokenavDialogDetector::PokenavDialogDetector(Color color) + : m_dialog_border_box(0.04, 0.717, 0.926425, 0.002033) //Very narrow, not many places to detect + , m_dialog_main_box(0.039784, 0.759311, 0.039008, 0.010672) +{} +void PokenavDialogDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_border_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_main_box)); +} +bool PokenavDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 dialog_border_image = extract_box_reference(game_screen, m_dialog_border_box); + ImageViewRGB32 dialog_main_image = extract_box_reference(game_screen, m_dialog_main_box); + + if (is_solid(dialog_border_image, { 0.20498, 0.45, 0.3448 }, 0.25, 20) //mint green 107, 235, 180 + && is_white(dialog_main_image) + ){ + return true; + } + return false; +} + + + + } } } diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h index 94cd6625e2..85ddb7eeec 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h @@ -104,6 +104,30 @@ class AdvanceBattleDialogWatcher : public DetectorToFinder{ +public: + PokenavDialogWatcher(Color color) + : DetectorToFinder("PokenavDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + // Future note: when given a choice popup, there is no advance arrow. From e068585502c777518142c54296d11b3bc54a381d Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 13:28:55 -0400 Subject: [PATCH 06/18] rse start menu detector --- .../Menus/PokemonRSE_StartMenuDetector.cpp | 41 ++++++++++++++++ .../Menus/PokemonRSE_StartMenuDetector.h | 48 +++++++++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 3 files changed, 91 insertions(+) create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp new file mode 100644 index 0000000000..b78281e285 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp @@ -0,0 +1,41 @@ +/* Start Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Color.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "PokemonRSE/PokemonRSE_Settings.h" +#include "PokemonRSE_StartMenuDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +StartMenuDetector::StartMenuDetector(Color color) + : m_menu_top_box(0.766756, 0.046978, 0.197701, 0.01334) + , m_menu_bottom_box(0.766756, 0.94, 0.197701, 0.01334) +{} +void StartMenuDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_bottom_box)); +} +bool StartMenuDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 menu_top_image = extract_box_reference(game_screen, m_menu_top_box); + ImageViewRGB32 menu_bottom_image = extract_box_reference(game_screen, m_menu_bottom_box); + + return (is_white(menu_top_image) + && is_white(menu_bottom_image) + ); +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h new file mode 100644 index 0000000000..4dee431bc4 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h @@ -0,0 +1,48 @@ +/* Start Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_StartMenuDetector_H +#define PokemonAutomation_PokemonRSE_StartMenuDetector_H + +#include +#include "Common/Cpp/Color.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/VisualDetector.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonRSE{ + +// Detect the full start menu by looking for empty white sections on the top and bottom +// Works for all RSE, all languages. Will not work for early game (before pokedex or party). +// No arrow detection, RS japan and all emerald languages have an arrow, +// but RS for all non-japan languages use a red box for selection. +class StartMenuDetector : public StaticScreenDetector{ +public: + StartMenuDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_menu_top_box; + ImageFloatBox m_menu_bottom_box; +}; +class StartMenuWatcher : public DetectorToFinder{ +public: + StartMenuWatcher(Color color = COLOR_RED) + : DetectorToFinder("StartMenuWatcher", std::chrono::milliseconds(250), color) + {} +}; + + +} +} +} + +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 0d46d3a803..bab726b57f 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2082,6 +2082,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h + Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp + Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp From 80de9cc3baa607eebef0abc368b9c8206a5d82fc Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 14:16:50 -0400 Subject: [PATCH 07/18] rse summary detector --- .../Menus/PokemonRSE_SummaryDetector.cpp | 61 +++++++++++++++++++ .../Menus/PokemonRSE_SummaryDetector.h | 53 ++++++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 3 files changed, 116 insertions(+) create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.cpp new file mode 100644 index 0000000000..13e3df04b9 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.cpp @@ -0,0 +1,61 @@ +/* Summary Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "PokemonRSE/PokemonRSE_Settings.h" +#include "PokemonRSE_SummaryDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +SummaryDetector::SummaryDetector(Color color) + : m_top_bar_box(0.639093, 0.014963, 0.014185, 0.058694) + , m_page_1_box(0.389085, 0.03764, 0.007979, 0.014674) //white + , m_page_2_box(0.456, 0.03764, 0.007979, 0.014674) + , m_page_3_box(0.523, 0.03764, 0.007979, 0.014674) + , m_page_4_box(0.589, 0.03764, 0.007979, 0.014674) +{} +void SummaryDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_page_1_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_page_2_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_page_3_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_page_4_box)); +} +bool SummaryDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 top_bar_image = extract_box_reference(game_screen, m_top_bar_box); + + ImageViewRGB32 page_1_image = extract_box_reference(game_screen, m_page_1_box); + ImageViewRGB32 page_2_image = extract_box_reference(game_screen, m_page_2_box); + ImageViewRGB32 page_3_image = extract_box_reference(game_screen, m_page_3_box); + ImageViewRGB32 page_4_image = extract_box_reference(game_screen, m_page_4_box); + + if ((is_solid(top_bar_image, { 0.3798, 0.37674, 0.24341 }, 0.25, 20) //RS light yellow 245, 243, 157 + ||is_solid(top_bar_image, { 0.34652, 0.3083, 0.345156889495 }, 0.25, 20)) //E light purple 254, 226, 253 + && is_white(page_1_image) + && !is_white(page_2_image) + && !is_white(page_3_image) + && !is_white(page_4_image) + ){ + return true; + } + return false; +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h new file mode 100644 index 0000000000..1f3ab77a46 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h @@ -0,0 +1,53 @@ +/* Summary Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_SummaryDetector_H +#define PokemonAutomation_PokemonRSE_SummaryDetector_H + +#include +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonTools/VisualDetector.h" +#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonRSE{ + +// Detect the default (first) Pokemon Summary page +// This has nature and OT, but no stats +// This will work with all languages, as the page indicators are in the same positions +// pg 2 stats, pg 3 moves, pg4 contest +class SummaryDetector : public StaticScreenDetector{ +public: + SummaryDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_top_bar_box; + + ImageFloatBox m_page_1_box; + ImageFloatBox m_page_2_box; + ImageFloatBox m_page_3_box; + ImageFloatBox m_page_4_box; +}; +class SummaryWatcher : public DetectorToFinder{ +public: + SummaryWatcher(Color color) + : DetectorToFinder("SummaryWatcher", std::chrono::milliseconds(250), color) + {} +}; + +} +} +} + +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index bab726b57f..847f252db6 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2084,6 +2084,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.cpp Source/PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h + Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.cpp + Source/PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp From 415432bdd0a741386b9b2c2dfd9b589eee6f821f Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 15:09:20 -0400 Subject: [PATCH 08/18] party slot detection --- .../Menus/PokemonRSE_PartyMenuDetector.cpp | 26 +++++++++++-------- .../Menus/PokemonRSE_PartyMenuDetector.h | 17 ++++++++++-- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp index 06ea1d83c5..05162ffbb8 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp @@ -55,25 +55,27 @@ bool PartyMenuDetector::detect(const ImageViewRGB32& screen){ return false; } -/* + + ImageFloatBox PartySlotDetector::party_slot_boxes(PartySlot position){ + //These positions should work for RSE all languages switch (position){ case PartySlot::ONE: - return ImageFloatBox(0.035000, 0.376617, 0.003280, 0.070000); + return ImageFloatBox(0.35185, 0.271083, 0.003546, 0.138731); case PartySlot::TWO: - return ImageFloatBox(0.985000, 0.080000, 0.003280, 0.099663); + return ImageFloatBox(0.987, 0.085, 0.003, 0.09966); case PartySlot::THREE: - return ImageFloatBox(0.985000, 0.230000, 0.003280, 0.099663); + return ImageFloatBox(0.987, 0.235, 0.003, 0.09966); case PartySlot::FOUR: - return ImageFloatBox(0.985000, 0.380000, 0.003280, 0.099663); + return ImageFloatBox(0.987, 0.385, 0.003, 0.09966); case PartySlot::FIVE: - return ImageFloatBox(0.985000, 0.530000, 0.003280, 0.099663); + return ImageFloatBox(0.987, 0.535, 0.003, 0.09966); case PartySlot::SIX: - return ImageFloatBox(0.985000, 0.675000, 0.003280, 0.099663); + return ImageFloatBox(0.987, 0.685, 0.003, 0.09966); default: break; } - throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid FRLG Party Slot Position"); + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid RSE Party Slot Position"); } PartySlotDetector::PartySlotDetector( Color color, @@ -98,14 +100,16 @@ bool PartySlotDetector::detect(const ImageViewRGB32& screen){ ImageViewRGB32 target_box_party = extract_box_reference(game_screen, m_party_box); - //orange FF701C border. light/dark blues in the selected box are close to each other. - if (is_solid(target_box_party, { 0.6455696, 0.2835, 0.070886 }, 0.25, 20) + //orange border FD7132 for emerald + //FD7132 AND EB712D for rs, the background is faintly striped, so it may require a wider range? + //FD7132 - 253, 113, 50 + if (is_solid(target_box_party, { 0.608173, 0.2716346, 0.1201923 }, 0.25, 20) ){ return true; } return false; } -*/ + } diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h index a26244b714..bf6c740915 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h @@ -22,6 +22,18 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ + +enum class PartySlot{ + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX + //CXL +}; + + // Detect the Party Menu (Start Menu -> Pokemon). class PartyMenuDetector : public StaticScreenDetector{ public: @@ -42,7 +54,9 @@ class PartyMenuWatcher : public DetectorToFinder{ {} }; -/* + +//Detect the highlighted slot. +//Looks for the red outline class PartySlotDetector : public StaticScreenDetector{ public: PartySlotDetector( @@ -76,7 +90,6 @@ class PartySlotWatcher : public DetectorToFinder{ } }; -*/ } } From 7e78e24043bd8360fd028cea3417ddf54cba83ed Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 16:54:43 -0400 Subject: [PATCH 09/18] split out battle dialogs --- .../Dialogs/PokemonRSE_BattleDialogs.cpp | 181 ++++++++++++++++++ .../Dialogs/PokemonRSE_BattleDialogs.h | 116 +++++++++++ .../Dialogs/PokemonRSE_DialogDetector.cpp | 151 --------------- .../Dialogs/PokemonRSE_DialogDetector.h | 83 -------- .../PokemonRSE/PokemonRSE_Navigation.cpp | 1 + .../PokemonRSE_AudioStarterReset.cpp | 1 + .../PokemonRSE_LegendaryRunAway-Emerald.cpp | 2 +- .../PokemonRSE_ShinyHunt-Deoxys.cpp | 2 +- .../ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp | 2 +- .../ShinyHunting/PokemonRSE_StarterReset.cpp | 1 + SerialPrograms/cmake/SourceFiles.cmake | 2 + 11 files changed, 305 insertions(+), 237 deletions(-) create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.cpp new file mode 100644 index 0000000000..5dea3f42e4 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.cpp @@ -0,0 +1,181 @@ +/* Battle Dialog Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/ImageFilter.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTypes/ImageRGB32.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "PokemonRSE/PokemonRSE_Settings.h" +#include "PokemonRSE_BattleDialogs.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +/* +BattleDialogDetector::BattleDialogDetector(Color color) + : m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023) + , m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602) + , m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034) + , m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590) +{} +void BattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box)); +} +bool BattleDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); + ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box); + ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box); + + if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) + || + (is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) + ){ + return true; + } + return false; +} +*/ + +BattleMenuDetector::BattleMenuDetector(Color color) + : m_menu_top_box(0.599462, 0.738530, 0.366233, 0.004011) //top of the white box + , m_menu_bottom_box(0.602128, 0.947120, 0.368010, 0.006686) + , m_dialog_top_box(0.068780, 0.739867, 0.488903, 0.008023) //teal boxes + , m_dialog_right_box(0.553238, 0.739867, 0.006222, 0.215276) + + , m_menu_top_eme_box(0.534571, 0.746553, 0.434679, 0.008023) + , m_menu_right_eme_box(0.962138, 0.747890, 0.007111, 0.205916) + , m_dialog_top_eme_box(0.046557, 0.741204, 0.442679, 0.008023) + , m_dialog_right_eme_box(0.487458, 0.742541, 0.004445, 0.216613) +{} +void BattleMenuDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_bottom_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); + + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_eme_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_right_eme_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_eme_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_eme_box)); +} +bool BattleMenuDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + //Menu is white + ImageViewRGB32 menu_top_image = extract_box_reference(game_screen, m_menu_top_box); + ImageViewRGB32 menu_bottom_image = extract_box_reference(game_screen, m_menu_bottom_box); + ImageViewRGB32 menu_top_jpn_image = extract_box_reference(game_screen, m_menu_top_eme_box); + ImageViewRGB32 menu_right_jpn_image = extract_box_reference(game_screen, m_menu_right_eme_box); + + //Background dialog is teal + ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); + ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_eme_box); + ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_eme_box); + + if ((is_white(menu_top_image) //Ruby/Sapphire, all languages. Emerald Japan. + && is_white(menu_bottom_image) + && is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) + || + (is_white(menu_top_jpn_image) //Emerald, all languages except Japan. + && is_white(menu_right_jpn_image) + && is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) + ){ + return true; + } + return false; +} + + +AdvanceBattleDialogDetector::AdvanceBattleDialogDetector(Color color) + : m_dialog_box(0.043890, 0.737193, 0.913804, 0.227310) + , m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023) + , m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602) + , m_dialog_jpn_box(0.059891, 0.742541, 0.872914, 0.216613) + , m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034) + , m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590) +{} +void AdvanceBattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_jpn_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box)); +} +bool AdvanceBattleDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + const bool replace_color_within_range = false; + + //Filter out background + ImageRGB32 filtered_region = filter_rgb32_range( + extract_box_reference(game_screen, m_dialog_box), + combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range + ); + ImageStats stats = image_stats(filtered_region); + + /* + filtered_region.save("./filtered_only.png"); + cout << stats.average.r << endl; + cout << stats.average.g << endl; + cout << stats.average.b << endl; + */ + + //japanese + ImageRGB32 filtered_region_jpn = filter_rgb32_range( + extract_box_reference(game_screen, m_dialog_jpn_box), + combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range + ); + ImageStats stats2 = image_stats(filtered_region_jpn); + + ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); + ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box); + ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box); + + if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && (stats.average.r > stats.average.b + 180) + && (stats.average.r > stats.average.g + 180)) + || + (is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) + && (stats2.average.r > stats2.average.b + 180) + && (stats2.average.r > stats2.average.g + 180)) + ){ + return true; + } + return false; +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h new file mode 100644 index 0000000000..caaca53f7f --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h @@ -0,0 +1,116 @@ +/* Battle Dialog Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_BattleDialogs_H +#define PokemonAutomation_PokemonRSE_BattleDialogs_H + +#include +#include +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonTools/VisualDetector.h" +#include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" + +namespace PokemonAutomation{ + class CancellableScope; + class VideoFeed; +namespace NintendoSwitch{ +namespace PokemonRSE{ + +// Battle dialog boxes are teal +// This is unused right now so isn't tested well +/* +class BattleDialogDetector : public StaticScreenDetector{ +public: + BattleDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_dialog_top_box; + ImageFloatBox m_dialog_right_box; + + ImageFloatBox m_dialog_top_jpn_box; + ImageFloatBox m_dialog_right_jpn_box; +}; +class BattleDialogWatcher : public DetectorToFinder{ +public: + BattleDialogWatcher(Color color) + : DetectorToFinder("BattleDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; +*/ + +// Battle menu is a white box on the right +// Teal dialog box remains on the left +// For R/S, the selection arrow is only in JPN ver, other languages use a box +// Positions slightly different on non-JPN Emerald but all langs use an arrow +class BattleMenuDetector : public StaticScreenDetector{ +public: + BattleMenuDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + //R/S both JPN and ENG, E for JPN + ImageFloatBox m_menu_top_box; + ImageFloatBox m_menu_bottom_box; + ImageFloatBox m_dialog_top_box; + ImageFloatBox m_dialog_right_box; + + //Emerald for non-JPN + ImageFloatBox m_menu_top_eme_box; + ImageFloatBox m_menu_right_eme_box; + ImageFloatBox m_dialog_top_eme_box; + ImageFloatBox m_dialog_right_eme_box; +}; +class BattleMenuWatcher : public DetectorToFinder{ +public: + BattleMenuWatcher(Color color) + : DetectorToFinder("BattleMenuWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + +// Detect the red advancement arrow by filtering for red. +// This is the same as BattleDialogDetector apart from the arrow +class AdvanceBattleDialogDetector : public StaticScreenDetector{ +public: + AdvanceBattleDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_dialog_box; + ImageFloatBox m_dialog_top_box; + ImageFloatBox m_dialog_right_box; + + ImageFloatBox m_dialog_jpn_box; + ImageFloatBox m_dialog_top_jpn_box; + ImageFloatBox m_dialog_right_jpn_box; +}; +class AdvanceBattleDialogWatcher : public DetectorToFinder{ +public: + AdvanceBattleDialogWatcher(Color color) + : DetectorToFinder("AdvanceBattleDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + +// Future note: when given a choice popup, there is no advance arrow. + + +} +} +} + +#endif diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp index 5348abe934..320b1aa730 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp @@ -22,157 +22,6 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ -/* -BattleDialogDetector::BattleDialogDetector(Color color) - : m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023) - , m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602) - , m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034) - , m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590) -{} -void BattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ - const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box)); -} -bool BattleDialogDetector::detect(const ImageViewRGB32& screen){ - ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); - - ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); - ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); - ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box); - ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box); - - if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) - || - (is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) - ){ - return true; - } - return false; -} -*/ - -BattleMenuDetector::BattleMenuDetector(Color color) - : m_menu_top_box(0.599462, 0.738530, 0.366233, 0.004011) //top of the white box - , m_menu_bottom_box(0.602128, 0.947120, 0.368010, 0.006686) - , m_dialog_top_box(0.068780, 0.739867, 0.488903, 0.008023) //teal boxes - , m_dialog_right_box(0.553238, 0.739867, 0.006222, 0.215276) - - , m_menu_top_eme_box(0.534571, 0.746553, 0.434679, 0.008023) - , m_menu_right_eme_box(0.962138, 0.747890, 0.007111, 0.205916) - , m_dialog_top_eme_box(0.046557, 0.741204, 0.442679, 0.008023) - , m_dialog_right_eme_box(0.487458, 0.742541, 0.004445, 0.216613) -{} -void BattleMenuDetector::make_overlays(VideoOverlaySet& items) const{ - const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_bottom_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); - - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_top_eme_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_menu_right_eme_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_eme_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_eme_box)); -} -bool BattleMenuDetector::detect(const ImageViewRGB32& screen){ - ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); - - //Menu is white - ImageViewRGB32 menu_top_image = extract_box_reference(game_screen, m_menu_top_box); - ImageViewRGB32 menu_bottom_image = extract_box_reference(game_screen, m_menu_bottom_box); - ImageViewRGB32 menu_top_jpn_image = extract_box_reference(game_screen, m_menu_top_eme_box); - ImageViewRGB32 menu_right_jpn_image = extract_box_reference(game_screen, m_menu_right_eme_box); - - //Background dialog is teal - ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); - ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); - ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_eme_box); - ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_eme_box); - - if ((is_white(menu_top_image) //Ruby/Sapphire, all languages. Emerald Japan. - && is_white(menu_bottom_image) - && is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) - || - (is_white(menu_top_jpn_image) //Emerald, all languages except Japan. - && is_white(menu_right_jpn_image) - && is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20)) - ){ - return true; - } - return false; -} - - -AdvanceBattleDialogDetector::AdvanceBattleDialogDetector(Color color) - : m_dialog_box(0.043890, 0.737193, 0.913804, 0.227310) - , m_dialog_top_box(0.049224, 0.738530, 0.901359, 0.008023) - , m_dialog_right_box(0.943471, 0.746553, 0.007111, 0.212602) - , m_dialog_jpn_box(0.059891, 0.742541, 0.872914, 0.216613) - , m_dialog_top_jpn_box(0.068780, 0.738530, 0.856913, 0.012034) - , m_dialog_right_jpn_box(0.918582, 0.747890, 0.007111, 0.208590) -{} -void AdvanceBattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ - const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_jpn_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_top_jpn_box)); - items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_dialog_right_jpn_box)); -} -bool AdvanceBattleDialogDetector::detect(const ImageViewRGB32& screen){ - ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); - - const bool replace_color_within_range = false; - - //Filter out background - ImageRGB32 filtered_region = filter_rgb32_range( - extract_box_reference(game_screen, m_dialog_box), - combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range - ); - ImageStats stats = image_stats(filtered_region); - - /* - filtered_region.save("./filtered_only.png"); - cout << stats.average.r << endl; - cout << stats.average.g << endl; - cout << stats.average.b << endl; - */ - - //japanese - ImageRGB32 filtered_region_jpn = filter_rgb32_range( - extract_box_reference(game_screen, m_dialog_jpn_box), - combine_rgb(164, 0, 0), combine_rgb(255, 114, 87), Color(0), replace_color_within_range - ); - ImageStats stats2 = image_stats(filtered_region_jpn); - - ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_dialog_top_box); - ImageViewRGB32 dialog_right_image = extract_box_reference(game_screen, m_dialog_right_box); - ImageViewRGB32 dialog_top_jpn_image = extract_box_reference(game_screen, m_dialog_top_jpn_box); - ImageViewRGB32 dialog_right_jpn_image = extract_box_reference(game_screen, m_dialog_right_jpn_box); - - if ((is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && (stats.average.r > stats.average.b + 180) - && (stats.average.r > stats.average.g + 180)) - || - (is_solid(dialog_top_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && is_solid(dialog_right_jpn_image, { 0.176, 0.357, 0.467 }, 0.25, 20) - && (stats2.average.r > stats2.average.b + 180) - && (stats2.average.r > stats2.average.g + 180)) - ){ - return true; - } - return false; -} - PokenavDialogDetector::PokenavDialogDetector(Color color) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h index 85ddb7eeec..521175868c 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h @@ -21,89 +21,6 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ -// Battle dialog boxes are teal -// This is unused right now so isn't tested well -/* -class BattleDialogDetector : public StaticScreenDetector{ -public: - BattleDialogDetector(Color color); - - virtual void make_overlays(VideoOverlaySet& items) const override; - virtual bool detect(const ImageViewRGB32& screen) override; - -private: - ImageFloatBox m_dialog_top_box; - ImageFloatBox m_dialog_right_box; - - ImageFloatBox m_dialog_top_jpn_box; - ImageFloatBox m_dialog_right_jpn_box; -}; -class BattleDialogWatcher : public DetectorToFinder{ -public: - BattleDialogWatcher(Color color) - : DetectorToFinder("BattleDialogWatcher", std::chrono::milliseconds(250), color) - {} -}; -*/ - -// Battle menu is a white box on the right -// Teal dialog box remains on the left -// For R/S, the selection arrow is only in JPN ver, other languages use a box -// Positions slightly different on non-JPN Emerald but all langs use an arrow -class BattleMenuDetector : public StaticScreenDetector{ -public: - BattleMenuDetector(Color color); - - virtual void make_overlays(VideoOverlaySet& items) const override; - virtual bool detect(const ImageViewRGB32& screen) override; - -private: - //R/S both JPN and ENG, E for JPN - ImageFloatBox m_menu_top_box; - ImageFloatBox m_menu_bottom_box; - ImageFloatBox m_dialog_top_box; - ImageFloatBox m_dialog_right_box; - - //Emerald for non-JPN - ImageFloatBox m_menu_top_eme_box; - ImageFloatBox m_menu_right_eme_box; - ImageFloatBox m_dialog_top_eme_box; - ImageFloatBox m_dialog_right_eme_box; -}; -class BattleMenuWatcher : public DetectorToFinder{ -public: - BattleMenuWatcher(Color color) - : DetectorToFinder("BattleMenuWatcher", std::chrono::milliseconds(250), color) - {} -}; - - - -// Detect the red advancement arrow by filtering for red. -// This is the same as BattleDialogDetector apart from the arrow -class AdvanceBattleDialogDetector : public StaticScreenDetector{ -public: - AdvanceBattleDialogDetector(Color color); - - virtual void make_overlays(VideoOverlaySet& items) const override; - virtual bool detect(const ImageViewRGB32& screen) override; - -private: - ImageFloatBox m_dialog_box; - ImageFloatBox m_dialog_top_box; - ImageFloatBox m_dialog_right_box; - - ImageFloatBox m_dialog_jpn_box; - ImageFloatBox m_dialog_top_jpn_box; - ImageFloatBox m_dialog_right_jpn_box; -}; -class AdvanceBattleDialogWatcher : public DetectorToFinder{ -public: - AdvanceBattleDialogWatcher(Color color) - : DetectorToFinder("AdvanceBattleDialogWatcher", std::chrono::milliseconds(250), color) - {} -}; - // Detect the Pokenav dialog box that appears when being called on the overworld // This does not work for calls from the Pokenav menu itself, as the position is different diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index c5b88e6246..5c4676e7a4 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -18,6 +18,7 @@ #include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" #include "NintendoSwitch/NintendoSwitch_ConsoleHandle.h" #include "Pokemon/Pokemon_Strings.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.h" #include "PokemonRSE/PokemonRSE_Settings.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp index 7fb74c31e7..d3d3179eeb 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp @@ -12,6 +12,7 @@ #include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "Pokemon/Pokemon_Strings.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp index ff6bc889a4..59ec11a812 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp @@ -15,7 +15,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" -#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_LegendaryRunAway-Emerald.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp index 703da0fb53..0a32893cce 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp @@ -15,7 +15,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" -#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_ShinyHunt-Deoxys.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp index cbc4826891..0bca214777 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp @@ -15,7 +15,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" -#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_ShinyHunt-Mew.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp index 6e50b206bd..718aac327a 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp @@ -14,6 +14,7 @@ #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 847f252db6..e645b6f04d 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2076,6 +2076,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonPokopia/Programs/PokemonPokopia_DailyFarmer.h Source/PokemonPokopia/Programs/PokemonPokopia_PCNavigation.cpp Source/PokemonPokopia/Programs/PokemonPokopia_PCNavigation.h + Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.cpp + Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h Source/PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.cpp From 5d3d5d70897c5adad456bd341f44c03fda06f25a Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 19:08:00 -0400 Subject: [PATCH 10/18] RSE dialog detectors --- .../Dialogs/PokemonRSE_BattleDialogs.h | 1 - .../Dialogs/PokemonRSE_DialogDetector.cpp | 152 ++++++++++++++++++ .../Dialogs/PokemonRSE_DialogDetector.h | 83 +++++++++- 3 files changed, 234 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h index caaca53f7f..3acf28a6fc 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h @@ -106,7 +106,6 @@ class AdvanceBattleDialogWatcher : public DetectorToFinder stats.average.b + 180) + && (stats.average.r > stats.average.g + 180)) + || + (is_white(dialog_side_image_jpn) + && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)) //E - green - 1 202 188 + && (stats2.average.r > stats2.average.b + 180) + && (stats2.average.r > stats2.average.g + 180)) + ){ + return true; + } + return false; +} + + + +SelectionDialogDetector::SelectionDialogDetector(Color color) + : m_top_box(0.13, 0.745, 0.7, 0.005) + , m_inner_box(0.044217, 0.756643, 0.007092, 0.17875) + + , m_side_box_jpn(0.067267, 0.763312, 0.018618, 0.170746) + , m_inner_box_jpn(0.114254, 0.771316, 0.007092, 0.161409) //white + + , m_yes_box(0.85, 0.46, 0.007, 0.069) + , m_no_box(0.85, 0.57, 0.007, 0.069) +{} +void SelectionDialogDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_top_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_inner_box)); + + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_side_box_jpn)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_inner_box_jpn)); + + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_yes_box)); + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_no_box)); +} +bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 game_screen = extract_box_reference(screen, GameSettings::instance().GAME_BOX); + + ImageViewRGB32 dialog_top_image = extract_box_reference(game_screen, m_top_box); + ImageViewRGB32 dialog_inner_image = extract_box_reference(game_screen, m_inner_box); + + ImageViewRGB32 dialog_side_image_jpn = extract_box_reference(game_screen, m_side_box_jpn); + ImageViewRGB32 dialog_inner_image_jpn = extract_box_reference(game_screen, m_inner_box_jpn); + + ImageViewRGB32 dialog_yes_image = extract_box_reference(game_screen, m_yes_box); + ImageViewRGB32 dialog_no_image = extract_box_reference(game_screen, m_no_box); + + if ((is_white(dialog_yes_image) && is_white(dialog_no_image)) + && ((is_white(dialog_inner_image) && is_white(dialog_top_image)) + || + (is_white(dialog_side_image_jpn) + && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)))) //E - green - 1 202 188 + ){ + return true; + } + return false; +} + } diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h index 521175868c..bbc0a80626 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h @@ -45,7 +45,88 @@ class PokenavDialogWatcher : public DetectorToFinder{ {} }; -// Future note: when given a choice popup, there is no advance arrow. + +// Dialog box without advance arrow. +// Positions are different between japan and ROW, but are the same across games. +class DialogDetector : public StaticScreenDetector{ +public: + DialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + //Position for non-JPN + ImageFloatBox m_top_box; + ImageFloatBox m_inner_box; + + //Position for JPN + ImageFloatBox m_side_box_jpn; + ImageFloatBox m_inner_box_jpn; +}; +class DialogWatcher : public DetectorToFinder{ +public: + DialogWatcher(Color color) + : DetectorToFinder("DialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + +// Dialog box, now with arrow! +class AdvanceDialogDetector : public StaticScreenDetector{ +public: + AdvanceDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + //Position for non-JPN + ImageFloatBox m_top_box; + ImageFloatBox m_inner_box; + ImageFloatBox m_dialog_box; + + //Position for JPN + ImageFloatBox m_side_box_jpn; + ImageFloatBox m_inner_box_jpn; + ImageFloatBox m_dialog_jpn_box; +}; +class AdvanceDialogWatcher : public DetectorToFinder{ +public: + AdvanceDialogWatcher(Color color) + : DetectorToFinder("AdvanceDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + +// Detection for the Yes/No prompt +class SelectionDialogDetector : public StaticScreenDetector{ +public: + SelectionDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + //Position for non-JPN + ImageFloatBox m_top_box; + ImageFloatBox m_inner_box; + + //Position for JPN + ImageFloatBox m_side_box_jpn; + ImageFloatBox m_inner_box_jpn; + + ImageFloatBox m_yes_box; + ImageFloatBox m_no_box; +}; +class SelectionDialogWatcher : public DetectorToFinder{ +public: + SelectionDialogWatcher(Color color) + : DetectorToFinder("SelectionDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; } From 8908504ca8ebb65e483ec94f18000f2e82843bb7 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sat, 1 Aug 2026 21:32:34 -0400 Subject: [PATCH 11/18] rse sr update --- .../Inference/Dialogs/PokemonRSE_DialogDetector.h | 3 ++- .../Source/PokemonRSE/PokemonRSE_Navigation.cpp | 9 +++++++-- .../ShinyHunting/PokemonRSE_AudioStarterReset.cpp | 6 ++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h index bbc0a80626..f65830dcce 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h @@ -101,7 +101,8 @@ class AdvanceDialogWatcher : public DetectorToFinder{ -// Detection for the Yes/No prompt +// Detection for the Yes/No prompt. (Poke Center, Move Deleter) +// Does not work for starter selection, dialog box and prompt are different. class SelectionDialogDetector : public StaticScreenDetector{ public: SelectionDialogDetector(Color color); diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index 5c4676e7a4..24fe0409a2 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -20,6 +20,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/Inference/Menus/PokemonRSE_LoadMenuDetector.h" #include "PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.h" #include "PokemonRSE/PokemonRSE_Settings.h" #include "PokemonRSE_Navigation.h" @@ -88,11 +89,13 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ // A + B + Select + Start pbf_press_button(context, BUTTON_B | BUTTON_A | BUTTON_MINUS | BUTTON_PLUS, 360ms, 1440ms); + //Mash select to get to "Push Start Button" with Kyogre/Groudon/Rayquaza background pbf_mash_button(context, BUTTON_MINUS, GameSettings::instance().SELECT_BUTTON_MASH0); context.wait_for_all_requests(); //Wait for save file select screen WhiteScreenOverWatcher whitescreen(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + LoadMenuWatcher load_menu(COLOR_RED); int ls = run_until( console, context, @@ -101,11 +104,13 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ pbf_wait(context, 5000ms); context.wait_for_all_requests(); }, - { whitescreen } + { whitescreen, load_menu } ); context.wait_for_all_requests(); if (ls == 0){ - console.log("Entered load menu."); + console.log("Entered load menu. (WhiteScreenOver)"); + }else if (ls == 1){ + console.log("Entered load menu. (LoadMenu)"); }else{ console.log("soft_reset(): Unable to enter load menu.", COLOR_RED); return false; diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp index d3d3179eeb..00a5aa15a5 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp @@ -147,7 +147,8 @@ void AudioStarterReset::program(SingleSwitchProgramEnvironment& env, ProControll switch (TARGET){ case Target::treecko: env.log("Treecko selected. Moving left."); - pbf_press_dpad(context, DPAD_LEFT, 320ms, 800ms); + pbf_press_dpad(context, DPAD_LEFT, 120ms, 100ms); //Double up in case + pbf_press_dpad(context, DPAD_LEFT, 120ms, 100ms); //of dropped press break; case Target::torchic: //Default cursor position, do nothing. @@ -155,7 +156,8 @@ void AudioStarterReset::program(SingleSwitchProgramEnvironment& env, ProControll break; case Target::mudkip: env.log("Mudkip selected. Moving right."); - pbf_press_dpad(context, DPAD_RIGHT, 320ms, 800ms); + pbf_press_dpad(context, DPAD_RIGHT, 120ms, 100ms); + pbf_press_dpad(context, DPAD_RIGHT, 120ms, 100ms); break; default: env.log("Invalid target selected."); From aea1df8bcafc08c2d56b620bf69acd8898eb5c29 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sun, 2 Aug 2026 12:11:53 -0400 Subject: [PATCH 12/18] blackwhite watchers --- .../Dialogs/PokemonRSE_DialogDetector.h | 31 +++++++++++++++++++ .../PokemonRSE/PokemonRSE_Navigation.cpp | 6 ++-- .../PokemonRSE_AudioStarterReset.cpp | 4 +-- .../ShinyHunting/PokemonRSE_StarterReset.cpp | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h index f65830dcce..64d5bcbcbb 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h @@ -13,7 +13,9 @@ #include "Common/Cpp/Color.h" #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonTools/VisualDetector.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" +#include "PokemonRSE/PokemonRSE_Settings.h" namespace PokemonAutomation{ class CancellableScope; @@ -22,6 +24,35 @@ namespace NintendoSwitch{ namespace PokemonRSE{ +class WhiteScreenOverWatcher : public PokemonAutomation::WhiteScreenOverWatcher{ +public: + WhiteScreenOverWatcher(Color color = COLOR_RED) + : PokemonAutomation::WhiteScreenOverWatcher( + color, + GameSettings::instance().GAME_BOX.inner_to_outer({0.231692, 0.0616538, 0.551385, 0.9045}) + ) + {} +}; +class BlackScreenWatcher : public PokemonAutomation::BlackScreenWatcher{ +public: + BlackScreenWatcher(Color color = COLOR_RED) + : PokemonAutomation::BlackScreenWatcher( + color, + GameSettings::instance().GAME_BOX.inner_to_outer({0.231692, 0.0616538, 0.551385, 0.9045}) + ) + {} +}; +class BlackScreenOverWatcher : public PokemonAutomation::BlackScreenOverWatcher{ +public: + BlackScreenOverWatcher(Color color = COLOR_RED) + : PokemonAutomation::BlackScreenOverWatcher( + color, + GameSettings::instance().GAME_BOX.inner_to_outer({0.231692, 0.0616538, 0.551385, 0.9045}) + ) + {} +}; + + // Detect the Pokenav dialog box that appears when being called on the overworld // This does not work for calls from the Pokenav menu itself, as the position is different // Emerald only, Ruby and Sapphire don't have to worry about randomly being interrupted. diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index 24fe0409a2..055af6ddfa 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -94,7 +94,7 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ context.wait_for_all_requests(); //Wait for save file select screen - WhiteScreenOverWatcher whitescreen(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + WhiteScreenOverWatcher whitescreen(COLOR_RED); LoadMenuWatcher load_menu(COLOR_RED); int ls = run_until( @@ -123,7 +123,7 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ pbf_press_button(context, BUTTON_A, 160ms, 320ms); //Wait for game to load in - BlackScreenOverWatcher detector2(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher detector2(COLOR_RED); int ret = wait_until( console, context, GameSettings::instance().ENTER_GAME_WAIT0, @@ -185,7 +185,7 @@ void flee_battle(VideoStream& stream, ProControllerContext& context){ ); } - BlackScreenOverWatcher battle_over(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher battle_over(COLOR_RED); int ret3 = run_until( stream, context, [&](ProControllerContext& context){ diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp index 00a5aa15a5..721464b3c7 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp @@ -118,7 +118,7 @@ void AudioStarterReset::program(SingleSwitchProgramEnvironment& env, ProControll //Pressing A opens the bag so the screen goes black env.log("Opening bag and selecting starter."); - BlackScreenOverWatcher bag_opened(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher bag_opened(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ @@ -172,7 +172,7 @@ void AudioStarterReset::program(SingleSwitchProgramEnvironment& env, ProControll } //Mash A to select starter. Stop once black screen is detected to start listening for shiny pooch. - BlackScreenWatcher starter_battle_start(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenWatcher starter_battle_start(COLOR_RED); int ret3 = run_until( env.console, context, [](ProControllerContext& context){ diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp index 718aac327a..a8c8d6214b 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp @@ -154,7 +154,7 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon pbf_press_dpad(context, DPAD_DOWN, 320ms, 640ms); pbf_press_button(context, BUTTON_A, 320ms, 640ms); - BlackScreenOverWatcher detector(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher detector(COLOR_RED); int ret2 = wait_until( env.console, context, std::chrono::milliseconds(3000), From d8b4c7b5dc660d573c44b58bdf4eaa767ab2ceb8 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sun, 2 Aug 2026 14:18:54 -0400 Subject: [PATCH 13/18] rs legendary reset --- .../Source/PokemonRSE/PokemonRSE_Panels.cpp | 6 +- .../PokemonRSE_LegendaryReset.cpp | 175 ++++++++++++++++++ .../ShinyHunting/PokemonRSE_LegendaryReset.h | 63 +++++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 4 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp index aaa8875a6b..c31f7aa17c 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp @@ -11,6 +11,7 @@ #include "PokemonRSE_Settings.h" #include "Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h" +#include "Programs/ShinyHunting/PokemonRSE_LegendaryReset.h" #include "Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h" @@ -39,10 +40,11 @@ std::vector PanelListFactory::make_panels() const{ //ret.emplace_back("---- General ----"); - ret.emplace_back("---- Shiny Hunting (Ruby/Sapphire) ----"); + ret.emplace_back("---- Shiny Hunting (Ruby/Sapphire) ----"); //Remove RS only if E is fixed ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); - ret.emplace_back("---- Shiny Hunting (Emerald) ----"); + ret.emplace_back("---- Shiny Hunting (Emerald only) ----"); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp new file mode 100644 index 0000000000..58c0efc52c --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp @@ -0,0 +1,175 @@ +/* Legendary Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/PokemonRSE_Navigation.h" +#include "PokemonRSE_LegendaryReset.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +LegendaryReset_Descriptor::LegendaryReset_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonRSE:LegendaryReset", + Pokemon::STRING_POKEMON + " RSE", "Legendary Reset", + "Programs/PokemonRSE/LegendaryReset.html", + "Shiny hunt legendary Pokemon using soft resets.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +struct LegendaryReset_Descriptor::Stats : public StatsTracker{ + Stats() + : resets(m_stats["Resets"]) + , shinies(m_stats["Shinies"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Resets"); + m_display_order.emplace_back("Shinies"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + std::atomic& resets; + std::atomic& shinies; + std::atomic& errors; +}; +std::unique_ptr LegendaryReset_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +LegendaryReset::LegendaryReset() + : TARGET( + "Target:
", + { + {Target::press_a, "press_a", "Press A: Rayquaza, Regis, Latios/Latias (Southern Island), Voltorb, Electrode, Kecleon"}, + {Target::walk_left, "walk_left", "Walk Left: Kyogre, Groudon"}, //if emerald is fixed, +deoxys hooh lugia + {Target::walk_right, "walk_right", "Walk Right: Kyogre, Groudon"},//tested groudon, regi, ray, kecleon + }, + LockMode::LOCK_WHILE_RUNNING, + Target::press_a + ) + , TAKE_VIDEO("Take Video:
Record a video when the shiny is found.", LockMode::UNLOCK_WHILE_RUNNING, true) + , GO_HOME_WHEN_DONE(true) + , NOTIFICATION_SHINY( + "Shiny found", + true, true, ImageAttachmentMode::JPG, + {"Notifs", "Showcase"} + ) + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_SHINY, + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + }) +{ + PA_ADD_STATIC(SHINY_REQUIRES_AUDIO); + PA_ADD_OPTION(TARGET); + PA_ADD_OPTION(TAKE_VIDEO); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryReset_Descriptor::Stats& stats = env.current_stats(); + + home_black_border_check(env.console, context); + + while (true){ + //Mash B until black screen detected but not over (entered battle) + BlackScreenWatcher battle_entered(COLOR_RED); + int ret = run_until( + env.console, context, + [&](ProControllerContext& context){ + switch (TARGET){ + case Target::press_a: + env.log("Target: Press A."); + //Mash A as Kecleon has a Yes/No dialog + pbf_mash_button(context, BUTTON_A, 3000ms); + break; + case Target::walk_left: + env.log("Target: Walk Left."); + //Step forward to start the encounter. + pbf_press_dpad(context, DPAD_LEFT, 320ms, 100ms); + pbf_press_dpad(context, DPAD_LEFT, 320ms, 100ms); + break; + case Target::walk_right: + env.log("Target: Walk Right."); + pbf_press_dpad(context, DPAD_RIGHT, 320ms, 100ms); + pbf_press_dpad(context, DPAD_RIGHT, 320ms, 100ms); + break; + } + context.wait_for_all_requests(); + + //Long mash to clear dialog+wait for those long animations + env.log("Mashing B through animation."); + pbf_mash_button(context, BUTTON_B, 120s); + context.wait_for_all_requests(); + }, + {battle_entered} + ); + context.wait_for_all_requests(); + if (ret != 0){ + stats.errors++; + env.update_stats(); + env.log("Failed to enter battle.", COLOR_RED); + stats.errors += soft_reset(env.console, context); + continue; + }else{ + env.log("Battle started."); + } + + bool legendary_shiny = handle_encounter(env.console, context, false); + if (legendary_shiny){ + stats.shinies++; + env.update_stats(); + send_program_notification( + env, + NOTIFICATION_SHINY, + COLOR_YELLOW, + "Shiny found!", + {}, "", + env.console.video().snapshot(), + true + ); + if (TAKE_VIDEO){ + pbf_press_button(context, BUTTON_CAPTURE, 2000ms, 0ms); + } + break; + } + + //No shiny found + env.log("Soft resetting."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Soft resetting." + ); + + stats.errors += soft_reset(env.console, context); + stats.resets++; + env.update_stats(); + context.wait_for_all_requests(); + } + + if (GO_HOME_WHEN_DONE){ + pbf_press_button(context, BUTTON_HOME, 200ms, 1000ms); + } + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + +} +} +} + diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h new file mode 100644 index 0000000000..92ab85b3a0 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h @@ -0,0 +1,63 @@ +/* Legendary Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_LegendaryReset_H +#define PokemonAutomation_PokemonRSE_LegendaryReset_H + +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" +#include "Common/Cpp/Options/EnumDropdownOption.h" +#include "Common/Cpp/Options/SimpleIntegerOption.h" +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "PokemonLA/Options/PokemonLA_ShinyDetectedAction.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +class LegendaryReset_Descriptor : public SingleSwitchProgramDescriptor{ +public: + LegendaryReset_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class LegendaryReset : public SingleSwitchProgramInstance{ +public: + LegendaryReset(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override{} + +private: + PokemonLA::ShinyRequiresAudioText SHINY_REQUIRES_AUDIO; + + enum class Target{ + press_a, + walk_left, + walk_right, + }; + EnumDropdownOption TARGET; + + BooleanCheckBoxOption TAKE_VIDEO; + + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif + + diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index e645b6f04d..e10718f623 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2100,6 +2100,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/PokemonRSE_Settings.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp From 555e664de1bab386d85b4dfbbc09e2d51b127f2d Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sun, 2 Aug 2026 21:12:29 -0400 Subject: [PATCH 14/18] black white screen updates --- SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp | 8 ++++++++ SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.h | 1 + .../ShinyHunting/PokemonRSE_AudioStarterReset.cpp | 1 - .../Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp | 4 ++-- .../Programs/ShinyHunting/PokemonRSE_StarterReset.cpp | 1 - 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp index b842247228..ded02f10c3 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.cpp @@ -37,6 +37,7 @@ GameSettings::GameSettings() { {Device::switch_1_2, "switch_1_2", "Nintendo Switch 1 and 2"}, {Device::rg35xx, "rg35xx", "RG35XX"}, + {Device::tablet, "tablet", "tablet"}, //{Device::custom, "custom", "Custom"}, }, LockMode::LOCK_WHILE_RUNNING, @@ -107,6 +108,13 @@ void GameSettings::on_config_value_changed(void* object){ GAME_BOX.HEIGHT.set(0.8861111111111111); GAME_BOX.set_visibility(ConfigOptionState::DISABLED); break; + case Device::tablet: + GAME_BOX.X.set(0.227907); + GAME_BOX.Y.set(0.003546); + GAME_BOX.WIDTH.set(0.544186); + GAME_BOX.HEIGHT.set(0.643026); + GAME_BOX.set_visibility(ConfigOptionState::DISABLED); + break; case Device::custom: GAME_BOX.set_visibility(ConfigOptionState::ENABLED); break; diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.h b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.h index 77b0884c97..54704ba1a1 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.h +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Settings.h @@ -28,6 +28,7 @@ class GameSettings : public BatchOption, private ConfigOption::Listener{ enum class Device{ switch_1_2, rg35xx, + tablet, custom, }; diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp index 721464b3c7..c4ddd93f8e 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp @@ -9,7 +9,6 @@ #include "CommonFramework/Notifications/ProgramNotifications.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" -#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp index 0a32893cce..44030a3933 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp @@ -10,12 +10,12 @@ #include "CommonFramework/ProgramStats/StatsTracking.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" -#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_ShinyHunt-Deoxys.h" @@ -249,7 +249,7 @@ void ShinyHuntDeoxys::program(SingleSwitchProgramEnvironment& env, ProController } //Start battle - BlackScreenWatcher legendary_battle_start(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenWatcher legendary_battle_start(COLOR_RED); int ret3 = run_until( env.console, context, [&](ProControllerContext& context){ diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp index a8c8d6214b..45b4835314 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp @@ -10,7 +10,6 @@ #include "CommonFramework/ProgramStats/StatsTracking.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" -#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" From bb257c20a183c0d6cb764875a0dcbfc9f081b97f Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Sun, 2 Aug 2026 21:13:13 -0400 Subject: [PATCH 15/18] fix mew movement --- .../PokemonRSE_LegendaryRunAway-Emerald.cpp | 24 +++--- .../ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp | 74 ++++++++++--------- .../ShinyHunting/PokemonRSE_ShinyHunt-Mew.h | 12 +-- 3 files changed, 56 insertions(+), 54 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp index 59ec11a812..5f67d71840 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp @@ -7,7 +7,6 @@ //#include "Common/Cpp/PrettyPrint.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonTools/Async/InferenceRoutines.h" -#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "CommonFramework/Notifications/ProgramNotifications.h" #include "CommonFramework/ProgramStats/StatsTracking.h" @@ -16,6 +15,7 @@ #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_LegendaryRunAway-Emerald.h" @@ -155,8 +155,8 @@ void LegendaryRunAwayEmerald::reset_regi(SingleSwitchProgramEnvironment& env, Pr LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); //turn around, walk down 4/until black screen over - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); + BlackScreenOverWatcher enter_area(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ @@ -243,7 +243,7 @@ void LegendaryRunAwayEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, context.wait_for_all_requests(); //Turn down. Exit. Black screen over. - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ @@ -270,7 +270,7 @@ void LegendaryRunAwayEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, context.wait_for_all_requests(); //Reverse above steps. - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED); int ret2 = run_until( env.console, context, [](ProControllerContext& context){ @@ -351,7 +351,7 @@ void LegendaryRunAwayEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, context.wait_for_all_requests(); //Turn down. Exit. Black screen over. - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ @@ -377,7 +377,7 @@ void LegendaryRunAwayEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, pbf_wait(context, 1000ms); context.wait_for_all_requests(); - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED); int ret2 = run_until( env.console, context, [](ProControllerContext& context){ @@ -432,7 +432,7 @@ void LegendaryRunAwayEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, void LegendaryRunAwayEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); //Turn around, 10 steps down ssf_press_button(context, BUTTON_B, 0ms, HOOH_UP_DOWN); pbf_press_dpad(context, DPAD_DOWN, HOOH_UP_DOWN, 0ms); @@ -464,7 +464,7 @@ void LegendaryRunAwayEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, Pr pbf_wait(context, 500ms); context.wait_for_all_requests(); - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED); //turn left, take one step. now turn back right and take a step. wait for black screen over. int ret2 = run_until( env.console, context, @@ -507,7 +507,7 @@ void LegendaryRunAwayEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, Pr void LegendaryRunAwayEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ LegendaryRunAwayEmerald_Descriptor::Stats& stats = env.current_stats(); - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); //Turn around, 5 steps down ssf_press_button(context, BUTTON_B, 0ms, LUGIA_UP_DOWN0); pbf_press_dpad(context, DPAD_DOWN, LUGIA_UP_DOWN0, 0ms); @@ -539,7 +539,7 @@ void LegendaryRunAwayEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, P pbf_wait(context, 500ms); context.wait_for_all_requests(); - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED); //turn up, take one step. then turn back down and take a step. wait for black screen over. int ret2 = run_until( env.console, context, @@ -594,7 +594,7 @@ void LegendaryRunAwayEmerald::program(SingleSwitchProgramEnvironment& env, ProCo */ while (true){ - BlackScreenWatcher legendary_battle_start(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenWatcher legendary_battle_start(COLOR_RED); int ret3 = run_until( env.console, context, [&](ProControllerContext& context){ diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp index 0bca214777..56bc590cc9 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.cpp @@ -10,12 +10,12 @@ #include "CommonFramework/ProgramStats/StatsTracking.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" -#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_BattleDialogs.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/PokemonRSE_Navigation.h" #include "PokemonRSE_ShinyHunt-Mew.h" @@ -70,32 +70,32 @@ ShinyHuntMew::ShinyHuntMew() , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) - , MEW_WAIT_TIME( + , MEW_WAIT_TIME0( "Mew wait time:
Wait this long after entering for Mew to hide in the grass.", LockMode::LOCK_WHILE_RUNNING, "2000 ms" ) - , DOOR_TO_GRASS_TIME( + , DOOR_TO_GRASS_TIME0( "Door to grass time:
Time it takes to run from the door to the edge of the tall grass. Three steps up.", LockMode::LOCK_WHILE_RUNNING, "400 ms" ) - , RIGHT_GRASS_1_TIME( + , RIGHT_GRASS_1_TIME0( "First Right time:
Time it takes to turn right and take three steps. This follows the edge of the grass.", LockMode::LOCK_WHILE_RUNNING, - "450 ms" + "400 ms" ) - , UP_GRASS_1_TIME( - "Move Up time::
Time it takes turn up and take one step.", + , UP_GRASS_1_TIME0( + "Move Up time:
Time it takes turn up and take one step.", LockMode::LOCK_WHILE_RUNNING, - "200 ms" + "150 ms" ) - , RIGHT_GRASS_2_TIME( + , RIGHT_GRASS_2_TIME0( "Second Right time:
Time it takes to turn right and take two steps.", LockMode::LOCK_WHILE_RUNNING, "260 ms" ) - , FACE_UP_TIME( + , FACE_UP_TIME0( "Face Up time:
Time it takes to tap the up button and face up, without taking a step.", LockMode::LOCK_WHILE_RUNNING, "150 ms" @@ -105,19 +105,19 @@ ShinyHuntMew::ShinyHuntMew() PA_ADD_OPTION(TAKE_VIDEO); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); - PA_ADD_STATIC(m_advanced_options); - PA_ADD_OPTION(MEW_WAIT_TIME); - PA_ADD_OPTION(DOOR_TO_GRASS_TIME); - PA_ADD_OPTION(RIGHT_GRASS_1_TIME); - PA_ADD_OPTION(UP_GRASS_1_TIME); - PA_ADD_OPTION(RIGHT_GRASS_2_TIME); - PA_ADD_OPTION(FACE_UP_TIME); + //PA_ADD_STATIC(m_advanced_options); + //PA_ADD_OPTION(MEW_WAIT_TIME); + //PA_ADD_OPTION(DOOR_TO_GRASS_TIME); + //PA_ADD_OPTION(RIGHT_GRASS_1_TIME); + //PA_ADD_OPTION(UP_GRASS_1_TIME); + //PA_ADD_OPTION(RIGHT_GRASS_2_TIME); + //PA_ADD_OPTION(FACE_UP_TIME); } void ShinyHuntMew::enter_mew(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ ShinyHuntMew_Descriptor::Stats& stats = env.current_stats(); - BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ @@ -141,32 +141,33 @@ void ShinyHuntMew::enter_mew(SingleSwitchProgramEnvironment& env, ProControllerC } //Wait for Mew ! animation to finish - pbf_wait(context, MEW_WAIT_TIME); + pbf_wait(context, MEW_WAIT_TIME0); context.wait_for_all_requests(); //DO NOT pause while running! //Run up toward the extra tall grass - 3 steps - ssf_press_button(context, BUTTON_B, 0ms, DOOR_TO_GRASS_TIME); - pbf_press_dpad(context, DPAD_UP, DOOR_TO_GRASS_TIME, 0ms); + ssf_press_button(context, BUTTON_B, 0ms, 1220ms); //+10ms + //ssf_press_button(context, BUTTON_B, 0ms, DOOR_TO_GRASS_TIME); + pbf_press_dpad(context, DPAD_UP, DOOR_TO_GRASS_TIME0, 0ms); //Turn right, take 3 steps - ssf_press_button(context, BUTTON_B, 0ms, RIGHT_GRASS_1_TIME); - pbf_press_dpad(context, DPAD_RIGHT, RIGHT_GRASS_1_TIME, 0ms); + //ssf_press_button(context, BUTTON_B, 0ms, RIGHT_GRASS_1_TIME); + pbf_press_dpad(context, DPAD_RIGHT, RIGHT_GRASS_1_TIME0, 0ms); //Turn up, take 1 step - ssf_press_button(context, BUTTON_B, 0ms, UP_GRASS_1_TIME); - pbf_press_dpad(context, DPAD_UP, UP_GRASS_1_TIME, 0ms); + //ssf_press_button(context, BUTTON_B, 0ms, UP_GRASS_1_TIME); + pbf_press_dpad(context, DPAD_UP, UP_GRASS_1_TIME0, 0ms); //Turn right, take 2 steps - ssf_press_button(context, BUTTON_B, 0ms, RIGHT_GRASS_2_TIME); - pbf_press_dpad(context, DPAD_RIGHT, RIGHT_GRASS_2_TIME, 0ms); + //ssf_press_button(context, BUTTON_B, 0ms, RIGHT_GRASS_2_TIME); + pbf_press_dpad(context, DPAD_RIGHT, RIGHT_GRASS_2_TIME0, 0ms); //Turn up. - pbf_press_dpad(context, DPAD_UP, FACE_UP_TIME, 0ms); + pbf_press_dpad(context, DPAD_UP, FACE_UP_TIME0, 0ms); context.wait_for_all_requests(); //Start battle. - BlackScreenWatcher legendary_battle_start(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenWatcher legendary_battle_start(COLOR_RED); int ret3 = run_until( env.console, context, [&](ProControllerContext& context){ @@ -197,16 +198,17 @@ void ShinyHuntMew::enter_mew(SingleSwitchProgramEnvironment& env, ProControllerC void ShinyHuntMew::exit_mew(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ ShinyHuntMew_Descriptor::Stats& stats = env.current_stats(); - ssf_press_button(context, BUTTON_B, 0ms, 400ms); - pbf_press_dpad(context, DPAD_DOWN, 400ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, 1920ms); + //ssf_press_button(context, BUTTON_B, 0ms, 400ms); + pbf_press_dpad(context, DPAD_DOWN, 400ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 720ms); - pbf_press_dpad(context, DPAD_LEFT, 720ms, 160ms); + //ssf_press_button(context, BUTTON_B, 0ms, 720ms); + pbf_press_dpad(context, DPAD_LEFT, 650ms, 0ms); - ssf_press_button(context, BUTTON_B, 0ms, 800ms); - pbf_press_dpad(context, DPAD_DOWN, 800ms, 160ms); + //ssf_press_button(context, BUTTON_B, 0ms, 800ms); + pbf_press_dpad(context, DPAD_DOWN, 800ms, 0ms); - BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher exit_area(COLOR_RED); int ret = run_until( env.console, context, [](ProControllerContext& context){ diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h index a653a08ead..d5ac76894c 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Mew.h @@ -46,12 +46,12 @@ class ShinyHuntMew : public SingleSwitchProgramInstance{ SectionDividerOption m_advanced_options; - MillisecondsOption MEW_WAIT_TIME; - MillisecondsOption DOOR_TO_GRASS_TIME; - MillisecondsOption RIGHT_GRASS_1_TIME; - MillisecondsOption UP_GRASS_1_TIME; - MillisecondsOption RIGHT_GRASS_2_TIME; - MillisecondsOption FACE_UP_TIME; + MillisecondsOption MEW_WAIT_TIME0; + MillisecondsOption DOOR_TO_GRASS_TIME0; + MillisecondsOption RIGHT_GRASS_1_TIME0; + MillisecondsOption UP_GRASS_1_TIME0; + MillisecondsOption RIGHT_GRASS_2_TIME0; + MillisecondsOption FACE_UP_TIME0; void enter_mew(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void exit_mew(SingleSwitchProgramEnvironment& env, ProControllerContext& context); From 4c54c303757cba037a93242de9edba250c8d2586 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Wed, 12 Aug 2026 14:08:52 -0400 Subject: [PATCH 16/18] rs gift reset --- .../Dialogs/PokemonRSE_DialogDetector.cpp | 12 +- .../Menus/PokemonRSE_PartyMenuDetector.cpp | 4 +- .../Source/PokemonRSE/PokemonRSE_Panels.cpp | 2 + .../ShinyHunting/PokemonRSE_GiftReset.cpp | 442 ++++++++++++++++++ .../ShinyHunting/PokemonRSE_GiftReset.h | 65 +++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 6 files changed, 519 insertions(+), 8 deletions(-) create mode 100644 SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.cpp create mode 100644 SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.h diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp index f180717394..62ccb2f329 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp @@ -76,8 +76,8 @@ bool DialogDetector::detect(const ImageViewRGB32& screen){ && is_white(dialog_top_image)) || (is_white(dialog_side_image_jpn) - && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20))) //E - green - 1 202 188 + && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20))) //E - green - 1 202 188 ){ return true; } @@ -143,8 +143,8 @@ bool AdvanceDialogDetector::detect(const ImageViewRGB32& screen){ && (stats.average.r > stats.average.g + 180)) || (is_white(dialog_side_image_jpn) - && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)) //E - green - 1 202 188 + && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)) //E - green - 1 202 188 && (stats2.average.r > stats2.average.b + 180) && (stats2.average.r > stats2.average.g + 180)) ){ @@ -192,8 +192,8 @@ bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){ && ((is_white(dialog_inner_image) && is_white(dialog_top_image)) || (is_white(dialog_side_image_jpn) - && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)))) //E - green - 1 202 188 + && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)))) //E - green - 1 202 188 ){ return true; } diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp index 05162ffbb8..01ea1a7ad7 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.cpp @@ -101,9 +101,9 @@ bool PartySlotDetector::detect(const ImageViewRGB32& screen){ ImageViewRGB32 target_box_party = extract_box_reference(game_screen, m_party_box); //orange border FD7132 for emerald - //FD7132 AND EB712D for rs, the background is faintly striped, so it may require a wider range? + //FD7132 AND EB712D for rs, the background is faintly striped, so it requires a wider range //FD7132 - 253, 113, 50 - if (is_solid(target_box_party, { 0.608173, 0.2716346, 0.1201923 }, 0.25, 20) + if (is_solid(target_box_party, { 0.608173, 0.2716346, 0.1201923 }, 0.25, 50) ){ return true; } diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp index c31f7aa17c..5f896f90dd 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp @@ -11,6 +11,7 @@ #include "PokemonRSE_Settings.h" #include "Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h" +#include "Programs/ShinyHunting/PokemonRSE_GiftReset.h" #include "Programs/ShinyHunting/PokemonRSE_LegendaryReset.h" #include "Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h" @@ -43,6 +44,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- Shiny Hunting (Ruby/Sapphire) ----"); //Remove RS only if E is fixed ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Shiny Hunting (Emerald only) ----"); ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.cpp new file mode 100644 index 0000000000..b9513ff34d --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.cpp @@ -0,0 +1,442 @@ +/* Gift Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/Inference/Menus/PokemonRSE_PartyMenuDetector.h" +#include "PokemonRSE/Inference/Menus/PokemonRSE_StartMenuDetector.h" +#include "PokemonRSE/Inference/Menus/PokemonRSE_SummaryDetector.h" +#include "PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h" +#include "PokemonRSE/PokemonRSE_Navigation.h" +#include "PokemonRSE_GiftReset.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +GiftReset_Descriptor::GiftReset_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonRSE:GiftReset", + Pokemon::STRING_POKEMON + " RSE", "Gift Reset", + "Programs/PokemonRSE/GiftReset.html", + "Soft reset for a shiny gift Pokemon.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +struct GiftReset_Descriptor::Stats : public StatsTracker{ + Stats() + : resets(m_stats["Resets"]) + , shinies(m_stats["Shinies"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Resets"); + m_display_order.emplace_back("Shinies"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + std::atomic& resets; + std::atomic& shinies; + std::atomic& errors; +}; +std::unique_ptr GiftReset_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +GiftReset::GiftReset() + : TARGET( + "Target:
", + { + {Target::fossils, "fossils", "Lileep / Anorith"}, + {Target::castform, "castform", "Castform"}, + {Target::beldum, "beldum", "Beldum"}, + }, + LockMode::LOCK_WHILE_RUNNING, + Target::beldum + ) + , TAKE_VIDEO("Take Video:
Record a video when the shiny is found.", LockMode::UNLOCK_WHILE_RUNNING, true) + , GO_HOME_WHEN_DONE(true) + , NOTIFICATION_SHINY( + "Shiny found", + true, true, ImageAttachmentMode::JPG, + {"Notifs", "Showcase"} + ) + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_SHINY, + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_RECOVERABLE, + }) +{ + PA_ADD_OPTION(TARGET); + PA_ADD_OPTION(TAKE_VIDEO); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void GiftReset::obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + GiftReset_Descriptor::Stats& stats = env.current_stats(); + + env.log("Obtaining Pokemon."); + + AdvanceDialogWatcher adv_white_start(COLOR_RED); + int rets = run_until( + env.console, context, + [](ProControllerContext& context){ + for (int i = 0; i < 10; i++){ + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + pbf_wait(context, 2000ms); + context.wait_for_all_requests(); + } + }, + { adv_white_start } + ); + context.wait_for_all_requests(); + if (rets < 0){ + stats.errors++; + env.update_stats(); + env.log("obtain_pokemon(): Unable to start starter dialog after 10 attempts.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "obtain_pokemon(): Unable to start starter dialog after 10 attempts.", + env.console + ); + } + env.log("Initial A press completed."); + + bool seen_selection_arrow = false; + + while (true){ + context.wait_for_all_requests(); + + AdvanceDialogWatcher adv_white(COLOR_RED); + SelectionDialogWatcher selection_dialog(COLOR_RED); + + int ret = wait_until( + env.console, context, + 10s, + { + adv_white, + selection_dialog, + } + ); + context.wait_for(500ms); + + switch (ret){ + case 0: + env.log("Detected Advance Dialog. Pressing B."); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + continue; + case 1: + env.log("Detected Selection Dialog. Pressing A."); + if (!seen_selection_arrow){ + + if (TARGET == Target::beldum){ //two yes/no boxes, castform and fossils??? + env.log("First selection box detected. YES to obtain."); + seen_selection_arrow = true; + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + }else{ //fossils + env.log("Selection box detected. NO to nickname."); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + context.wait_for_all_requests(); + return; + } + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + + }else{ + env.log("Second selection box detected. NO to nickname."); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + context.wait_for_all_requests(); + return; + } + continue; + default: + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "obtain_starter(): No recognized state after 10 seconds.", + env.console + ); + } + //Small wait for dialog boxes + //pbf_wait(context, 500ms); + //context.wait_for_all_requests(); + } + context.wait_for_all_requests(); +} + +void GiftReset::obtain_fossils(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + env.log("Obtaining Fossil."); + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + //At least 6 lines of dialog/No to nickname for fossils + //This takes care of the entire conversion+nickname+exit dialog + int limit = 10; + for (int i = 0; i < limit; i++){ + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 100ms); + context.wait_for_all_requests(); + } + context.wait_for_all_requests(); +} + +bool GiftReset::try_open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + StartMenuWatcher start_menu(COLOR_RED); + + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + for (int i = 0; i < 10; i++){ + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 100ms); + context.wait_for_all_requests(); + pbf_press_button(context, BUTTON_PLUS, 320ms, 640ms); + pbf_wait(context, 100ms); + context.wait_for_all_requests(); + } + }, + { start_menu } + ); + context.wait_for_all_requests(); + if (ret < 0){ + env.update_stats(); + env.log("open_summary(): Unable to open Start menu after 10 attempts.", COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "open_summary(): Unable to open Start menu after 10 attempts." + ); + return false; + } + + env.log("Navigating to party menu."); + pbf_wait(context, 200ms); + context.wait_for_all_requests(); + + pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms); + + /* + if (!move_cursor_to_position(env.console, context, SelectionArrowPositionStartMenu::POKEMON)){ + std::string str = "open_summary(): Unable to move menu cursor to: " + Pokemon::STRING_POKEMON; + env.log(str, COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + str + ); + return false; + } + */ + + //Open party menu + PartyMenuWatcher blk1(COLOR_RED); + + int pm = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + pbf_wait(context, 5000ms); + context.wait_for_all_requests(); + }, + { blk1 } + ); + context.wait_for_all_requests(); + if (pm == 0){ + env.log("Entered party menu."); + }else{ + env.log("open_summary(): Unable to enter party menu.", COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "open_summary(): Unable to enter party menu." + ); + return false; + } + + //Press up twice to get to the last slot + PartySlotWatcher last_slot(COLOR_RED, PartySlot::SIX); + int ps = run_until( + env.console, context, + [](ProControllerContext& context){ + for (int i = 0; i < 15; i++){ //Enough to cycle through 6pty+cxl twice + pbf_wait(context, 320ms); + context.wait_for_all_requests(); + pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); + } + }, + { last_slot } + ); + context.wait_for_all_requests(); + if (ps == 0){ + env.log("Moved selection to slot six."); + }else{ + env.log("open_summary(): Unable to move selection to slot six.", COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "open_summary(): Unable to move selection to slot six." + ); + return false; + } + + //Two presses to open summary + BlackScreenOverWatcher blk2(COLOR_RED); + int sm = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + pbf_wait(context, 5000ms); + context.wait_for_all_requests(); + }, + { blk2 } + ); + if (sm == 0){ + env.log("Entered summary."); + }else{ + env.log("open_summary(): Unable to enter summary.", COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "open_summary(): Unable to enter summary." + ); + return false; + } + + //Double check that we are on summary + SummaryWatcher sum1(COLOR_RED); + int sm1 = wait_until( + env.console, context, + std::chrono::seconds(5), + {{ sum1 }} + ); + if (sm1 == 0){ + env.log("Summary page dots detected."); + }else{ + env.log("open_summary(): Unable to detect summary screen.", COLOR_RED); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "open_summary(): Unable to detect summary screen." + ); + return false; + } + + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); + return true; +} +uint64_t GiftReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + uint64_t errors = 0; + for (; errors < 5; errors++){ + if (try_open_summary(env, context)){ + return errors; + }else{ + env.log("Mashing B to return to overworld and retry..."); + pbf_mash_button(context, BUTTON_B, 10000ms); + } + } + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "open_summary(): Failed to open party summary after 5 attempts.", + env.console + ); +} + + +void GiftReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + //StartProgramChecks::check_performance_class_wired_or_wireless(context); + + GiftReset_Descriptor::Stats& stats = env.current_stats(); + + home_black_border_check(env.console, context); + + /* + * Settings: Text Speed fast. + * Setup: 5 pokemon in your party. Stand in front of the pokemon. Save the game. + * Move menu cursor back to the top (POKEDEX) + */ + + switch (TARGET){ + case Target::fossils: + env.log("Target: Fossils"); + break; + case Target::castform: + env.log("Target: Castform"); + break; + case Target::beldum: + env.log("Target: Beldum"); + break; + default: + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "GiftReset: Invalid target selection.", + env.console + ); + break; + } + + bool shiny_found = false; + + while (!shiny_found){ + if (TARGET != Target::fossils && TARGET != Target::castform){ + obtain_pokemon(env, context); + }else{ + obtain_fossils(env, context); + } + stats.errors += open_summary(env, context); + + VideoSnapshot screen = env.console.video().snapshot(); + + ShinyNumberDetector shiny_checker(COLOR_YELLOW); + shiny_found = shiny_checker.read(env.console.logger(), screen); + + if (shiny_found){ + env.log("Shiny found!"); + stats.shinies++; + send_program_notification( + env, + NOTIFICATION_SHINY, + COLOR_YELLOW, + "Shiny found!", + {}, "", + screen, + true + ); + if (TAKE_VIDEO){ + pbf_press_button(context, BUTTON_CAPTURE, 2000ms, 0ms); + } + break; + }else{ + env.log("Pokemon is not shiny."); + env.log("Soft resetting."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Soft resetting." + ); + stats.errors += soft_reset(env.console, context); + stats.resets++; + env.update_stats(); + context.wait_for_all_requests(); + } + } + + if (GO_HOME_WHEN_DONE){ + pbf_press_button(context, BUTTON_HOME, 200ms, 1000ms); + } + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + +} +} +} + diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.h new file mode 100644 index 0000000000..4063dbc443 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.h @@ -0,0 +1,65 @@ +/* Gift Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonRSE_GiftReset_H +#define PokemonAutomation_PokemonRSE_GiftReset_H + +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +class GiftReset_Descriptor : public SingleSwitchProgramDescriptor{ +public: + GiftReset_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class GiftReset : public SingleSwitchProgramInstance{ +public: + GiftReset(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override{} + +private: + void obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void obtain_fossils(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + bool try_open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + uint64_t open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + + enum class Target{ + fossils, + castform, + beldum, + //gen_2_starters, //only if emerald rng is fixed + }; + EnumDropdownOption TARGET; + + BooleanCheckBoxOption TAKE_VIDEO; + + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif + + + diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index e10718f623..66f483847c 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -2100,6 +2100,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonRSE/PokemonRSE_Settings.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.cpp + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_GiftReset.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryReset.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryRunAway-Emerald.cpp From 3c47999ffc569abb5f6c898f4f0751d20949fa89 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Wed, 12 Aug 2026 14:27:14 -0400 Subject: [PATCH 17/18] fix sr for emerald, fix/update shiny number detector --- .../PokemonRSE_ShinyNumberDetector.cpp | 32 ++++++++++++------- .../PokemonRSE_ShinyNumberDetector.h | 2 ++ .../PokemonRSE/PokemonRSE_Navigation.cpp | 7 ++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp index 46d394d68b..8fa6a872e2 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp @@ -13,36 +13,44 @@ #include "CommonTools/Images/ImageFilter.h" #include "PokemonRSE_ShinyNumberDetector.h" -//#include -//using std::cout; -//using std::endl; +#include +using std::cout; +using std::endl; namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonRSE{ ShinyNumberDetector::ShinyNumberDetector(Color color) - : m_box_number(0.136, 0.156, 0.123, 0.072) + : m_box_number(0.025599, 0.103004, 0.125004, 0.093377) {} void ShinyNumberDetector::make_overlays(VideoOverlaySet& items) const{ - items.add(COLOR_RED, m_box_number); + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(COLOR_RED, GAME_BOX.inner_to_outer(m_box_number)); } bool ShinyNumberDetector::read(Logger& logger, const ImageViewRGB32& frame){ + ImageViewRGB32 game_screen = extract_box_reference(frame, GameSettings::instance().GAME_BOX); + const bool replace_color_within_range = true; - //Filter out background + //Filter out background (R/S, purple) ImageRGB32 filtered_region = filter_rgb32_range( - extract_box_reference(frame, m_box_number), - combine_rgb(138, 97, 221), combine_rgb(200, 181, 239), Color(0), replace_color_within_range + extract_box_reference(game_screen, m_box_number), + combine_rgb(120, 100, 185), combine_rgb(254, 251, 255), Color(0), replace_color_within_range + ); + //Filter out background (E, green) + ImageRGB32 filtered_region2 = filter_rgb32_range( + filtered_region, + combine_rgb(86, 162, 99), combine_rgb(236, 255, 240), Color(0), replace_color_within_range ); - ImageStats stats = image_stats(filtered_region); + ImageStats stats = image_stats(filtered_region2); - /* - filtered_region.save("./filtered_only.png"); + + filtered_region2.save("./filtered_only.png"); cout << stats.average.r << endl; cout << stats.average.g << endl; cout << stats.average.b << endl; - */ + /* Shiny: diff --git a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h index 22c17ab55e..2ff053fc36 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h @@ -9,6 +9,7 @@ #include "Common/Cpp/Logging/AbstractLogger.h" #include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "PokemonRSE/PokemonRSE_Settings.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -17,6 +18,7 @@ namespace PokemonRSE{ // In the summary screen, the dex number will be yellow if a shiny, white if not. // Additionally, the background behind the sprite will be white if shiny, grey if not. // Number is easier to check as the background is scan lines. +// Warning: In Emerald, the moving sprite may cover the number! class ShinyNumberDetector{ public: ShinyNumberDetector(Color color); diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index 055af6ddfa..9c1b3ee507 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -94,7 +94,7 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ context.wait_for_all_requests(); //Wait for save file select screen - WhiteScreenOverWatcher whitescreen(COLOR_RED); + //WhiteScreenOverWatcher whitescreen(COLOR_RED); LoadMenuWatcher load_menu(COLOR_RED); int ls = run_until( @@ -104,12 +104,11 @@ bool try_soft_reset(ConsoleHandle& console, ProControllerContext& context){ pbf_wait(context, 5000ms); context.wait_for_all_requests(); }, - { whitescreen, load_menu } + { load_menu } ); context.wait_for_all_requests(); if (ls == 0){ - console.log("Entered load menu. (WhiteScreenOver)"); - }else if (ls == 1){ + //console.log("Entered load menu. (WhiteScreenOver)"); console.log("Entered load menu. (LoadMenu)"); }else{ console.log("soft_reset(): Unable to enter load menu.", COLOR_RED); From 8a23fc4a15d5cf1d8e85d3cc0e0859974c201fbe Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Wed, 12 Aug 2026 15:13:09 -0400 Subject: [PATCH 18/18] fix dialog detectors for japan --- .../Dialogs/PokemonRSE_DialogDetector.cpp | 18 +++++++++--------- .../PokemonRSE_ShinyNumberDetector.cpp | 10 +++++----- .../Inference/PokemonRSE_ShinyNumberDetector.h | 1 + 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp index 62ccb2f329..66f53f1770 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp @@ -75,9 +75,9 @@ bool DialogDetector::detect(const ImageViewRGB32& screen){ if ((is_white(dialog_inner_image) && is_white(dialog_top_image)) || - (is_white(dialog_side_image_jpn) - && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20))) //E - green - 1 202 188 + (is_white(dialog_inner_image_jpn) + && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20))) //E - green - 1 202 188 ){ return true; } @@ -142,9 +142,9 @@ bool AdvanceDialogDetector::detect(const ImageViewRGB32& screen){ && (stats.average.r > stats.average.b + 180) && (stats.average.r > stats.average.g + 180)) || - (is_white(dialog_side_image_jpn) - && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)) //E - green - 1 202 188 + (is_white(dialog_inner_image_jpn) + && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)) //E - green - 1 202 188 && (stats2.average.r > stats2.average.b + 180) && (stats2.average.r > stats2.average.g + 180)) ){ @@ -191,9 +191,9 @@ bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){ if ((is_white(dialog_yes_image) && is_white(dialog_no_image)) && ((is_white(dialog_inner_image) && is_white(dialog_top_image)) || - (is_white(dialog_side_image_jpn) - && (is_solid(dialog_inner_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 - || is_solid(dialog_inner_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)))) //E - green - 1 202 188 + (is_white(dialog_inner_image_jpn) + && (is_solid(dialog_side_image_jpn, { 0.2300556, 0.34508, 0.42486 }, 0.25, 20) //RS - blue - 124 186 229 + || is_solid(dialog_side_image_jpn, { 0.0025575, 0.516624, 0.4808184 }, 0.25, 20)))) //E - green - 1 202 188 ){ return true; } diff --git a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp index 8fa6a872e2..192aef5e24 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp +++ b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp @@ -13,9 +13,9 @@ #include "CommonTools/Images/ImageFilter.h" #include "PokemonRSE_ShinyNumberDetector.h" -#include -using std::cout; -using std::endl; +//#include +//using std::cout; +//using std::endl; namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -45,12 +45,12 @@ bool ShinyNumberDetector::read(Logger& logger, const ImageViewRGB32& frame){ ); ImageStats stats = image_stats(filtered_region2); - + /* filtered_region2.save("./filtered_only.png"); cout << stats.average.r << endl; cout << stats.average.g << endl; cout << stats.average.b << endl; - + */ /* Shiny: diff --git a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h index 2ff053fc36..c625984622 100644 --- a/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h +++ b/SerialPrograms/Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h @@ -18,6 +18,7 @@ namespace PokemonRSE{ // In the summary screen, the dex number will be yellow if a shiny, white if not. // Additionally, the background behind the sprite will be white if shiny, grey if not. // Number is easier to check as the background is scan lines. +// Number might be cut off a bit for japan, but as long as most of it is in its fine // Warning: In Emerald, the moving sprite may cover the number! class ShinyNumberDetector{ public: