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