From 04a6202ffc33e2ca6e8b4a5c5a27193611278ca5 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 12:13:54 -0500 Subject: [PATCH 1/9] fix error message --- .../Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp index b10b44464..e4f2bd0aa 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp @@ -255,7 +255,7 @@ void GiftReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerC if (ret1 == 0){ env.log("Entered party menu."); }else{ - env.log("Timed out waiting to enter game.", COLOR_RED); + env.log("Unable to enter party menu.", COLOR_RED); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "open_summary(): Unable to enter Party menu.", @@ -283,7 +283,7 @@ void GiftReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerC if (ret2 == 0){ env.log("Entered summary."); }else{ - env.log("Timed out waiting to enter game.", COLOR_RED); + env.log("Unable to enter summary.", COLOR_RED); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "open_summary(): Unable to enter summary.", From 2f9b6577319fcfccf847ce2f0f9159d7d4420edb Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 15:36:30 -0500 Subject: [PATCH 2/9] battle dialog detectors --- .../Dialogs/PokemonFRLG_DialogDetector.cpp | 95 +++++++++++++++++++ .../Dialogs/PokemonFRLG_DialogDetector.h | 66 +++++++++++++ 2 files changed, 161 insertions(+) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp index 78961198d..7f4fc9c03 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp @@ -120,6 +120,101 @@ bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){ return false; } +BattleDialogDetector::BattleDialogDetector(Color color) + : m_dialog_top_box(0.152, 0.721, 0.694, 0.008) + , m_dialog_right_box(0.842, 0.730, 0.004, 0.171) +{} +void BattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_dialog_top_box); + items.add(COLOR_RED, m_dialog_right_box); +} +bool BattleDialogDetector::detect(const ImageViewRGB32& screen){ + //Background dialog is teal + ImageViewRGB32 dialog_top_image = extract_box_reference(screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(screen, m_dialog_right_box); + + if (is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }) + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }) + ){ + return true; + } + return false; +} + + +BattleMenuDetector::BattleMenuDetector(Color color) + : m_menu_top_box(0.522, 0.716, 0.331, 0.011) + , m_menu_right_box(0.845, 0.727, 0.008, 0.175) + , m_dialog_top_box(0.153, 0.720, 0.343, 0.014) //top of the white dialog box + , m_dialog_right_box(0.486, 0.722, 0.009, 0.177) //right side, closest to the menu +{} +void BattleMenuDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_menu_top_box); + items.add(COLOR_RED, m_menu_right_box); + items.add(COLOR_RED, m_dialog_top_box); + items.add(COLOR_RED, m_dialog_right_box); +} +bool BattleMenuDetector::detect(const ImageViewRGB32& screen){ + //Menu is white + ImageViewRGB32 menu_top_image = extract_box_reference(screen, m_menu_top_box); + ImageViewRGB32 menu_right_image = extract_box_reference(screen, m_menu_right_box); + + //Background dialog is teal + ImageViewRGB32 dialog_top_image = extract_box_reference(screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(screen, m_dialog_right_box); + + if (is_solid(menu_top_image, { 0.335, 0.332, 0.335 }) //253, 251, 254 white + && is_solid(menu_right_image, { 0.335, 0.332, 0.335 }) + && is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }) //40, 81, 106 teal + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }) + ){ + return true; + } + return false; +} + + +AdvanceBattleDialogDetector::AdvanceBattleDialogDetector(Color color) + : m_dialog_box(0.152, 0.721, 0.694, 0.177) + , m_dialog_top_box(0.152, 0.721, 0.694, 0.008) + , m_dialog_right_box(0.842, 0.730, 0.004, 0.171) +{} +void AdvanceBattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_dialog_box); + items.add(COLOR_RED, m_dialog_top_box); + items.add(COLOR_RED, m_dialog_right_box); +} +bool AdvanceBattleDialogDetector::detect(const ImageViewRGB32& screen){ + const bool replace_color_within_range = false; + + //Filter out background + ImageRGB32 filtered_region = filter_rgb32_range( + extract_box_reference(screen, m_dialog_box), + combine_rgb(185, 0, 1), combine_rgb(255, 32, 33), 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; + */ + + ImageViewRGB32 dialog_top_image = extract_box_reference(screen, m_dialog_top_box); + ImageViewRGB32 dialog_right_image = extract_box_reference(screen, m_dialog_right_box); + + if (is_solid(dialog_top_image, { 0.176, 0.357, 0.467 }) + && is_solid(dialog_right_image, { 0.176, 0.357, 0.467 }) + && (stats.average.r > stats.average.b + 200) + && (stats.average.r > stats.average.g + 200) + ) + { + return true; + } + return false; +} + } } diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h index a77b39381..0471f9c9a 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h @@ -95,6 +95,72 @@ class SelectionDialogWatcher : public DetectorToFinder{ {} }; +// Battle dialog boxes are teal, similar to r/s/e +// We look in multiple places for this solid color +// doing so checks that the battle menu is not up +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; +}; +class BattleDialogWatcher : public DetectorToFinder{ +public: + BattleDialogWatcher(Color color) + : DetectorToFinder("BattleDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + + +// Battle menu is a white box on the right with FIGHT/POKEMON/BAG/RUN +// The dialog box with "what will POKEMON do?" is dark teal/navy +class BattleMenuDetector : public StaticScreenDetector{ +public: + BattleMenuDetector(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_right_box; + ImageFloatBox m_dialog_top_box; + ImageFloatBox m_dialog_right_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. +// No red colored text in battle? +// reuse the battledialog checks for the top and right +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; +}; +class AdvanceBattleDialogWatcher : public DetectorToFinder{ +public: + AdvanceBattleDialogWatcher(Color color) + : DetectorToFinder("AdvanceBattleDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; From 052dde8d14cad3329cb205992b1ab8bd3855a171 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 15:53:47 -0500 Subject: [PATCH 3/9] copy handle_encounter over from RSE navi --- .../Dialogs/PokemonFRLG_DialogDetector.cpp | 1 - .../Dialogs/PokemonFRLG_DialogDetector.h | 2 -- .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 32 ++++++++----------- .../PokemonFRLG/PokemonFRLG_Navigation.h | 6 ++-- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp index 7f4fc9c03..ac5bbb3e1 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp @@ -129,7 +129,6 @@ void BattleDialogDetector::make_overlays(VideoOverlaySet& items) const{ items.add(COLOR_RED, m_dialog_right_box); } bool BattleDialogDetector::detect(const ImageViewRGB32& screen){ - //Background dialog is teal ImageViewRGB32 dialog_top_image = extract_box_reference(screen, m_dialog_top_box); ImageViewRGB32 dialog_right_image = extract_box_reference(screen, m_dialog_right_box); diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h index 0471f9c9a..ffaf3f020 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h @@ -96,8 +96,6 @@ class SelectionDialogWatcher : public DetectorToFinder{ }; // Battle dialog boxes are teal, similar to r/s/e -// We look in multiple places for this solid color -// doing so checks that the battle menu is not up class BattleDialogDetector : public StaticScreenDetector{ public: BattleDialogDetector(Color color); diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index c446dd02d..7d10a7a32 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -136,34 +136,29 @@ void open_slot_six(ConsoleHandle& console, ProControllerContext& context){ context.wait_for_all_requests(); } -/* -bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool send_out_lead) { +bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, bool send_out_lead) { float shiny_coefficient = 1.0; - ShinySoundDetector shiny_detector(stream.logger(), [&](float error_coefficient) -> bool{ + ShinySoundDetector shiny_detector(console.logger(), [&](float error_coefficient) -> bool{ shiny_coefficient = error_coefficient; return true; }); AdvanceBattleDialogWatcher legendary_appeared(COLOR_YELLOW); - stream.log("Starting battle."); - pbf_mash_button(context, BUTTON_A, 4320ms); - context.wait_for_all_requests(); - int res = run_until( - stream, context, + console, context, [&](ProControllerContext& context) { int ret = wait_until( - stream, context, + console, context, std::chrono::seconds(30), {{legendary_appeared}} ); if (ret == 0) { - stream.log("Advance arrow detected."); + console.log("Battle Advance arrow detected."); } else { OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "handle_encounter(): Did not detect battle start.", - stream + "handle_encounter(): Did not detect battle advance arrow.", + console ); } pbf_wait(context, 1000ms); @@ -173,30 +168,30 @@ bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool s ); shiny_detector.throw_if_no_sound(); if (res == 0){ - stream.log("Shiny detected!"); + console.log("Shiny detected!"); return true; } - stream.log("Shiny not found."); + console.log("No shiny detected."); if (send_out_lead) { //Send out lead, no shiny detection needed. BattleMenuWatcher battle_menu(COLOR_RED); - stream.log("Sending out lead Pokemon."); + console.log("Sending out lead Pokemon."); pbf_press_button(context, BUTTON_A, 320ms, 320ms); int ret = wait_until( - stream, context, + console, context, std::chrono::seconds(15), { {battle_menu} } ); if (ret == 0) { - stream.log("Battle menu detecteed!"); + console.log("Battle menu detecteed!"); } else { OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "handle_encounter(): Did not detect battle menu.", - stream + console ); } pbf_wait(context, 1000ms); @@ -205,7 +200,6 @@ bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool s return false; } -*/ } } diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h index 46389e4b8..a216444e3 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h @@ -27,9 +27,9 @@ void soft_reset(const ProgramInfo& info, VideoStream& stream, ProControllerConte void open_slot_six(ConsoleHandle& console, ProControllerContext& context); // After press A/walking up to enter a battle, run this handle the battle start and to check if opponent is shiny. -// Set send_out_lead to true and then use flee_battle() after if game is Emerald. -// For R/S, send_out_lead as false and then soft_reset() to save time. -//bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool send_out_lead); +// Set send_out_lead to true and then use flee_battle() after if for run away resets +// For soft resets, send_out_lead as false and then soft_reset() to save time. +bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, bool send_out_lead); } From 07b5a190494400a09f420c2f15fa3e89501bc02d Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 16:52:41 -0500 Subject: [PATCH 4/9] legendary reset --- .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 2 +- .../Source/PokemonFRLG/PokemonFRLG_Panels.cpp | 2 + .../ShinyHunting/PokemonFRLG_GiftReset.cpp | 2 +- .../PokemonFRLG_LegendaryReset.cpp | 144 ++++++++++++++++++ .../ShinyHunting/PokemonFRLG_LegendaryReset.h | 50 ++++++ .../PokemonFRLG_PrizeCornerReset.cpp | 2 +- 6 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index 7d10a7a32..82b5ed776 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -174,7 +174,7 @@ bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, boo console.log("No shiny detected."); if (send_out_lead) { - //Send out lead, no shiny detection needed. + //Send out lead, no shiny detection needed. (Or wanted.) BattleMenuWatcher battle_menu(COLOR_RED); console.log("Sending out lead Pokemon."); pbf_press_button(context, BUTTON_A, 320ms, 320ms); diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index 0a13424a5..75853c676 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -11,6 +11,7 @@ #include "PokemonFRLG_Settings.h" #include "Programs/ShinyHunting/PokemonFRLG_GiftReset.h" +#include "Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h" #include "Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" @@ -34,6 +35,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- Shiny Hunting ----"); 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/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp index e4f2bd0aa..d977d99ab 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp @@ -69,7 +69,7 @@ GiftReset::GiftReset() ) , GO_HOME_WHEN_DONE(true) , NOTIFICATION_SHINY( - "Shiny", + "Shiny found", true, true, ImageAttachmentMode::JPG, {"Notifs", "Showcase"} ) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp new file mode 100644 index 000000000..232b6f7dc --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp @@ -0,0 +1,144 @@ +/* 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 "CommonTools/VisualDetectors/BlackScreenDetector.h" +#include "CommonTools/StartupChecks/StartProgramChecks.h" +#include "Pokemon/Pokemon_Strings.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h" +#include "PokemonFRLG/PokemonFRLG_Navigation.h" +#include "PokemonFRLG_LegendaryReset.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +LegendaryReset_Descriptor::LegendaryReset_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:LegendaryReset", + Pokemon::STRING_POKEMON + " FRLG", "Legendary Reset", + "Programs/PokemonFRLG/LegendaryReset.html", + "Shiny hunt legendary Pokemon using soft resets.", + ProgramControllerClass::StandardController_RequiresPrecision, + 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() + : 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_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryReset_Descriptor::Stats& stats = env.current_stats(); + + /* + * Settings: Text Speed fast. Default borders. Audio required. + * Setup: Stand in front of target. Save the game. + * Lead can be shiny, reset is before lead is even sent out. + * This is the same as RSE resets. + */ + + while (true) { + //Talk to target + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + + //Mash B until black screen detected but not over (entered battle) + BlackScreenWatcher battle_entered(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_mash_button(context, BUTTON_B, 10000ms); + 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); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter battle.", + env.console + ); + } + else { + env.log("Battle started."); + } + + //handle_encounter will wait for "POKEMON appeared!" + bool legendary_shiny = handle_encounter(env.console, context, true); + if (legendary_shiny) { + stats.shinies++; + + send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", env.console.video().snapshot(), true); + break; + } + + //No shiny found + env.log("Soft resetting."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Soft resetting." + ); + + soft_reset(env.program_info(), 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/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h new file mode 100644 index 000000000..f8d019ce8 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h @@ -0,0 +1,50 @@ +/* Legendary Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_LegendaryReset_H +#define PokemonAutomation_PokemonFRLG_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" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +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: + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif + + diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp index 092a5614c..097d5c3c5 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp @@ -70,7 +70,7 @@ PrizeCornerReset::PrizeCornerReset() ) , GO_HOME_WHEN_DONE(true) , NOTIFICATION_SHINY( - "Shiny", + "Shiny found", true, true, ImageAttachmentMode::JPG, {"Notifs", "Showcase"} ) From 2ba13308e1c819573fe88eab70caa8154417a4b8 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 16:52:46 -0500 Subject: [PATCH 5/9] fixes for frlg legendary reset --- .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 28 ++++++++++++++++++- .../PokemonFRLG_LegendaryReset.cpp | 4 +-- SerialPrograms/cmake/SourceFiles.cmake | 2 ++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index 82b5ed776..6e8cf0599 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -149,7 +149,7 @@ bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, boo [&](ProControllerContext& context) { int ret = wait_until( console, context, - std::chrono::seconds(30), + std::chrono::seconds(30), //More than enough time for shiny sound {{legendary_appeared}} ); if (ret == 0) { @@ -163,6 +163,32 @@ bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, boo } pbf_wait(context, 1000ms); context.wait_for_all_requests(); + + /* + //Send out shiny lead to test detection + BattleMenuWatcher battle_menu(COLOR_RED); + console.log("Sending out lead Pokemon."); + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + + int ret2 = wait_until( + console, context, + std::chrono::seconds(15), + { {battle_menu} } + ); + if (ret2 == 0) { + console.log("Battle menu detecteed!"); + } + else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "handle_encounter(): Did not detect battle menu.", + console + ); + } + pbf_wait(context, 100000ms); //extreme audio delay on my cheap test device + context.wait_for_all_requests(); + */ + }, {{shiny_detector}} ); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp index 232b6f7dc..4fe282be6 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp @@ -111,10 +111,10 @@ void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerC } //handle_encounter will wait for "POKEMON appeared!" - bool legendary_shiny = handle_encounter(env.console, context, true); + 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); break; } diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 73970aacb..ae752b29c 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1417,6 +1417,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/PokemonFRLG_Settings.h Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp From bc96e28b631753a7a0c9611d586ca138690ab356 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 18:04:17 -0500 Subject: [PATCH 6/9] flee_battle() from RSE --- .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 41 +++++++++++++++++++ .../PokemonFRLG/PokemonFRLG_Navigation.h | 2 + .../PokemonFRLG_LegendaryReset.cpp | 3 ++ 3 files changed, 46 insertions(+) diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index 6e8cf0599..061b9c807 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -227,6 +227,47 @@ bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, boo return false; } +void flee_battle(ConsoleHandle& console, ProControllerContext& context) { + console.log("Navigate to Run."); + pbf_press_dpad(context, DPAD_RIGHT, 160ms, 160ms); + pbf_press_dpad(context, DPAD_DOWN, 160ms, 160ms); + pbf_press_button(context, BUTTON_A, 160ms, 320ms); + + AdvanceBattleDialogWatcher ran_away(COLOR_YELLOW); + int ret2 = wait_until( + console, context, + std::chrono::seconds(5), + {{ran_away}} + ); + if (ret2 == 0) { + console.log("Running away..."); + } else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "flee_battle(): Unable to navigate to flee button.", + console + ); + } + + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + BlackScreenOverWatcher battle_over(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret3 = wait_until( + console, context, + std::chrono::seconds(5), + {{battle_over}} + ); + if (ret3 == 0) { + console.log("Successfully fled the battle."); + } else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "flee_battle(): Unable to flee from battle.", + console + ); + } +} + + } } } diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h index a216444e3..29f1fcf50 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h @@ -31,6 +31,8 @@ void open_slot_six(ConsoleHandle& console, ProControllerContext& context); // For soft resets, send_out_lead as false and then soft_reset() to save time. bool handle_encounter(ConsoleHandle& console, ProControllerContext& context, bool send_out_lead); +// Run from battle. Cursor must start on the FIGHT button. Assumes fleeing will always work. (Smoke Ball) +void flee_battle(ConsoleHandle& console, ProControllerContext& context); } } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp index 4fe282be6..9eff6efe8 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp @@ -79,6 +79,7 @@ void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerC * Setup: Stand in front of target. Save the game. * Lead can be shiny, reset is before lead is even sent out. * This is the same as RSE resets. + * For deoxys solve the puzzle first. */ while (true) { @@ -90,7 +91,9 @@ void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerC int ret = run_until( env.console, context, [](ProControllerContext& context){ + //Long mash + wait for cases like Deoxys pbf_mash_button(context, BUTTON_B, 10000ms); + pbf_wait(context, 10000ms); context.wait_for_all_requests(); }, {battle_entered} From bab188ef166ae45098d76ea5ecdd6ed4260045db Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 18:04:45 -0500 Subject: [PATCH 7/9] legendary run away --- .../Source/PokemonFRLG/PokemonFRLG_Panels.cpp | 2 + .../PokemonFRLG_LegendaryRunAway.cpp | 281 ++++++++++++++++++ .../PokemonFRLG_LegendaryRunAway.h | 59 ++++ SerialPrograms/cmake/SourceFiles.cmake | 2 + 4 files changed, 344 insertions(+) create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index 75853c676..e2ed54faa 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -12,6 +12,7 @@ #include "Programs/ShinyHunting/PokemonFRLG_GiftReset.h" #include "Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h" +#include "Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h" #include "Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" @@ -36,6 +37,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- Shiny Hunting ----"); 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/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp new file mode 100644 index 000000000..e15098a58 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp @@ -0,0 +1,281 @@ +/* Legendary Run Away + * + * 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 "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 "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h" +#include "PokemonFRLG/PokemonFRLG_Navigation.h" +#include "PokemonFRLG_LegendaryRunAway.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +LegendaryRunAway_Descriptor::LegendaryRunAway_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:LegendaryRunAway", + Pokemon::STRING_POKEMON + " FRLG", "Legendary Run Away", + "Programs/PokemonFRLG/LegendaryRunAway.html", + "Shiny hunt legendary Pokemon using the run away method.", + ProgramControllerClass::StandardController_RequiresPrecision, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +struct LegendaryRunAway_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 LegendaryRunAway_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +LegendaryRunAway::LegendaryRunAway() + : TARGET( + "Target:
", + { + {Target::hooh, "hooh", "Ho-Oh"}, + {Target::lugia, "lugia", "Lugia"}, + }, + LockMode::LOCK_WHILE_RUNNING, + Target::hooh + ) + , 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_OPTION(TARGET); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void LegendaryRunAway::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + 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, 160ms); + + //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, 160ms); + pbf_wait(context, 2400ms); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //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, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0ms, 320ms); + pbf_press_dpad(context, DPAD_LEFT, 320ms, 160ms); + + ssf_press_button(context, BUTTON_B, 0ms, 320ms); + pbf_press_dpad(context, DPAD_RIGHT, 320ms, 160ms); + pbf_wait(context, 2400ms); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + //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); + + ssf_press_button(context, BUTTON_B, 0ms, 1360ms); + pbf_press_dpad(context, DPAD_UP, 1360ms, 160ms); + + context.wait_for_all_requests(); +} + +void LegendaryRunAway::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + 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, 160ms); + + //Turn right, 3 steps right. Wait for black screen over. + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0ms, 720ms); + pbf_press_dpad(context, DPAD_RIGHT, 720ms, 160ms); + pbf_wait(context, 2400ms); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //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, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0ms, 320ms); + pbf_press_dpad(context, DPAD_UP, 320ms, 160ms); + + ssf_press_button(context, BUTTON_B, 0ms, 320ms); + pbf_press_dpad(context, DPAD_DOWN, 320ms, 160ms); + pbf_wait(context, 2400ms); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + //reverse above steps + ssf_press_button(context, BUTTON_B, 0ms, 560ms); + pbf_press_dpad(context, DPAD_LEFT, 560ms, 160ms); + + ssf_press_button(context, BUTTON_B, 0ms, 720ms); + pbf_press_dpad(context, DPAD_UP, 720ms, 160ms); + + context.wait_for_all_requests(); +} + +void LegendaryRunAway::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryRunAway_Descriptor::Stats& stats = env.current_stats(); + + /* + * Settings: Text Speed fast. Default borders. Battle animations off. Audio required. + * Smoke Ball or fast pokemon required. + * Setup: Stand in front of target. Save the game. + * Lead can be shiny, reset is before lead is even sent out. + * This is the same as RSE resets. + */ + + while (true) { + if (TARGET == Target::hooh) { + //Step forward to start the encounter. + pbf_press_dpad(context, DPAD_UP, 160ms, 400ms); + } else { + //Press A to start the encounter. + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + } + + bool legendary_shiny = handle_encounter(env.console, context, true); + if (legendary_shiny) { + stats.shinies++; + env.update_stats(); + send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", env.console.video().snapshot(), true); + break; + } + env.log("No shiny found."); + flee_battle(env.console, context); + + //Close out dialog box + pbf_mash_button(context, BUTTON_B, 2000ms); + context.wait_for_all_requests(); + + //Exit and re-enter the room + switch (TARGET) { + case Target::hooh: + reset_hooh(env, context); + break; + case Target::lugia: + reset_lugia(env, context); + break; + default: + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Invalid target!", + env.console + ); + break; + } + stats.resets++; + env.update_stats(); + } + + 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/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h new file mode 100644 index 000000000..8a05a8648 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h @@ -0,0 +1,59 @@ +/* Legendary Run Away + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_LegendaryRunAway_H +#define PokemonAutomation_PokemonFRLG_LegendaryRunAway_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" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class LegendaryRunAway_Descriptor : public SingleSwitchProgramDescriptor{ +public: + LegendaryRunAway_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class LegendaryRunAway : public SingleSwitchProgramInstance{ +public: + LegendaryRunAway(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override{} + +private: + enum class Target{ + hooh, + lugia, + }; + EnumDropdownOption TARGET; + + void reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + + 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 ae752b29c..a87fc8919 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1419,6 +1419,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp From 8d87e4c9221116d62922f5c0ac71d0bfb6d2099b Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 19:34:22 -0500 Subject: [PATCH 8/9] walk up option for legendary reset --- .../ShinyHunting/PokemonFRLG_LegendaryReset.cpp | 13 ++++++++++++- .../ShinyHunting/PokemonFRLG_LegendaryReset.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp index 9eff6efe8..293ebec96 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.cpp @@ -54,7 +54,12 @@ std::unique_ptr LegendaryReset_Descriptor::make_stats() const{ } LegendaryReset::LegendaryReset() - : GO_HOME_WHEN_DONE(true) + : WALK_UP( + "Walk Up (Ho-Oh only):
Walk up to trigger encounter.", + LockMode::LOCK_WHILE_RUNNING, + false + ) + , GO_HOME_WHEN_DONE(true) , NOTIFICATION_SHINY( "Shiny found", true, true, ImageAttachmentMode::JPG, @@ -67,6 +72,7 @@ LegendaryReset::LegendaryReset() &NOTIFICATION_PROGRAM_FINISH, }) { + PA_ADD_OPTION(WALK_UP); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); } @@ -83,6 +89,11 @@ void LegendaryReset::program(SingleSwitchProgramEnvironment& env, ProControllerC */ while (true) { + if (WALK_UP) { + //Step forward to start the encounter. + pbf_press_dpad(context, DPAD_UP, 320ms, 400ms); + } + //Talk to target pbf_press_button(context, BUTTON_A, 320ms, 320ms); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h index f8d019ce8..624f6c822 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h @@ -35,6 +35,8 @@ class LegendaryReset : public SingleSwitchProgramInstance{ ) override{} private: + BooleanCheckBoxOption WALK_UP; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; EventNotificationOption NOTIFICATION_SHINY; From c9ed6a97e6251ea961166f5fa245c6c198f4d94a Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 23 Feb 2026 19:24:43 -0500 Subject: [PATCH 9/9] ho-oh and lugia values and options --- .../PokemonFRLG_LegendaryRunAway.cpp | 77 ++++++++++++------- .../PokemonFRLG_LegendaryRunAway.h | 8 ++ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp index e15098a58..602139759 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.cpp @@ -76,24 +76,51 @@ LegendaryRunAway::LegendaryRunAway() &NOTIFICATION_STATUS_UPDATE, &NOTIFICATION_PROGRAM_FINISH, }) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , 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, + "1000 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_DOWN( + "Lugia up/down:
Time it takes to run up to Lugia or down away to reset.", + LockMode::LOCK_WHILE_RUNNING, + "600 ms" + ) + , LUGIA_LEFT_RIGHT( + "Lugia left time:
Time it takes walk three steps left after entering Lugia's room.", + LockMode::LOCK_WHILE_RUNNING, + "550 ms" + ) { PA_ADD_OPTION(TARGET); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); + PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(HOOH_UP_DOWN); + PA_ADD_OPTION(HOOH_LEFT_RIGHT); + PA_ADD_OPTION(LUGIA_UP_DOWN); + PA_ADD_OPTION(LUGIA_LEFT_RIGHT); } void LegendaryRunAway::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { 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, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, HOOH_UP_DOWN); + pbf_press_dpad(context, DPAD_DOWN, HOOH_UP_DOWN, 160ms); //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, 160ms); + [&](ProControllerContext& context){ + pbf_press_dpad(context, DPAD_RIGHT, HOOH_LEFT_RIGHT, 160ms); pbf_wait(context, 2400ms); }, {exit_area} @@ -115,12 +142,11 @@ void LegendaryRunAway::reset_hooh(SingleSwitchProgramEnvironment& env, ProContro //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, - [](ProControllerContext& context){ - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_LEFT, 320ms, 160ms); + [&](ProControllerContext& context){ + pbf_press_dpad(context, DPAD_LEFT, HOOH_LEFT_RIGHT, 160ms); - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_RIGHT, 320ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_RIGHT, 500ms, 160ms); pbf_wait(context, 2400ms); }, {enter_area} @@ -140,11 +166,10 @@ void LegendaryRunAway::reset_hooh(SingleSwitchProgramEnvironment& env, ProContro //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(); } @@ -152,8 +177,8 @@ void LegendaryRunAway::reset_hooh(SingleSwitchProgramEnvironment& env, ProContro void LegendaryRunAway::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { 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, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_UP_DOWN); + pbf_press_dpad(context, DPAD_DOWN, LUGIA_UP_DOWN, 160ms); //Turn right, 3 steps right. Wait for black screen over. int ret = run_until( @@ -183,11 +208,11 @@ void LegendaryRunAway::reset_lugia(SingleSwitchProgramEnvironment& env, ProContr int ret2 = run_until( env.console, context, [](ProControllerContext& context){ - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_UP, 320ms, 160ms); - - ssf_press_button(context, BUTTON_B, 0ms, 320ms); - pbf_press_dpad(context, DPAD_DOWN, 320ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_UP, 500ms, 160ms); + context.wait_for_all_requests(); + ssf_press_button(context, BUTTON_B, 0ms, 500ms); + pbf_press_dpad(context, DPAD_DOWN, 500ms, 160ms); pbf_wait(context, 2400ms); }, {enter_area} @@ -206,12 +231,12 @@ void LegendaryRunAway::reset_lugia(SingleSwitchProgramEnvironment& env, ProContr } //reverse above steps - ssf_press_button(context, BUTTON_B, 0ms, 560ms); - pbf_press_dpad(context, DPAD_LEFT, 560ms, 160ms); - - ssf_press_button(context, BUTTON_B, 0ms, 720ms); - pbf_press_dpad(context, DPAD_UP, 720ms, 160ms); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_LEFT_RIGHT); + pbf_press_dpad(context, DPAD_LEFT, LUGIA_LEFT_RIGHT, 160ms); + context.wait_for_all_requests(); + ssf_press_button(context, BUTTON_B, 0ms, LUGIA_UP_DOWN); + pbf_press_dpad(context, DPAD_UP, LUGIA_UP_DOWN, 160ms); context.wait_for_all_requests(); } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h index 8a05a8648..5f54b6266 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h @@ -7,11 +7,13 @@ #ifndef PokemonAutomation_PokemonFRLG_LegendaryRunAway_H #define PokemonAutomation_PokemonFRLG_LegendaryRunAway_H +#include "Common/Cpp/Options/StaticTextOption.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/TimeDurationOption.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -49,6 +51,12 @@ class LegendaryRunAway : public SingleSwitchProgramInstance{ EventNotificationOption NOTIFICATION_SHINY; EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS; + + SectionDividerOption m_advanced_options; + MillisecondsOption HOOH_UP_DOWN; + MillisecondsOption HOOH_LEFT_RIGHT; + MillisecondsOption LUGIA_UP_DOWN; + MillisecondsOption LUGIA_LEFT_RIGHT; }; }