From 3101eade27cb79a19a91c2127845a85fe32362c9 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 30 Mar 2026 14:15:21 -0400 Subject: [PATCH 1/2] frlg detectors for selected party slot --- .../Menus/PokemonFRLG_PartyMenuDetector.cpp | 52 +++++++++++++++++++ .../Menus/PokemonFRLG_PartyMenuDetector.h | 49 +++++++++++++++++ .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 23 ++++++-- .../ShinyHunting/PokemonFRLG_GiftReset.cpp | 28 ++++++++-- 4 files changed, 146 insertions(+), 6 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp index d01e230b2..756f02241 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp @@ -4,6 +4,7 @@ * */ +#include "Common/Cpp/Exceptions.h" #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonFramework/ImageTypes/ImageRGB32.h" #include "CommonFramework/ImageTools/ImageStats.h" @@ -72,6 +73,57 @@ bool PartySelectionDetector::detect(const ImageViewRGB32& screen){ return false; } +ImageFloatBox PartySlotDetector::party_slot_boxes(PartySlot position){ + switch (position){ + case PartySlot::ONE: + return ImageFloatBox(0.130000, 0.163000, 0.200000, 0.009000); + case PartySlot::TWO: + return ImageFloatBox(0.710000, 0.063000, 0.260000, 0.009000); + case PartySlot::THREE: + return ImageFloatBox(0.710000, 0.213000, 0.260000, 0.009000); + case PartySlot::FOUR: + return ImageFloatBox(0.710000, 0.363000, 0.260000, 0.009000); + case PartySlot::FIVE: + return ImageFloatBox(0.710000, 0.513000, 0.260000, 0.009000); + case PartySlot::SIX: + return ImageFloatBox(0.710000, 0.663000, 0.260000, 0.009000); + 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/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h index 46d141a6b..abd482d3f 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h @@ -21,6 +21,15 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonFRLG{ +enum class PartySlot{ + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX + //CXL +}; // The Party menu has a white box on the bottom // The background around the edges is dark teal/navy @@ -63,6 +72,46 @@ class PartySelectionWatcher : public DetectorToFinder{ }; +class PartySlotDetector : public StaticScreenDetector{ +public: + PartySlotDetector( + Color color, + const ImageFloatBox& box + ); + + PartySlotDetector( + Color color, + PartySlot position + ); + + static ImageFloatBox party_slot_boxes(PartySlot position); + + const ImageFloatBox& last_detected() const { return m_last_detected; } + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + const Color m_color; + VideoOverlay* m_overlay; + const ImageFloatBox m_party_box; + + ImageFloatBox m_last_detected; + std::optional m_last_detected_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)) + { + } +}; + + } } diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index 05b19d501..eda690fd5 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -147,7 +147,7 @@ bool try_open_slot_six(ConsoleHandle& console, ProControllerContext& context){ } console.log("Navigating to party menu."); - BlackScreenOverWatcher blk1(COLOR_RED); + PartyMenuWatcher blk1(COLOR_RED); int pm = run_until( console, context, @@ -166,8 +166,25 @@ bool try_open_slot_six(ConsoleHandle& console, ProControllerContext& context){ context.wait_for_all_requests(); //Press up twice to get to the last slot - pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); - pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); + PartySlotWatcher last_slot(COLOR_RED, PartySlot::SIX); + int ps = run_until( + 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){ + console.log("Moved selection to slot six."); + } else{ + console.log("open_slot_six(): Unable to move selection to slot six.", COLOR_RED); + return false; + } //Two presses to open summary BlackScreenOverWatcher blk2(COLOR_RED); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp index 89996a1d5..c266c0598 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp @@ -13,6 +13,7 @@ #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_SummaryDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" @@ -293,7 +294,7 @@ bool GiftReset::try_open_summary(SingleSwitchProgramEnvironment& env, ProControl } //Open party menu - BlackScreenOverWatcher blk1(COLOR_RED); + PartyMenuWatcher blk1(COLOR_RED); int pm = run_until( env.console, context, @@ -318,8 +319,29 @@ bool GiftReset::try_open_summary(SingleSwitchProgramEnvironment& env, ProControl //Press up twice to get to the last slot if (TARGET != Target::starters){ - pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); - pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); + 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 From 5d24f867ecbdfe9ecaff7d41875e674c05f97cc1 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Mon, 30 Mar 2026 15:02:21 -0400 Subject: [PATCH 2/2] Update PokemonFRLG_PartyMenuDetector.h --- .../Inference/Menus/PokemonFRLG_PartyMenuDetector.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h index abd482d3f..74ab616a6 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h @@ -86,18 +86,12 @@ class PartySlotDetector : public StaticScreenDetector{ static ImageFloatBox party_slot_boxes(PartySlot position); - const ImageFloatBox& last_detected() const { return m_last_detected; } - virtual void make_overlays(VideoOverlaySet& items) const override; virtual bool detect(const ImageViewRGB32& screen) override; private: const Color m_color; - VideoOverlay* m_overlay; const ImageFloatBox m_party_box; - - ImageFloatBox m_last_detected; - std::optional m_last_detected_box; }; class PartySlotWatcher : public DetectorToFinder{ public: