From 3b23c707a2082e061658f4f42f4d09745d5da43d Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 09:38:44 -0500 Subject: [PATCH 1/7] FRLG panels + sound listener --- SerialPrograms/Source/PanelLists.cpp | 2 + .../Sounds/PokemonFRLG_ShinySoundDetector.cpp | 47 ++++++++ .../Sounds/PokemonFRLG_ShinySoundDetector.h | 36 +++++++ .../Source/PokemonFRLG/PokemonFRLG_Panels.cpp | 51 +++++++++ .../Source/PokemonFRLG/PokemonFRLG_Panels.h | 30 ++++++ .../PokemonFRLG/PokemonFRLG_Settings.cpp | 91 ++++++++++++++++ .../Source/PokemonFRLG/PokemonFRLG_Settings.h | 55 ++++++++++ .../PokemonFRLG_SoundListener.cpp | 101 ++++++++++++++++++ .../TestPrograms/PokemonFRLG_SoundListener.h | 53 +++++++++ SerialPrograms/cmake/SourceFiles.cmake | 8 ++ 10 files changed, 474 insertions(+) create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.h create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h diff --git a/SerialPrograms/Source/PanelLists.cpp b/SerialPrograms/Source/PanelLists.cpp index d9d186a643..a45f33b2f6 100644 --- a/SerialPrograms/Source/PanelLists.cpp +++ b/SerialPrograms/Source/PanelLists.cpp @@ -17,6 +17,7 @@ #include "PokemonSwSh/PokemonSwSh_Panels.h" #include "PokemonHome/PokemonHome_Panels.h" #include "PokemonBDSP/PokemonBDSP_Panels.h" +#include "PokemonFRLG/PokemonFRLG_Panels.h" #include "PokemonLA/PokemonLA_Panels.h" #include "PokemonLGPE/PokemonLGPE_Panels.h" #include "PokemonRSE/PokemonRSE_Panels.h" @@ -56,6 +57,7 @@ ProgramSelect::ProgramSelect(QWidget& parent, PanelHolder& holder) add(std::make_unique()); if (PreloadSettings::instance().DEVELOPER_MODE){ add(std::make_unique()); + add(std::make_unique()); } add(std::make_unique()); diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp new file mode 100644 index 0000000000..077fca53e0 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp @@ -0,0 +1,47 @@ +/* Shiny Sound Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonTools/Audio/AudioTemplateCache.h" +#include "CommonTools/Audio/SpectrogramMatcher.h" +#include "NintendoSwitch/NintendoSwitch_ConsoleHandle.h" +#include "PokemonFRLG/PokemonFRLG_Settings.h" +#include "PokemonFRLG_ShinySoundDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +ShinySoundDetector::ShinySoundDetector(Logger& logger, DetectedCallback detected_callback) + // Use a yellow as the detection color because the shiny animation is yellow. + : AudioPerSpectrumDetectorBase( + logger, + "ShinySoundDetector", + "Shiny sound", + COLOR_YELLOW, + detected_callback + ) +{} + + +float ShinySoundDetector::get_score_threshold() const{ + return (float)GameSettings::instance().SHINY_SOUND_THRESHOLD; +} + +std::unique_ptr ShinySoundDetector::build_spectrogram_matcher(size_t sample_rate){ + return std::make_unique( + "Shiny Sound", + AudioTemplateCache::instance().get_throw("PokemonRSE/ShinySound", sample_rate), + SpectrogramMatcher::Mode::SPIKE_CONV, sample_rate, + GameSettings::instance().SHINY_SOUND_LOW_FREQUENCY + ); +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h new file mode 100644 index 0000000000..4547c37081 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h @@ -0,0 +1,36 @@ +/* Shiny Sound Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_ShinySoundDetector_H +#define PokemonAutomation_PokemonFRLG_ShinySoundDetector_H + +#include "CommonTools/Audio/AudioPerSpectrumDetectorBase.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +class ShinySoundDetector : public AudioPerSpectrumDetectorBase{ +public: + // Warning: The callback will be called from the audio inference thread. + ShinySoundDetector(Logger& logger, DetectedCallback detected_callback); + + // Implement AudioPerSpectrumDetectorBase::get_score_threshold() + virtual float get_score_threshold() const override; + +protected: + // Implement AudioPerSpectrumDetectorBase::build_spectrogram_matcher() + virtual std::unique_ptr build_spectrogram_matcher(size_t sample_rate) override; +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp new file mode 100644 index 0000000000..895a5d88c7 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -0,0 +1,51 @@ +/* Pokemon FRLG Panels + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/GlobalSettingsPanel.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonFRLG_Panels.h" + +#include "PokemonFRLG_Settings.h" + +//#include "Programs/ShinyHunting/PokemonFRLG_StarterReset.h" +#include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + + +PanelListFactory::PanelListFactory() + : PanelListDescriptor(Pokemon::STRING_POKEMON + " Fire Red and Leaf Green") +{} + +std::vector PanelListFactory::make_panels() const{ + std::vector ret; + + ret.emplace_back("---- Settings ----"); + ret.emplace_back(make_settings()); + + //ret.emplace_back("---- General ----"); + + ret.emplace_back("---- Shiny Hunting ----"); + //ret.emplace_back(make_single_switch_program()); + + + if (PreloadSettings::instance().DEVELOPER_MODE){ + ret.emplace_back("---- Developer Tools ----"); + ret.emplace_back(make_single_switch_program()); + } + + return ret; +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.h new file mode 100644 index 0000000000..b579755fb8 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.h @@ -0,0 +1,30 @@ +/* Pokemon FRLG Panels + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_Panels_H +#define PokemonAutomation_PokemonFRLG_Panels_H + +#include "CommonFramework/Panels/PanelList.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + + +class PanelListFactory : public PanelListDescriptor{ +public: + PanelListFactory(); +private: + virtual std::vector make_panels() const override; +}; + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp new file mode 100644 index 0000000000..d8626dc2f0 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp @@ -0,0 +1,91 @@ +/* Pokemon FRLG Settings + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Pokemon/Pokemon_Strings.h" + +#include "PokemonFRLG_Settings.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +using namespace Pokemon; + + + +GameSettings& GameSettings::instance(){ + static GameSettings settings; + return settings; +} +GameSettings::GameSettings() + : BatchOption(LockMode::LOCK_WHILE_RUNNING) + , m_soft_reset_timings("Soft Reset Timings:") + , START_BUTTON_MASH0( + "Start Button Mash:
Mash Start for this long after a soft reset to get to the main menu.", + LockMode::LOCK_WHILE_RUNNING, + "5000 ms" + ) + , ENTER_GAME_WAIT0( + "Enter Game Wait:
Wait this long for the game to load.", + LockMode::LOCK_WHILE_RUNNING, + "3000 ms" + ) + , m_shiny_audio_settings("Shiny Audio Settings:") + , SHINY_SOUND_THRESHOLD( + "Shiny Sound Threshold:
Maximum error coefficient to trigger a shiny detection.", + LockMode::LOCK_WHILE_RUNNING, + 0.97, 0, 1.0 + ) + , SHINY_SOUND_LOW_FREQUENCY( + "Shiny Sound Low Frequency (Hz):
High pass filter frequency for shiny sound.", + LockMode::LOCK_WHILE_RUNNING, + 5000, 0, 48000 + ) +{ + PA_ADD_STATIC(m_soft_reset_timings); + PA_ADD_OPTION(START_BUTTON_MASH0); + PA_ADD_OPTION(ENTER_GAME_WAIT0); + PA_ADD_STATIC(m_shiny_audio_settings); + PA_ADD_OPTION(SHINY_SOUND_THRESHOLD); + PA_ADD_OPTION(SHINY_SOUND_LOW_FREQUENCY); +} + + + + + +GameSettings_Descriptor::GameSettings_Descriptor() + : PanelDescriptor( + Color(), + "PokemonFRLG:GlobalSettings", + STRING_POKEMON + " FRLG", "Game Settings", + "Programs/PokemonFRLG/FRLGSettings.html", + "Global " + STRING_POKEMON + " Fire Red and Leaf Green Settings" + ) +{} + + + +GameSettingsPanel::GameSettingsPanel(const GameSettings_Descriptor& descriptor) + : SettingsPanelInstance(descriptor) + , settings(GameSettings::instance()) +{ + PA_ADD_OPTION(settings); +} + + + + + +} +} +} + + + + + + diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h new file mode 100644 index 0000000000..d649a3d0a8 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h @@ -0,0 +1,55 @@ +/* Pokemon FRLG Settings + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_Settings_H +#define PokemonAutomation_PokemonFRLG_Settings_H + +#include "Common/Cpp/Options/StaticTextOption.h" +#include "Common/Cpp/Options/FloatingPointOption.h" +#include "Common/Cpp/Options/TimeDurationOption.h" +#include "CommonFramework/Panels/SettingsPanel.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +class GameSettings : public BatchOption{ + GameSettings(); +public: + static GameSettings& instance(); + + SectionDividerOption m_soft_reset_timings; + MillisecondsOption START_BUTTON_MASH0; + MillisecondsOption ENTER_GAME_WAIT0; + + SectionDividerOption m_shiny_audio_settings; + FloatingPointOption SHINY_SOUND_THRESHOLD; + FloatingPointOption SHINY_SOUND_LOW_FREQUENCY; + +}; + + + + +class GameSettings_Descriptor : public PanelDescriptor{ +public: + GameSettings_Descriptor(); +}; + + +class GameSettingsPanel : public SettingsPanelInstance{ +public: + GameSettingsPanel(const GameSettings_Descriptor& descriptor); +private: + GameSettings& settings; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp new file mode 100644 index 0000000000..9798ea5173 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp @@ -0,0 +1,101 @@ +/* Sound Listener + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include +#include +#include +#include +#include +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Containers/AlignedVector.tpp" +#include "CommonFramework/AudioPipeline/AudioFeed.h" +#include "CommonFramework/AudioPipeline/AudioTemplate.h" +#include "CommonTools/Audio/AudioTemplateCache.h" +#include "CommonTools/Audio/SpectrogramMatcher.h" +#include "CommonTools/Async/InferenceSession.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h" +#include "PokemonFRLG_SoundListener.h" + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + using namespace Pokemon; + + +SoundListener_Descriptor::SoundListener_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:SoundListener", + STRING_POKEMON + " FRLG", "Sound Listener", + "", + "Test sound detectors listening to audio stream.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::ENABLE_COMMANDS + ) +{} + + +SoundListener::SoundListener() + : SOUND_TYPE("Which Sound to Detect", + { + {SoundType::SHINY, "shiny", "Shiny Sound"}, + }, + LockMode::LOCK_WHILE_RUNNING, + SoundType::SHINY + ) + , STOP_ON_DETECTED_SOUND( + "Stop on the detected sound
Stop program when the sound is detected.", + LockMode::LOCK_WHILE_RUNNING, + false + ) +{ + PA_ADD_OPTION(SOUND_TYPE); + PA_ADD_OPTION(STOP_ON_DETECTED_SOUND); +} + +void SoundListener::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + // search_alpha_roar_from_audio_dump(); + // return; + + std::cout << "Running audio test program." << std::endl; + + std::shared_ptr detector; + auto action = [&](float error_coefficient) -> bool{ + // This lambda function will be called when the sound is detected. + // Its return will determine whether to stop the program: + return STOP_ON_DETECTED_SOUND; + }; + + SoundType type = SOUND_TYPE; + switch (type){ + case SoundType::SHINY: + detector = std::make_shared(env.console, action); + break; + default: + throw InternalProgramError( + &env.logger(), PA_CURRENT_FUNCTION, + "Not such sound detector as sound type " + std::to_string((size_t)type) + ); + return; + } + + InferenceSession session( + context, env.console, + {{*detector, std::chrono::milliseconds(20)}} + ); + context.wait_until_cancel(); + + std::cout << "Audio test program Sound listener finished." << std::endl; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h new file mode 100644 index 0000000000..904f98f272 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h @@ -0,0 +1,53 @@ +/* Sound Listener + * + * From: https://github.com/PokemonAutomation/ + * + * Debug program to test all kinds of sound detectors. + */ + +#ifndef PokemonAutomation_PokemonFRLG_SoundListener_H +#define PokemonAutomation_PokemonFRLG_SoundListener_H + +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "Common/Cpp/Options/EnumDropdownOption.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +//#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +class SoundListener_Descriptor : public SingleSwitchProgramDescriptor{ +public: + SoundListener_Descriptor(); +}; + + +class SoundListener : public SingleSwitchProgramInstance{ +public: + SoundListener(); + + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override{} + +private: + enum class SoundType{ + SHINY, + }; + + EnumDropdownOption SOUND_TYPE; + BooleanCheckBoxOption STOP_ON_DETECTED_SOUND; +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 8ca2c37409..57bc4f11eb 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1397,6 +1397,14 @@ file(GLOB LIBRARY_SOURCES Source/PokemonBDSP/Programs/Trading/PokemonBDSP_TradeRoutines.h Source/PokemonBDSP/Resources/PokemonBDSP_NameDatabase.cpp Source/PokemonBDSP/Resources/PokemonBDSP_NameDatabase.h + Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp + Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h + Source/PokemonFRLG/PokemonFRLG_Panels.cpp + Source/PokemonFRLG/PokemonFRLG_Panels.h + Source/PokemonFRLG/PokemonFRLG_Settings.cpp + Source/PokemonFRLG/PokemonFRLG_Settings.h + Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp + Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h Source/PokemonHome/Inference/PokemonHome_BallReader.cpp Source/PokemonHome/Inference/PokemonHome_BallReader.h Source/PokemonHome/Inference/PokemonHome_BoxGenderDetector.cpp From 1dd5d39bcaf40768c0f3fb74446e6fb918531a3c Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 13:11:00 -0500 Subject: [PATCH 2/7] fix name, advance dialog detector --- .../Dialogs/PokemonFRLG_DialogDetector.cpp | 103 ++++++++++++++++++ .../Dialogs/PokemonFRLG_DialogDetector.h | 79 ++++++++++++++ .../Source/PokemonFRLG/PokemonFRLG_Panels.cpp | 6 +- .../PokemonFRLG/PokemonFRLG_Settings.cpp | 2 +- SerialPrograms/cmake/SourceFiles.cmake | 6 + 5 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp new file mode 100644 index 0000000000..6eca531cfc --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp @@ -0,0 +1,103 @@ +/* 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 "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "CommonTools/ImageMatch/WaterfillTemplateMatcher.h" +#include "CommonFramework/VideoPipeline/VideoOverlay.h" +#include "PokemonFRLG_DialogDetector.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +WhiteDialogDetector::WhiteDialogDetector(Color color) + : m_right_box(0.812, 0.726, 0.013, 0.169) + , m_top_box(0.178, 0.716, 0.632, 0.017) + , m_bottom_box(0.178, 0.888, 0.632, 0.011) +{} +void WhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_right_box); + items.add(COLOR_RED, m_top_box); + items.add(COLOR_RED, m_bottom_box); +} +bool WhiteDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 right_image = extract_box_reference(screen, m_right_box); + ImageViewRGB32 top_image = extract_box_reference(screen, m_top_box); + ImageViewRGB32 bottom_image = extract_box_reference(screen, m_bottom_box); + if (is_solid(right_image, { 0.25, 0.38, 0.369 }) + && is_solid(top_image, { 0.25, 0.38, 0.369 }) + && is_solid(bottom_image, { 0.25, 0.38, 0.369 }) + ){ + return true; + } + return false; +} + +// Detect the red advancement arrow by filtering for DARK red. +// There is red/pink color text for female npcs in non-japan versions +// might be an issue if using it for things other than shiny-hunt resetting +// all gifts are black/blue dialogs? +AdvanceWhiteDialogDetector::AdvanceWhiteDialogDetector(Color color) + : m_dialog_box(0.170, 0.726, 0.655, 0.172) + , m_right_box(0.812, 0.726, 0.013, 0.169) + , m_top_box(0.178, 0.716, 0.632, 0.017) + , m_bottom_box(0.178, 0.888, 0.632, 0.011) +{} +void AdvanceWhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_dialog_box); + items.add(COLOR_RED, m_right_box); + items.add(COLOR_RED, m_top_box); + items.add(COLOR_RED, m_bottom_box); +} +bool AdvanceWhiteDialogDetector::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 right_image = extract_box_reference(screen, m_right_box); + ImageViewRGB32 top_image = extract_box_reference(screen, m_top_box); + ImageViewRGB32 bottom_image = extract_box_reference(screen, m_bottom_box); + + if (is_solid(right_image, { 0.25, 0.38, 0.369 }) + && is_solid(top_image, { 0.25, 0.38, 0.369 }) + && is_solid(bottom_image, { 0.25, 0.38, 0.369 }) + && (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 new file mode 100644 index 0000000000..4b1fc7b1de --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h @@ -0,0 +1,79 @@ +/* Dialog Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_DialogDetector_H +#define PokemonAutomation_PokemonFRLG_DialogDetector_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 PokemonFRLG{ + +// When given a choice popup, there is no advance arrow. +// FRLG doesn't have an advance arrow on the final line of dialog when speaking to an npc! + +//Standard - npc dialog, interacting with items, etc. +//tutorial - white box with grey mat like a picture frame + +// Common dialog box for npcs, etc. +// This one detects the final line spoken, so no red arrow +class WhiteDialogDetector : public StaticScreenDetector{ +public: + WhiteDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_right_box; + ImageFloatBox m_top_box; + ImageFloatBox m_bottom_box; +}; +class WhiteDialogWatcher : public DetectorToFinder{ +public: + WhiteDialogWatcher(Color color) + : DetectorToFinder("WhiteDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + +// Same as WhiteDialogDetector, but filter for the red arrow +class AdvanceWhiteDialogDetector : public StaticScreenDetector{ +public: + AdvanceWhiteDialogDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const override; + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + ImageFloatBox m_dialog_box; + ImageFloatBox m_right_box; + ImageFloatBox m_top_box; + ImageFloatBox m_bottom_box; +}; +class AdvanceWhiteDialogWatcher : public DetectorToFinder{ +public: + AdvanceWhiteDialogWatcher(Color color) + : DetectorToFinder("AdvanceWhiteDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + + + + +} +} +} + +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index 895a5d88c7..3a6ee40648 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -10,7 +10,7 @@ #include "PokemonFRLG_Settings.h" -//#include "Programs/ShinyHunting/PokemonFRLG_StarterReset.h" +#include "Programs/ShinyHunting/PokemonFRLG_StarterReset.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" namespace PokemonAutomation{ @@ -20,7 +20,7 @@ namespace PokemonFRLG{ PanelListFactory::PanelListFactory() - : PanelListDescriptor(Pokemon::STRING_POKEMON + " Fire Red and Leaf Green") + : PanelListDescriptor(Pokemon::STRING_POKEMON + " FireRed and LeafGreen") {} std::vector PanelListFactory::make_panels() const{ @@ -32,7 +32,7 @@ std::vector PanelListFactory::make_panels() const{ //ret.emplace_back("---- General ----"); ret.emplace_back("---- Shiny Hunting ----"); - //ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); if (PreloadSettings::instance().DEVELOPER_MODE){ diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp index d8626dc2f0..245092a513 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp @@ -63,7 +63,7 @@ GameSettings_Descriptor::GameSettings_Descriptor() "PokemonFRLG:GlobalSettings", STRING_POKEMON + " FRLG", "Game Settings", "Programs/PokemonFRLG/FRLGSettings.html", - "Global " + STRING_POKEMON + " Fire Red and Leaf Green Settings" + "Global " + STRING_POKEMON + " FireRed and LeafGreen Settings" ) {} diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 57bc4f11eb..a2500d883a 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1397,12 +1397,18 @@ file(GLOB LIBRARY_SOURCES Source/PokemonBDSP/Programs/Trading/PokemonBDSP_TradeRoutines.h Source/PokemonBDSP/Resources/PokemonBDSP_NameDatabase.cpp Source/PokemonBDSP/Resources/PokemonBDSP_NameDatabase.h + Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp + Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h + Source/PokemonFRLG/PokemonFRLG_Navigation.cpp + Source/PokemonFRLG/PokemonFRLG_Navigation.h Source/PokemonFRLG/PokemonFRLG_Panels.cpp Source/PokemonFRLG/PokemonFRLG_Panels.h Source/PokemonFRLG/PokemonFRLG_Settings.cpp Source/PokemonFRLG/PokemonFRLG_Settings.h + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h Source/PokemonHome/Inference/PokemonHome_BallReader.cpp From 4f9ca8d0053a6969606a9584ac068dafcdbe4cc0 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 14:14:14 -0500 Subject: [PATCH 3/7] start starter reset, dialog fixes --- .../Dialogs/PokemonFRLG_DialogDetector.cpp | 39 ++- .../Dialogs/PokemonFRLG_DialogDetector.h | 26 ++ .../ShinyHunting/PokemonFRLG_StarterReset.cpp | 261 ++++++++++++++++++ .../ShinyHunting/PokemonFRLG_StarterReset.h | 51 ++++ 4 files changed, 369 insertions(+), 8 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp index 6eca531cfc..78961198d1 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp @@ -27,8 +27,8 @@ namespace PokemonFRLG{ WhiteDialogDetector::WhiteDialogDetector(Color color) : m_right_box(0.812, 0.726, 0.013, 0.169) - , m_top_box(0.178, 0.716, 0.632, 0.017) - , m_bottom_box(0.178, 0.888, 0.632, 0.011) + , m_top_box(0.175, 0.715, 0.649, 0.005) + , m_bottom_box(0.177, 0.896, 0.645, 0.008) {} void WhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{ items.add(COLOR_RED, m_right_box); @@ -48,15 +48,11 @@ bool WhiteDialogDetector::detect(const ImageViewRGB32& screen){ return false; } -// Detect the red advancement arrow by filtering for DARK red. -// There is red/pink color text for female npcs in non-japan versions -// might be an issue if using it for things other than shiny-hunt resetting -// all gifts are black/blue dialogs? AdvanceWhiteDialogDetector::AdvanceWhiteDialogDetector(Color color) : m_dialog_box(0.170, 0.726, 0.655, 0.172) , m_right_box(0.812, 0.726, 0.013, 0.169) - , m_top_box(0.178, 0.716, 0.632, 0.017) - , m_bottom_box(0.178, 0.888, 0.632, 0.011) + , m_top_box(0.175, 0.715, 0.649, 0.005) + , m_bottom_box(0.177, 0.896, 0.645, 0.008) {} void AdvanceWhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{ items.add(COLOR_RED, m_dialog_box); @@ -97,6 +93,33 @@ bool AdvanceWhiteDialogDetector::detect(const ImageViewRGB32& screen){ return false; } +SelectionDialogDetector::SelectionDialogDetector(Color color) + : m_right_box(0.812, 0.726, 0.013, 0.169) + , m_top_box(0.175, 0.715, 0.649, 0.005) + , m_bottom_box(0.177, 0.896, 0.645, 0.008) + , m_selection_box(0.783, 0.457, 0.017, 0.177) +{} +void SelectionDialogDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_right_box); + items.add(COLOR_RED, m_top_box); + items.add(COLOR_RED, m_bottom_box); + items.add(COLOR_RED, m_selection_box); +} +bool SelectionDialogDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 right_image = extract_box_reference(screen, m_right_box); + ImageViewRGB32 top_image = extract_box_reference(screen, m_top_box); + ImageViewRGB32 bottom_image = extract_box_reference(screen, m_bottom_box); + ImageViewRGB32 selection_image = extract_box_reference(screen, m_selection_box); + if (is_solid(right_image, { 0.25, 0.38, 0.369 }) + && is_solid(top_image, { 0.25, 0.38, 0.369 }) + && is_solid(bottom_image, { 0.25, 0.38, 0.369 }) + && is_solid(selection_image, { 0.25, 0.38, 0.369 }) + ){ + 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 4b1fc7b1de..a77b393815 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h @@ -29,6 +29,7 @@ namespace PokemonFRLG{ // Common dialog box for npcs, etc. // This one detects the final line spoken, so no red arrow +// This also detects all the other white dialogs, so use sparingly class WhiteDialogDetector : public StaticScreenDetector{ public: WhiteDialogDetector(Color color); @@ -49,6 +50,10 @@ class WhiteDialogWatcher : public DetectorToFinder{ }; // Same as WhiteDialogDetector, but filter for the red arrow +// Detect the red advancement arrow by filtering for DARK red. +// There is red/pink color text for female npcs in non-japan versions +// might be an issue if using it for things other than shiny-hunt resetting +// all gifts are black/blue dialogs? class AdvanceWhiteDialogDetector : public StaticScreenDetector{ public: AdvanceWhiteDialogDetector(Color color); @@ -69,6 +74,27 @@ class AdvanceWhiteDialogWatcher : public DetectorToFinder{ +public: + SelectionDialogWatcher(Color color) + : DetectorToFinder("SelectionDialogWatcher", std::chrono::milliseconds(250), color) + {} +}; + diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp new file mode 100644 index 0000000000..b061cfaaf4 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp @@ -0,0 +1,261 @@ +/* Starter 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/PokemonFRLG_Navigation.h" +#include "PokemonFRLG_StarterReset.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +StarterReset_Descriptor::StarterReset_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:StarterReset", + Pokemon::STRING_POKEMON + " FRLG", "Starter Reset", + "Programs/PokemonFRLG/StarterReset.html", + "Soft reset for a shiny starter.", + ProgramControllerClass::StandardController_RequiresPrecision, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +struct StarterReset_Descriptor::Stats : public StatsTracker{ + Stats() + : resets(m_stats["Resets"]) + , shinystarter(m_stats["Shiny Starter"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Resets"); + m_display_order.emplace_back("Shiny Starter"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + std::atomic& resets; + std::atomic& shinystarter; + std::atomic& errors; +}; +std::unique_ptr StarterReset_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +StarterReset::StarterReset() + : GO_HOME_WHEN_DONE(true) + , NOTIFICATION_SHINY_STARTER( + "Shiny Starter", + true, true, ImageAttachmentMode::JPG, + {"Notifs", "Showcase"} + ) + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_SHINY_STARTER, + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + }) +{ + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +//Pick up starter, say no to nickname +void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + StarterReset_Descriptor::Stats& stats = env.current_stats(); + + /* + Ah, STARTER is your choice... red adv arrow + So you're claiming... YES/NO box + rly quite energetic... no arrow + player received the starter from... no arrow, jingle must complete + nickname YES/NO box + wait, then rival picks up... no arrow + rival received the ... no arrow DONE + */ + + env.log("Obtaining starter."); + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + bool seen_selection_arrow = false; + bool seen_nickname_arrow = false; + while (true){ + context.wait_for_all_requests(); + + AdvanceWhiteDialogWatcher adv_white(COLOR_RED); + SelectionDialogWatcher selection_dialog(COLOR_RED); + //WhiteDialogWatcher white_dialog_no_arrow(COLOR_RED); + + int ret = wait_until( + env.console, context, + 10s, + { + adv_white, + selection_dialog, + //white_dialog_no_arrow, + } + ); + 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) { + env.log("First selection box detected. YES to starter."); + seen_selection_arrow = true; + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + //Skip past energetic and jingle + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 500ms); + context.wait_for_all_requests(); + 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); + seen_nickname_arrow = true; + + //Press B some to try and skip the rival's pickup + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + break; + } + continue; + //case 2: + // if (seen_selection_arrow) { + // env.log("White dialog box detected. Pressing B."); + // pbf_press_button(context, BUTTON_B, 320ms, 640ms); + // } + // continue; + default: + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "obtain_starter(): No recognized state after 10 seconds.", + env.console + ); + } + } + context.wait_for_all_requests(); +} + + +void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + //StartProgramChecks::check_performance_class_wired_or_wireless(context); + + //StarterReset_Descriptor::Stats& stats = env.current_stats(); + + /* + * Settings: Text Speed fast. + * Setup: Stand in front of the starter you want. Save the game. + */ + + obtain_starter(env, context); + + //From no to nickname to overworld + //open_menu + + /* + bool shiny_starter = false; + while (!shiny_starter) { + env.log("Selecting starter."); + pbf_press_button(context, BUTTON_A, 320ms, 1440ms); + context.wait_for_all_requests(); + + env.log("Starting battle."); + + //Now mash B until the battle menu appears + BattleMenuWatcher battle_menu(COLOR_RED); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_mash_button(context, BUTTON_B, 8000ms); + }, + {battle_menu} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.console.log("Failed to detect battle menu.", COLOR_RED); + } + else { + env.log("Battle menu detected."); + } + + //Open the summary and check the color of the number + 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}); + int ret2 = wait_until( + env.console, context, + std::chrono::milliseconds(3000), + {{detector}} + ); + if (ret2 == 0){ + env.log("Entered party menu."); + }else{ + env.log("Timed out waiting to enter party menu.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "StarterReset: Timed out waiting to enter party menu.", + env.console + ); + } + + pbf_press_button(context, BUTTON_A, 160ms, 1440ms); + pbf_press_dpad(context, DPAD_DOWN, 320ms, 640ms); + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); + + VideoSnapshot screen = env.console.video().snapshot(); + + ShinyNumberDetector shiny_checker(COLOR_YELLOW); + shiny_starter = shiny_checker.read(env.console.logger(), screen); + + if (shiny_starter) { + env.log("Shiny starter detected!"); + stats.shinystarter++; + send_program_status_notification(env, NOTIFICATION_SHINY_STARTER, "Shiny starter found!", screen, true); + } + else { + env.log("Starter is not shiny."); + env.log("Soft resetting."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Soft resetting." + ); + soft_reset(env.program_info(), env.console, context); + stats.resets++; + } + } + */ + + 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_StarterReset.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h new file mode 100644 index 0000000000..ee99eaefd9 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h @@ -0,0 +1,51 @@ +/* Starter Reset + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_StarterReset_H +#define PokemonAutomation_PokemonFRLG_StarterReset_H + +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class StarterReset_Descriptor : public SingleSwitchProgramDescriptor{ +public: + StarterReset_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class StarterReset : public SingleSwitchProgramInstance{ +public: + StarterReset(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override{} + +private: + void obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY_STARTER; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif + + + From 83e861568343df342ca044e65a5a763a049ab7b6 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 14:58:32 -0500 Subject: [PATCH 4/7] start menu detection --- .../Menus/PokemonFRLG_StartMenuDetector.cpp | 50 +++++++++++++++++++ .../Menus/PokemonFRLG_StartMenuDetector.h | 49 ++++++++++++++++++ .../ShinyHunting/PokemonFRLG_StarterReset.cpp | 39 ++++++++++++--- SerialPrograms/cmake/SourceFiles.cmake | 2 + 4 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.cpp new file mode 100644 index 0000000000..28ed63f050 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.cpp @@ -0,0 +1,50 @@ +/* Start Menu 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 "CommonTools/Images/SolidColorTest.h" +#include "CommonTools/Images/WaterfillUtilities.h" +#include "CommonTools/ImageMatch/WaterfillTemplateMatcher.h" +#include "CommonFramework/VideoPipeline/VideoOverlay.h" +#include "PokemonFRLG_StartMenuDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +StartMenuDetector::StartMenuDetector(Color color) + : m_right_box(0.864, 0.750, 0.006, 0.173) + , m_top_box(0.128, 0.744, 0.743, 0.006) + , m_bottom_box(0.128, 0.917, 0.736, 0.006) +{} +void StartMenuDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_right_box); + items.add(COLOR_RED, m_top_box); + items.add(COLOR_RED, m_bottom_box); +} +bool StartMenuDetector::detect(const ImageViewRGB32& screen){ + ImageViewRGB32 right_image = extract_box_reference(screen, m_right_box); + ImageViewRGB32 top_image = extract_box_reference(screen, m_top_box); + ImageViewRGB32 bottom_image = extract_box_reference(screen, m_bottom_box); + if (is_solid(right_image, { 0.00, 0.38, 0.62 }) + && is_solid(top_image, { 0.00, 0.38, 0.62 }) + && is_solid(bottom_image, { 0.00, 0.38, 0.62 }) + ){ + return true; + } + return false; +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h new file mode 100644 index 0000000000..914d52a864 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h @@ -0,0 +1,49 @@ +/* Start Menu Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_StartMenuDetector_H +#define PokemonAutomation_PokemonFRLG_StartMenuDetector_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 PokemonFRLG{ + +// Detect Start menu by looking for the blue info panel +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_right_box; + ImageFloatBox m_top_box; + ImageFloatBox m_bottom_box; +}; +class StartMenuWatcher : public DetectorToFinder{ +public: + StartMenuWatcher(Color color) + : DetectorToFinder("StartMenuWatcher", std::chrono::milliseconds(250), color) + {} +}; + + +} +} +} + +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp index b061cfaaf4..9154b501e8 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp @@ -15,6 +15,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h" #include "PokemonFRLG/PokemonFRLG_Navigation.h" #include "PokemonFRLG_StarterReset.h" @@ -88,7 +89,7 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro pbf_press_button(context, BUTTON_A, 320ms, 640ms); bool seen_selection_arrow = false; - bool seen_nickname_arrow = false; + //bool seen_nickname_arrow = false; while (true){ context.wait_for_all_requests(); @@ -128,13 +129,14 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro } else { env.log("Second selection box detected. NO to nickname."); pbf_press_button(context, BUTTON_B, 320ms, 640ms); - seen_nickname_arrow = true; + //seen_nickname_arrow = true; //Press B some to try and skip the rival's pickup pbf_press_button(context, BUTTON_B, 320ms, 640ms); pbf_press_button(context, BUTTON_B, 320ms, 640ms); pbf_press_button(context, BUTTON_B, 320ms, 640ms); - break; + context.wait_for_all_requests(); + return; } continue; //case 2: @@ -160,7 +162,7 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ //StartProgramChecks::check_performance_class_wired_or_wireless(context); - //StarterReset_Descriptor::Stats& stats = env.current_stats(); + StarterReset_Descriptor::Stats& stats = env.current_stats(); /* * Settings: Text Speed fast. @@ -168,10 +170,35 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon */ obtain_starter(env, context); - + //From no to nickname to overworld - //open_menu + StartMenuWatcher start_menu(COLOR_RED); + + int ret = run_until( + env.console, context, + [](ProControllerContext& context) { + for (int i = 0; i < 5; i++) { + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 500ms); + context.wait_for_all_requests(); + pbf_press_button(context, BUTTON_PLUS, 320ms, 640ms); + } + }, + { start_menu } + ); + context.wait_for_all_requests(); + if (ret < 0){ + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Unable to open Start menu.", + env.console + ); + } + //No Pokedex yet, so 1 A press to open party menu + /* bool shiny_starter = false; while (!shiny_starter) { diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index a2500d883a..dab42f4151 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1399,6 +1399,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonBDSP/Resources/PokemonBDSP_NameDatabase.h Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.cpp Source/PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.cpp + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h Source/PokemonFRLG/PokemonFRLG_Navigation.cpp From 86d645ee0d442a07a2ded6a80e0cd3242e5815e6 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 16:14:40 -0500 Subject: [PATCH 5/7] shiny symbol detector --- .../PokemonFRLG_ShinySymbolDetector.cpp | 62 ++++++++++++ .../PokemonFRLG_ShinySymbolDetector.h | 35 +++++++ .../ShinyHunting/PokemonFRLG_StarterReset.cpp | 96 ++++++++++++++++--- .../ShinyHunting/PokemonFRLG_StarterReset.h | 1 + SerialPrograms/cmake/SourceFiles.cmake | 2 + 5 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.cpp new file mode 100644 index 0000000000..83ea0f023b --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.cpp @@ -0,0 +1,62 @@ +/* Shiny Symbol Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageRGB32.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/ImageFilter.h" +#include "PokemonFRLG_ShinySymbolDetector.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +ShinySymbolDetector::ShinySymbolDetector(Color color) + : m_box_symbol(0.441, 0.250, 0.030, 0.050) +{} +void ShinySymbolDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_box_symbol); +} +bool ShinySymbolDetector::read(Logger& logger, const ImageViewRGB32& frame){ + const bool replace_color_within_range = false; + + //Filter out background + ImageRGB32 filtered_region = filter_rgb32_range( + extract_box_reference(frame, m_box_symbol), + combine_rgb(193, 152, 0), combine_rgb(255, 255, 162), 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; + */ + + /* + Shiny: + R: 195.786, G: 182.143, B: 142.286 + */ + + if (stats.average.b + 20 < stats.average.r){ + return true; + } + return false; +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h new file mode 100644 index 0000000000..5a8cb3553b --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h @@ -0,0 +1,35 @@ +/* Shiny Symbol Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_ShinySymbolDetector_H +#define PokemonAutomation_PokemonFRLG_ShinySymbolDetector_H + +#include "Common/Cpp/Logging/AbstractLogger.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +// Shinies have a bright yellow star symbol in their summary +// additionally, the border in the screen is blue instead of purple +class ShinySymbolDetector{ +public: + ShinySymbolDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const; + bool read(Logger& logger, const ImageViewRGB32& frame); + +private: + ImageFloatBox m_box_symbol; +}; + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp index 9154b501e8..1b8de29679 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp @@ -16,6 +16,7 @@ #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h" +#include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" #include "PokemonFRLG/PokemonFRLG_Navigation.h" #include "PokemonFRLG_StarterReset.h" @@ -158,19 +159,10 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro context.wait_for_all_requests(); } - -void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - //StartProgramChecks::check_performance_class_wired_or_wireless(context); - +//After declining to nickname, clear rival pickup and open your starter's summary +void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ StarterReset_Descriptor::Stats& stats = env.current_stats(); - /* - * Settings: Text Speed fast. - * Setup: Stand in front of the starter you want. Save the game. - */ - - obtain_starter(env, context); - //From no to nickname to overworld StartMenuWatcher start_menu(COLOR_RED); @@ -192,12 +184,92 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "Unable to open Start menu.", + "open_summary(): Unable to open Start menu.", env.console ); } //No Pokedex yet, so 1 A press to open party menu + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + BlackScreenOverWatcher blk1(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret1 = wait_until( + env.console, context, + 5s, + {blk1} + ); + if (ret1 == 0){ + env.log("Entered party menu."); + }else{ + env.log("Timed out waiting to enter game.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "open_summary(): Unable to enter Party menu.", + env.console + ); + } + context.wait_for_all_requests(); + + //Two presses to open summary + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + BlackScreenOverWatcher blk2(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret2 = wait_until( + env.console, context, + 5s, + {blk2} + ); + if (ret2 == 0){ + env.log("Entered summary."); + }else{ + env.log("Timed out waiting to enter game.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "open_summary(): Unable to enter summary.", + env.console + ); + } + context.wait_for_all_requests(); + +} + + +void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + //StartProgramChecks::check_performance_class_wired_or_wireless(context); + + StarterReset_Descriptor::Stats& stats = env.current_stats(); + + /* + * Settings: Text Speed fast. + * Setup: Stand in front of the starter you want. Save the game. + */ + + obtain_starter(env, context); + open_summary(env, context); + + VideoSnapshot screen = env.console.video().snapshot(); + + bool shiny_starter = false; + ShinySymbolDetector shiny_checker(COLOR_YELLOW); + shiny_starter = shiny_checker.read(env.console.logger(), screen); + + if (shiny_starter) { + env.log("Shiny starter detected!"); + stats.shinystarter++; + send_program_status_notification(env, NOTIFICATION_SHINY_STARTER, "Shiny starter found!", screen, true); + } + else { + env.log("Starter is not shiny."); + env.log("Soft resetting."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Soft resetting." + ); + //soft_reset(env.program_info(), env.console, context); + stats.resets++; + } + /* bool shiny_starter = false; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h index ee99eaefd9..1f6bb3c801 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h @@ -34,6 +34,7 @@ class StarterReset : public SingleSwitchProgramInstance{ private: void obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context); GoHomeWhenDoneOption GO_HOME_WHEN_DONE; diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index dab42f4151..15f2c5230e 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1403,6 +1403,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.cpp Source/PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h + Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.cpp + Source/PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h Source/PokemonFRLG/PokemonFRLG_Navigation.cpp Source/PokemonFRLG/PokemonFRLG_Navigation.h Source/PokemonFRLG/PokemonFRLG_Panels.cpp From 5d63acb54159f4d314a0dc49250e80e7dcefd02c Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 16:44:22 -0500 Subject: [PATCH 6/7] soft reset, complete starter reset --- .../PokemonFRLG/PokemonFRLG_Navigation.cpp | 130 ++++++++++++++++++ .../PokemonFRLG/PokemonFRLG_Navigation.h | 33 +++++ .../PokemonFRLG/PokemonFRLG_Settings.cpp | 5 + .../Source/PokemonFRLG/PokemonFRLG_Settings.h | 1 + .../ShinyHunting/PokemonFRLG_StarterReset.cpp | 89 ++---------- 5 files changed, 177 insertions(+), 81 deletions(-) create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp create mode 100644 SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp new file mode 100644 index 0000000000..291125f728 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -0,0 +1,130 @@ +/* Pokemon FRLG Navigation + * + * From: https://github.com/PokemonAutomation/ + * + * Soft reset, menus, etc. + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.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_Settings.h" +#include "PokemonFRLG_Navigation.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +void soft_reset(const ProgramInfo& info, VideoStream& stream, ProControllerContext& context){ + // A + B + Select + Start + pbf_press_button(context, BUTTON_B | BUTTON_A | BUTTON_MINUS | BUTTON_PLUS, 360ms, 1440ms); + + pbf_mash_button(context, BUTTON_PLUS, GameSettings::instance().START_BUTTON_MASH0); + context.wait_for_all_requests(); + + pbf_press_button(context, BUTTON_A, 160ms, 320ms); + + //Wait for game to load in + BlackScreenOverWatcher detector(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret = wait_until( + stream, context, + GameSettings::instance().ENTER_GAME_WAIT0, + {detector} + ); + if (ret == 0){ + stream.log("Entered game!"); + }else{ + stream.log("Timed out waiting to enter game.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "soft_reset(): Timed out waiting to enter game.", + stream + ); + } + + //Mash past "previously on..." + pbf_mash_button(context, BUTTON_B, GameSettings::instance().ENTER_GAME_MASH0); + + context.wait_for_all_requests(); +} + +/* +bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool send_out_lead) { + float shiny_coefficient = 1.0; + ShinySoundDetector shiny_detector(stream.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, + [&](ProControllerContext& context) { + int ret = wait_until( + stream, context, + std::chrono::seconds(30), + {{legendary_appeared}} + ); + if (ret == 0) { + stream.log("Advance arrow detected."); + } else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "handle_encounter(): Did not detect battle start.", + stream + ); + } + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); + }, + {{shiny_detector}} + ); + shiny_detector.throw_if_no_sound(); + if (res == 0){ + stream.log("Shiny detected!"); + return true; + } + stream.log("Shiny not found."); + + if (send_out_lead) { + //Send out lead, no shiny detection needed. + BattleMenuWatcher battle_menu(COLOR_RED); + stream.log("Sending out lead Pokemon."); + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + + int ret = wait_until( + stream, context, + std::chrono::seconds(15), + { {battle_menu} } + ); + if (ret == 0) { + stream.log("Battle menu detecteed!"); + } + else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "handle_encounter(): Did not detect battle menu.", + stream + ); + } + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); + } + + return false; +} +*/ + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h new file mode 100644 index 0000000000..832b31629e --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h @@ -0,0 +1,33 @@ +/* Pokemon FRLG Navigation + * + * From: https://github.com/PokemonAutomation/ + * + * Soft reset, menus, etc. + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_Navigation_H +#define PokemonAutomation_PokemonFRLG_Navigation_H + +#include "CommonFramework/Tools/VideoStream.h" +#include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" + +namespace PokemonAutomation{ + struct ProgramInfo; +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +// Press A+B+Select+Start at the same time to soft reset, then re-enters the game. +// For now this assumes no dry battery. +void soft_reset(const ProgramInfo& info, VideoStream& stream, 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); + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp index 245092a513..22c975aaed 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.cpp @@ -33,6 +33,11 @@ GameSettings::GameSettings() LockMode::LOCK_WHILE_RUNNING, "3000 ms" ) + , ENTER_GAME_MASH0( + "Enter Game Mash:
Mash B for this long after loading a save to bypass the \"Previously On Your Quest...\" screens.", + LockMode::LOCK_WHILE_RUNNING, + "3000 ms" + ) , m_shiny_audio_settings("Shiny Audio Settings:") , SHINY_SOUND_THRESHOLD( "Shiny Sound Threshold:
Maximum error coefficient to trigger a shiny detection.", diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h index d649a3d0a8..1349506358 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Settings.h @@ -25,6 +25,7 @@ class GameSettings : public BatchOption{ SectionDividerOption m_soft_reset_timings; MillisecondsOption START_BUTTON_MASH0; MillisecondsOption ENTER_GAME_WAIT0; + MillisecondsOption ENTER_GAME_MASH0; SectionDividerOption m_shiny_audio_settings; FloatingPointOption SHINY_SOUND_THRESHOLD; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp index 1b8de29679..f9877b0f54 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp @@ -230,6 +230,7 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll env.console ); } + pbf_wait(context, 1000ms); context.wait_for_all_requests(); } @@ -241,101 +242,27 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon StarterReset_Descriptor::Stats& stats = env.current_stats(); /* - * Settings: Text Speed fast. + * Settings: Text Speed fast. Default borders. * Setup: Stand in front of the starter you want. Save the game. */ - obtain_starter(env, context); - open_summary(env, context); - - VideoSnapshot screen = env.console.video().snapshot(); - bool shiny_starter = false; - ShinySymbolDetector shiny_checker(COLOR_YELLOW); - shiny_starter = shiny_checker.read(env.console.logger(), screen); - - if (shiny_starter) { - env.log("Shiny starter detected!"); - stats.shinystarter++; - send_program_status_notification(env, NOTIFICATION_SHINY_STARTER, "Shiny starter found!", screen, true); - } - else { - env.log("Starter is not shiny."); - env.log("Soft resetting."); - send_program_status_notification( - env, NOTIFICATION_STATUS_UPDATE, - "Soft resetting." - ); - //soft_reset(env.program_info(), env.console, context); - stats.resets++; - } - - /* - bool shiny_starter = false; while (!shiny_starter) { - env.log("Selecting starter."); - pbf_press_button(context, BUTTON_A, 320ms, 1440ms); - context.wait_for_all_requests(); - - env.log("Starting battle."); - - //Now mash B until the battle menu appears - BattleMenuWatcher battle_menu(COLOR_RED); - int ret = run_until( - env.console, context, - [](ProControllerContext& context){ - pbf_mash_button(context, BUTTON_B, 8000ms); - }, - {battle_menu} - ); - context.wait_for_all_requests(); - if (ret != 0){ - env.console.log("Failed to detect battle menu.", COLOR_RED); - } - else { - env.log("Battle menu detected."); - } - - //Open the summary and check the color of the number - 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}); - int ret2 = wait_until( - env.console, context, - std::chrono::milliseconds(3000), - {{detector}} - ); - if (ret2 == 0){ - env.log("Entered party menu."); - }else{ - env.log("Timed out waiting to enter party menu.", COLOR_RED); - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "StarterReset: Timed out waiting to enter party menu.", - env.console - ); - } - - pbf_press_button(context, BUTTON_A, 160ms, 1440ms); - pbf_press_dpad(context, DPAD_DOWN, 320ms, 640ms); - pbf_press_button(context, BUTTON_A, 320ms, 640ms); - - pbf_wait(context, 1000ms); - context.wait_for_all_requests(); + obtain_starter(env, context); + open_summary(env, context); VideoSnapshot screen = env.console.video().snapshot(); - ShinyNumberDetector shiny_checker(COLOR_YELLOW); + ShinySymbolDetector shiny_checker(COLOR_YELLOW); shiny_starter = shiny_checker.read(env.console.logger(), screen); if (shiny_starter) { env.log("Shiny starter detected!"); stats.shinystarter++; send_program_status_notification(env, NOTIFICATION_SHINY_STARTER, "Shiny starter found!", screen, true); - } - else { + break; + } else { env.log("Starter is not shiny."); env.log("Soft resetting."); send_program_status_notification( @@ -344,9 +271,9 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon ); soft_reset(env.program_info(), env.console, context); stats.resets++; + context.wait_for_all_requests(); } } - */ if (GO_HOME_WHEN_DONE) { pbf_press_button(context, BUTTON_HOME, 200ms, 1000ms); From a336968861fe128233af8861c05e1028ca5fa2bf Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 20 Feb 2026 16:54:28 -0500 Subject: [PATCH 7/7] Update PokemonFRLG_StarterReset.cpp --- .../Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp index f9877b0f54..e3c1d32c41 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp @@ -260,7 +260,7 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon if (shiny_starter) { env.log("Shiny starter detected!"); stats.shinystarter++; - send_program_status_notification(env, NOTIFICATION_SHINY_STARTER, "Shiny starter found!", screen, true); + send_program_notification(env, NOTIFICATION_SHINY_STARTER, COLOR_YELLOW, "Shiny starter found!", {}, "", screen, true); break; } else { env.log("Starter is not shiny.");