diff --git a/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.cpp b/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.cpp index 2baf0f1ff6..a1295d13db 100644 --- a/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.cpp @@ -211,15 +211,15 @@ bool NormalDialogDetector::process_frame(const ImageViewRGB32& frame, WallClock FlatWhiteDialogDetector::FlatWhiteDialogDetector(Color color, VideoOverlay* overlay) : m_color(color) , m_overlay(overlay) - , m_top(0.267838, 0.785156, 0.467618, 0.019531) + , m_bottom(0.265, 0.930, 0.465, 0.020) , m_arrow_box(0.727, 0.868, 0.037, 0.086) {} void FlatWhiteDialogDetector::make_overlays(VideoOverlaySet& items) const{ - items.add(m_color, m_top); + items.add(m_color, m_bottom); items.add(m_color, m_arrow_box); } bool FlatWhiteDialogDetector::detect(const ImageViewRGB32& screen){ - if (!is_white(extract_box_reference(screen, m_top), 500.0, 20.0)){ + if (!is_white(extract_box_reference(screen, m_bottom), 500.0, 20.0)){ m_last_detected_box.reset(); return false; } diff --git a/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.h b/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.h index 597b6b46bb..88f239f656 100644 --- a/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.h +++ b/SerialPrograms/Source/PokemonLZA/Inference/PokemonLZA_DialogDetector.h @@ -62,7 +62,7 @@ class FlatWhiteDialogDetector : public StaticScreenDetector{ const Color m_color; VideoOverlay* m_overlay; - const ImageFloatBox m_top; + const ImageFloatBox m_bottom; const ImageFloatBox m_arrow_box; ImageFloatBox m_last_detected; diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index 994b5589f6..f1fd42bdae 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -12,6 +12,7 @@ // General #include "Programs/PokemonLZA_ClothingBuyer.h" +#include "Programs/PokemonLZA_BerryBuyer.h" // Farming #include "Programs/Farming/PokemonLZA_RestaurantFarmer.h" @@ -47,6 +48,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- General ----"); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Farming ----"); ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.cpp new file mode 100644 index 0000000000..c4d68a9744 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.cpp @@ -0,0 +1,188 @@ +/* Berry Buyer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/StartupChecks/VideoResolutionCheck.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" +#include "PokemonLZA_BerryBuyer.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + +using namespace Pokemon; + +BerryBuyer_Descriptor::BerryBuyer_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonLZA:BerryBuyer", + STRING_POKEMON + " LZA", "Berry Buyer", + "Programs/PokemonLZA/BerryBuyer.html", + "Buy EV reducing berries from stall.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +class BerryBuyer_Descriptor::Stats : public StatsTracker{ +public: + Stats() + : purchases(m_stats["Purchases"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Purchases"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + + std::atomic& purchases; + std::atomic& errors; +}; +std::unique_ptr BerryBuyer_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + + +BerryBuyer::BerryBuyer() + : BERRY_TYPE( + "Berry to Purchase:", + { + {BerryType::POMEG, "pomeg", "Pomeg (HP)"}, + {BerryType::KELPSY, "kelpsy", "Kelpsy (Attack)"}, + {BerryType::QUALOT, "qualot", "Qualot (Defence)"}, + {BerryType::HONDEW, "hondew", "Hondew (Special Attack)"}, + {BerryType::GREPA, "grepa", "Grepa (Special Defence)"}, + {BerryType::TAMATO, "tamato", "Tamato (Speed)"}, + }, + LockMode::LOCK_WHILE_RUNNING, + BerryType::POMEG + ) + , NUM_PURCHASE( + "Number to Purchase:
The number of berries you want to purchase.", + LockMode::LOCK_WHILE_RUNNING, + 100, 1, 999 + ) + , GO_HOME_WHEN_DONE(false) + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_FATAL, + }) +{ + PA_ADD_OPTION(BERRY_TYPE); + PA_ADD_OPTION(NUM_PURCHASE); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void BerryBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + BerryBuyer_Descriptor::Stats& stats = env.current_stats(); + assert_16_9_720p_min(env.logger(), env.console); + while (true) { + context.wait_for_all_requests(); + + ButtonWatcher buttonA( + COLOR_RED, + ButtonType::ButtonA, + {0.1, 0.1, 0.8, 0.8}, + &env.console.overlay() + ); + SelectionArrowWatcher select( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + {0.715, 0.235, 0.045, 0.080} + ); + SelectionArrowWatcher confirm( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + {0.715, 0.600, 0.045, 0.080} + ); + FlatWhiteDialogWatcher dialog(COLOR_RED, &env.console.overlay()); + + int ret = wait_until( + env.console, context, + 30000ms, + { + buttonA, + select, + confirm, + dialog, + } + ); + context.wait_for(100ms); + + switch (ret){ + case 0: + env.log("Detected A button."); + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + continue; + + case 1: + env.log("Detected berry selection screen."); + switch(BERRY_TYPE){ + case BerryType::HONDEW: + pbf_press_dpad(context, DPAD_DOWN, 160ms, 80ms); + case BerryType::QUALOT: + pbf_press_dpad(context, DPAD_DOWN, 160ms, 80ms); + case BerryType::KELPSY: + pbf_press_dpad(context, DPAD_DOWN, 160ms, 80ms); + case BerryType::POMEG: + break; + case BerryType::GREPA: + pbf_press_dpad(context, DPAD_UP, 160ms, 80ms); + case BerryType::TAMATO: + pbf_press_dpad(context, DPAD_UP, 160ms, 80ms); + pbf_press_dpad(context, DPAD_UP, 160ms, 80ms); + break; + } + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + continue; + + case 2: + env.log("Detected purchase confirm screen."); + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + stats.purchases++; + env.update_stats(); + + if (stats.purchases == NUM_PURCHASE) { + // pbf_mash_button(context, BUTTON_B, 5000ms); + // intentionally don't leave the purchase menu to not get attacked + GO_HOME_WHEN_DONE.run_end_of_program(context); + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); + return; + } + + continue; + + case 3: + env.log("Detected white dialog."); + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + continue; + + default: + stats.errors++; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "No recognized state after 30 seconds.", + env.console + ); + } + } +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.h new file mode 100644 index 0000000000..f1f3940032 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.h @@ -0,0 +1,56 @@ +/* Berry Buyer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonLZA_BerryBuyer_H +#define PokemonAutomation_PokemonLZA_BerryBuyer_H + +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" +#include "Common/Cpp/Options/SimpleIntegerOption.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + +class BerryBuyer_Descriptor : public SingleSwitchProgramDescriptor{ +public: + BerryBuyer_Descriptor(); + + class Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class BerryBuyer : public SingleSwitchProgramInstance{ +public: + BerryBuyer(); + + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + enum class BerryType{ + POMEG, + KELPSY, + QUALOT, + HONDEW, + GREPA, + TAMATO, + }; + EnumDropdownOption BERRY_TYPE; + SimpleIntegerOption NUM_PURCHASE; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + + + + + +} +} +} +#endif diff --git a/SerialPrograms/SourceFiles.cmake b/SerialPrograms/SourceFiles.cmake index d2ec7ea843..0f659c44c7 100644 --- a/SerialPrograms/SourceFiles.cmake +++ b/SerialPrograms/SourceFiles.cmake @@ -1567,6 +1567,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonLZA/Programs/Farming/PokemonLZA_RestaurantFarmer.h Source/PokemonLZA/Programs/PokemonLZA_BasicNavigation.cpp Source/PokemonLZA/Programs/PokemonLZA_BasicNavigation.h + Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.cpp + Source/PokemonLZA/Programs/PokemonLZA_BerryBuyer.h Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp