diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.cpp new file mode 100644 index 0000000000..4b9e2f191d --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.cpp @@ -0,0 +1,55 @@ +/* Party Empty Slot Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "Common/Cpp/Exceptions.h" +#include "CommonTools/Images/SolidColorTest.h" +#include "PokemonFRLG/PokemonFRLG_Settings.h" +#include "PokemonFRLG_PartyEmptySlotDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +ImageFloatBox PartyEmptySlotDetector::box_for_slot(PartySlot slot){ + switch (slot){ + case PartySlot::ONE: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid FRLG Party Empty Slot. Slot 1 should always be occupied."); + case PartySlot::TWO: + return ImageFloatBox(0.709, 0.078, 0.01, 0.018); + case PartySlot::THREE: + return ImageFloatBox(0.709, 0.228, 0.01, 0.018); + case PartySlot::FOUR: + return ImageFloatBox(0.709, 0.378, 0.01, 0.018); + case PartySlot::FIVE: + return ImageFloatBox(0.709, 0.527, 0.01, 0.018); + case PartySlot::SIX: + return ImageFloatBox(0.709, 0.677, 0.01, 0.018); + default: + break; + } + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid FRLG Party Empty Slot."); +} + +PartyEmptySlotDetector::PartyEmptySlotDetector( + Color color, + PartySlot slot +) + : m_color(color) + , m_box(box_for_slot(slot)) +{} +void PartyEmptySlotDetector::make_overlays(VideoOverlaySet& items) const{ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + items.add(m_color, GAME_BOX.inner_to_outer(m_box)); +} +bool PartyEmptySlotDetector::detect(const ImageViewRGB32& screen){ + const BoxOption& GAME_BOX = GameSettings::instance().GAME_BOX; + ImageViewRGB32 game_screen = extract_box_reference(screen, GAME_BOX); + const auto stats = image_stats(extract_box_reference(game_screen, m_box)); + return stats.stddev.sum() > 20.0; +} +} +} +} \ No newline at end of file diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.h new file mode 100644 index 0000000000..765801d94b --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.h @@ -0,0 +1,38 @@ +/* Party Empty Slot Detector + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_PartyEmptySlotDetector_H +#define PokemonAutomation_PokemonFRLG_PartyEmptySlotDetector_H + +#include +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/VisualDetector.h" +#include "PokemonFRLG_PartySlot.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class PartyEmptySlotDetector : public StaticScreenDetector{ +public: + PartyEmptySlotDetector( + Color color, + PartySlot slot + ); + static ImageFloatBox box_for_slot(PartySlot slot); + virtual void make_overlays(VideoOverlaySet& items) const override; + // This is not const so that detectors can save/cache state. + virtual bool detect(const ImageViewRGB32& screen) override; + +private: + Color m_color; + ImageFloatBox m_box; +}; + +} +} +} +#endif \ No newline at end of file diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.cpp b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.cpp index 7f944b0f74..957b6c2d5b 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.cpp @@ -37,19 +37,19 @@ class PartyHeldItemMatcher : public ImageMatch::WaterfillTemplateMatcher{ } }; -ImageFloatBox PartyHeldItemDetector::box_for_slot(PartyHeldItemSlot slot){ +ImageFloatBox PartyHeldItemDetector::box_for_slot(PartySlot slot){ switch (slot){ - case PartyHeldItemSlot::SLOT_1: + case PartySlot::ONE: return ImageFloatBox(0.069, 0.285, 0.025, 0.050); - case PartyHeldItemSlot::SLOT_2: + case PartySlot::TWO: return ImageFloatBox(0.432, 0.150, 0.025, 0.050); - case PartyHeldItemSlot::SLOT_3: + case PartySlot::THREE: return ImageFloatBox(0.432, 0.3, 0.025, 0.050); - case PartyHeldItemSlot::SLOT_4: + case PartySlot::FOUR: return ImageFloatBox(0.432, 0.45, 0.025, 0.050); - case PartyHeldItemSlot::SLOT_5: + case PartySlot::FIVE: return ImageFloatBox(0.432, 0.6, 0.025, 0.050); - case PartyHeldItemSlot::SLOT_6: + case PartySlot::SIX: return ImageFloatBox(0.432, 0.725, 0.025, 0.050); default: break; @@ -60,7 +60,7 @@ ImageFloatBox PartyHeldItemDetector::box_for_slot(PartyHeldItemSlot slot){ PartyHeldItemDetector::PartyHeldItemDetector( Color color, VideoOverlay* overlay, - PartyHeldItemSlot slot + PartySlot slot ) : m_color(color) , m_overlay(overlay) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.h index 7dae06b1c5..6113a8f5d4 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.h @@ -7,23 +7,15 @@ #ifndef PokemonAutomation_PokemonFRLG_PartyHeldItemDetector_H #define PokemonAutomation_PokemonFRLG_PartyHeldItemDetector_H +#include #include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" #include "CommonTools/VisualDetector.h" +#include "PokemonFRLG_PartySlot.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonFRLG{ - -enum class PartyHeldItemSlot{ - SLOT_1, - SLOT_2, - SLOT_3, - SLOT_4, - SLOT_5, - SLOT_6 -}; - class PartyHeldItemDetector : public StaticScreenDetector{ public: PartyHeldItemDetector( @@ -34,10 +26,10 @@ class PartyHeldItemDetector : public StaticScreenDetector{ PartyHeldItemDetector( Color color, VideoOverlay* overlay, - PartyHeldItemSlot slot + PartySlot slot ); - static ImageFloatBox box_for_slot(PartyHeldItemSlot slot); + static ImageFloatBox box_for_slot(PartySlot slot); const ImageFloatBox& last_detected() const { return m_last_detected; } @@ -69,7 +61,7 @@ class PartyHeldItemWatcher : public DetectorToFinder{ PartyHeldItemWatcher( Color color, VideoOverlay* overlay, - PartyHeldItemSlot slot, + PartySlot slot, std::chrono::milliseconds hold_duration = std::chrono::milliseconds(250) ) : DetectorToFinder("PartyHeldItemWatcher", hold_duration, color, overlay, box_for_slot(slot)) diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h index 61dfb64d33..ee17121f4a 100644 --- a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h @@ -14,6 +14,7 @@ #include "CommonTools/VisualDetector.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" #include "PokemonFRLG/PokemonFRLG_Settings.h" +#include "PokemonFRLG_PartySlot.h" namespace PokemonAutomation{ class CancellableScope; @@ -21,16 +22,6 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonFRLG{ -enum class PartySlot{ - ONE, - TWO, - THREE, - FOUR, - FIVE, - SIX - //CXL -}; - // The Party menu has a white box on the bottom // The background around the edges is dark teal/navy class PartyMenuDetector : public StaticScreenDetector{ diff --git a/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartySlot.h b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartySlot.h new file mode 100644 index 0000000000..1cd50e2400 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartySlot.h @@ -0,0 +1,27 @@ +/* Party Slot + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_PartySlot_H +#define PokemonAutomation_PokemonFRLG_PartySlot_H + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +enum class PartySlot{ + ONE, + TWO, + THREE, + FOUR, + FIVE, + SIX + //CXL +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp index ff017e0744..4282fb9efe 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.cpp @@ -6,6 +6,7 @@ * */ +#include #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Random.h" @@ -24,11 +25,12 @@ #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_BattleDialogs.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_PartyDialogs.h" #include "PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_BagDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_LoadMenuDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_SummaryDetector.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h" -#include "PokemonFRLG/Inference/Menus/PokemonFRLG_BagDetector.h" #include "PokemonFRLG/Inference/Map/PokemonFRLG_MapDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_BattlePokemonDetector.h" #include "PokemonFRLG/Programs/PokemonFRLG_StartMenuNavigation.h" @@ -755,6 +757,26 @@ void open_party_menu_from_overworld(ConsoleHandle& console, ProControllerContext } } +PartySlot detect_last_occupied_party_slot(ConsoleHandle& console){ + const auto snapshot = console.video().snapshot(); + constexpr std::array slots{ + PartySlot::SIX, + PartySlot::FIVE, + PartySlot::FOUR, + PartySlot::THREE, + PartySlot::TWO, + }; + + for (PartySlot slot : slots){ + PartyEmptySlotDetector empty_slot_detector(COLOR_RED, slot); + if (!empty_slot_detector.detect(snapshot)){ + return slot; + } + } + + return PartySlot::ONE; +} + void open_bag_from_overworld(ConsoleHandle& console, ProControllerContext& context, StartMenuContext menu_context){ uint16_t errors = 0; bool start_menu_is_open = false; diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h index b5a3ac3177..fbeedc9479 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Navigation.h @@ -11,6 +11,7 @@ #include "CommonFramework/Tools/VideoStream.h" #include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" +#include "PokemonFRLG/Inference/Menus/PokemonFRLG_PartySlot.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -80,6 +81,9 @@ enum class StartMenuContext { }; void open_party_menu_from_overworld(ConsoleHandle& console, ProControllerContext& context, StartMenuContext menu_context = StartMenuContext::STANDARD); +// Starting from the party menu, detect the last occupied party slot +PartySlot detect_last_occupied_party_slot(ConsoleHandle& console); + // Starting from the start menu, a sub-screen of the start menu, or the overworld, navigate to the bag void open_bag_from_overworld(ConsoleHandle& console, ProControllerContext& context, StartMenuContext menu_context = StartMenuContext::STANDARD); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/Farming/PokemonFRLG_ItemDuplication.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/Farming/PokemonFRLG_ItemDuplication.cpp index 14d3a6f42f..970c423420 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/Farming/PokemonFRLG_ItemDuplication.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/Farming/PokemonFRLG_ItemDuplication.cpp @@ -86,7 +86,7 @@ void ItemDuplication::program(SingleSwitchProgramEnvironment& env, ProController ImageFloatBox mail_arrow_box{ 0.3670769231, 0.06373076639, 0.028, 0.077 }; ImageFloatBox mail_confirmation_arrow_box{ 0.7289230769, 0.4604230588, 0.029, 0.076 }; SelectionArrowWatcher mail_selection_arrow(COLOR_BLUE, &env.console.overlay(), mail_arrow_box); - PartyHeldItemWatcher farfetchd_held_item(COLOR_BLUE, &env.console.overlay(), PartyHeldItemSlot::SLOT_1); + PartyHeldItemWatcher farfetchd_held_item(COLOR_BLUE, &env.console.overlay(), PartySlot::ONE); AdvanceWhiteDialogWatcher item_description(COLOR_BLUE); SelectionArrowWatcher confirmation_arrow(COLOR_BLUE, &env.console.overlay(), SelectionArrowPositionConfirmationMenu::YES); SelectionArrowWatcher mail_confirmation_arrow(COLOR_BLUE, &env.console.overlay(), mail_confirmation_arrow_box); diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 2924fe9269..95e9cc92d5 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1472,10 +1472,13 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Inference/Menus/PokemonFRLG_BagDetector.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_LoadMenuDetector.cpp Source/PokemonFRLG/Inference/Menus/PokemonFRLG_LoadMenuDetector.h + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.cpp + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyEmptySlotDetector.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.cpp Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyHeldItemDetector.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.cpp Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartyMenuDetector.h + Source/PokemonFRLG/Inference/Menus/PokemonFRLG_PartySlot.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.cpp Source/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h Source/PokemonFRLG/Inference/Menus/PokemonFRLG_DexRegistrationDetector.cpp