From dbaa34f730101ccbaf776a5f7ba5032a7120c9c8 Mon Sep 17 00:00:00 2001 From: Dalton-V Date: Sun, 9 Nov 2025 17:10:42 -0600 Subject: [PATCH 1/3] Create Freindship Farmer program for ZA --- .../Source/PokemonLZA/PokemonLZA_Panels.cpp | 2 + .../Farming/PokemonLZA_FreindshipFarmer.cpp | 462 ++++++++++++++++++ .../Farming/PokemonLZA_FreindshipFarmer.h | 70 +++ SerialPrograms/SourceFiles.cmake | 2 + 4 files changed, 536 insertions(+) create mode 100644 SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp create mode 100644 SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index b32b7bd5a7..1c3940ff55 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -18,6 +18,7 @@ #include "Programs/Trading/PokemonLZA_SelfBoxTrade.h" // Farming +#include "Programs/Farming/PokemonLZA_FreindshipFarmer.h" #include "Programs/Farming/PokemonLZA_RestaurantFarmer.h" #include "Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.h" #include "Programs/Farming/PokemonLZA_MegaShardFarmer.h" @@ -62,6 +63,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back(make_single_switch_program()); if (IS_BETA_VERSION){ ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); } ret.emplace_back("---- Shiny Hunting ----"); diff --git a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp new file mode 100644 index 0000000000..bb0b99689e --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp @@ -0,0 +1,462 @@ +/* Friendship Farmer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "PokemonLZA_FreindshipFarmer.h" + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/Exceptions/ScreenshotException.h" +#include "CommonFramework/Logging/Logger.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + +FriendshipFarmer_Descriptor::FriendshipFarmer_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonLZA:FriendshipFarmer", + Pokemon::STRING_POKEMON + " LZA", "Friendship Farmer", + "Programs/PokemonLZA/Farming/FriendshipFarmer.html", + "Farm friendship for pokemon in your party using either the cafe or bench method.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{ +} + +class FriendshipFarmer_Descriptor::Stats : public StatsTracker{ +public: + Stats() + : drinks_shared(m_stats["Drinks Shared"]) + , hang_out_sessions(m_stats["Hang Out Sessions"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Drinks Shared"); + m_display_order.emplace_back("Hang Out Sessions"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + + std::atomic& drinks_shared; + std::atomic& hang_out_sessions; + std::atomic& errors; +}; + +std::unique_ptr FriendshipFarmer_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +FriendshipFarmer::~FriendshipFarmer(){ + FARMING_OPTION.remove_listener(*this); +} + +FriendshipFarmer::FriendshipFarmer() + : FARMING_OPTION( + "Farming Method", + { + { FarmingOption::Cafe, "Cafe", "Share drinks at a Cafe" }, + { FarmingOption::Bench, "Bench", "Hangout on a bench" } + }, + LockMode::LOCK_WHILE_RUNNING, + FarmingOption::Cafe + ) + , FRIENDSHIP_AMOUNT( + "Friendship Amount", + { + { FriendshipAmount::Evolve, "Evolve", "Increase friendship until pokemon can evolve." }, + { FriendshipAmount::Max, "Max", "Increase friendship to maximum." } + }, + LockMode::LOCK_WHILE_RUNNING, + FriendshipAmount::Evolve + ) + , NUM_PARTY_MEMBERS( + "Number of Party Members
Only used for Cafe Method", + LockMode::LOCK_WHILE_RUNNING, + 6, 1, 6 + ) + , GO_HOME_WHEN_DONE(false) + , NOTIFICATION_STATUS_OPTION("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_STATUS_OPTION, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_FATAL, + }) +{ + PA_ADD_OPTION(FARMING_OPTION); + PA_ADD_OPTION(FRIENDSHIP_AMOUNT); + PA_ADD_OPTION(NUM_PARTY_MEMBERS); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); + + FriendshipFarmer::on_config_value_changed(this); + + FARMING_OPTION.add_listener(*this); +} + +void FriendshipFarmer::on_config_value_changed(void* object) { + if (FARMING_OPTION == FarmingOption::Cafe) { + NUM_PARTY_MEMBERS.set_visibility(ConfigOptionState::ENABLED); + } + else { + NUM_PARTY_MEMBERS.set_visibility(ConfigOptionState::HIDDEN); + } +} + +void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + + while (true) + { + ButtonWatcher buttonA_watcher( + COLOR_WHITE, + ButtonType::ButtonA, + { 0.1, 0.1, 0.8, 0.8 }, + &env.console.overlay() + ); + ButtonWatcher buttonB_watcher( + COLOR_WHITE, + ButtonType::ButtonB, + { 0.911, 0.939, 0.019, 0.033 }, + &env.console.overlay() + ); + SelectionArrowWatcher selection_arrow_watcher( + COLOR_GREEN, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.6300, 0.4440, 0.2260, 0.3190 } + ); + FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); + BlueDialogWatcher blue_dialog_watcher(COLOR_BLUE, &env.console.overlay()); + + int ret = wait_until( + env.console, context, + 10000ms, + { + buttonA_watcher, + buttonB_watcher, + selection_arrow_watcher, + white_dialog_watcher, + blue_dialog_watcher, + } + ); + context.wait_for(100ms); + + switch (ret) { + case 0: + env.log("Detected A button."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 1: + env.log("Detected B button."); + pbf_press_button(context, BUTTON_B, 80ms, 40ms); + return; + case 2: + env.log("Detected selection arrow."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 3: + env.log("Detected white dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 4: + env.log("Detected blue dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + default: + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "enter_cafe(): No recognized state after 60 seconds.", + env.console + ); + } + } +} + +void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + + int reset_attempt = 0; + bool seen_selection_arrow = false; + + while (true) + { + ButtonWatcher buttonA_watcher( + COLOR_WHITE, + ButtonType::ButtonA, + { 0.1, 0.1, 0.8, 0.8 }, + &env.console.overlay() + ); + SelectionArrowWatcher selection_arrow_watcher( + COLOR_GREEN, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.6300, 0.4440, 0.2260, 0.3190 } + ); + FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); + BlueDialogWatcher blue_dialog_watcher(COLOR_RED, &env.console.overlay()); + + int ret = wait_until( + env.console, context, + 10000ms, + { + buttonA_watcher, + selection_arrow_watcher, + white_dialog_watcher, + blue_dialog_watcher, + } + ); + context.wait_for(100ms); + + switch (ret) { + case 0: + env.log("Detected A button."); + return; + case 1: + env.log("Detected selection arrow."); + seen_selection_arrow = true; + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 3: + env.log("Detected white dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 4: + env.log("Detected blue dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + default: + if (seen_selection_arrow && ++reset_attempt <= 10) { + env.log("Attempting to face the bench."); + pbf_move_left_joystick(context, 128, 0, 250ms, 0ms); + continue; + } + + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "exit_bench(): No recognized state after 60 seconds.", + env.console + ); + } + } +} + +void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + + int reset_attempt = 0; + bool seen_selection_arrow = false; + + while (true) + { + ButtonWatcher buttonA_watcher( + COLOR_WHITE, + ButtonType::ButtonA, + { 0.1, 0.1, 0.8, 0.8 }, + &env.console.overlay() + ); + SelectionArrowWatcher selection_arrow_watcher( + COLOR_GREEN, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.6300, 0.4440, 0.2260, 0.3190 } + ); + FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); + BlueDialogWatcher blue_dialog_watcher(COLOR_RED, &env.console.overlay()); + + int ret = wait_until( + env.console, context, + 10000ms, + { + buttonA_watcher, + selection_arrow_watcher, + white_dialog_watcher, + blue_dialog_watcher, + } + ); + context.wait_for(100ms); + + switch (ret) { + case 0: + env.log("Detected A button."); + return; + case 1: + env.log("Detected selection arrow."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 3: + env.log("Detected white dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 4: + env.log("Detected blue dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + default: + if (seen_selection_arrow && ++reset_attempt <= 10) { + env.log("No recognized state, possible day/night change. Waiting.."); + context.wait_for(1000ms); + continue; + } + + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "exit_cafe(): No recognized state after 60 seconds.", + env.console + ); + } + } +} + +void FriendshipFarmer::hang_out_bench(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + + while (true) + { + ButtonWatcher buttonA_watcher( + COLOR_WHITE, + ButtonType::ButtonA, + { 0.1, 0.1, 0.8, 0.8 }, + &env.console.overlay() + ); + ButtonWatcher buttonB_watcher( + COLOR_WHITE, + ButtonType::ButtonB, + { 0.902, 0.937, 0.022, 0.038 }, + &env.console.overlay() + ); + SelectionArrowWatcher selection_arrow_watcher( + COLOR_GREEN, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.6300, 0.4440, 0.2260, 0.3190 } + ); + FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); + BlueDialogWatcher blue_dialog_watcher(COLOR_BLUE, &env.console.overlay()); + + int ret = wait_until( + env.console, context, + 10000ms, + { + buttonA_watcher, + buttonB_watcher, + selection_arrow_watcher, + white_dialog_watcher, + blue_dialog_watcher, + } + ); + context.wait_for(100ms); + + switch (ret) { + case 0: + env.log("Detected A button."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 1: + env.log("Detected B button. Waiting 10 Seconds."); + context.wait_for(10000ms); + pbf_press_button(context, BUTTON_B, 80ms, 40ms); + return; + case 2: + env.log("Detected selection arrow."); + pbf_press_dpad(context, DPAD_DOWN, 40ms, 40ms); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 3: + env.log("Detected white dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + case 4: + env.log("Detected blue dialog."); + pbf_press_button(context, BUTTON_A, 80ms, 40ms); + continue; + default: + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "hang_out_bench(): No recognized state after 60 seconds.", + env.console + ); + } + } +} + +void FriendshipFarmer::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + + if (FARMING_OPTION.get() == FarmingOption::Cafe){ + env.log("Cafe Method Started"); + + for (int i = 0; i < NUM_PARTY_MEMBERS; ++i) + { + int drinks_needed; + + //Each drink at cafe gives 30 friendship points. + //Evolve = 160 points = 5.33 drinks + //Max = 255 points = 8.5 drinks + if (FRIENDSHIP_AMOUNT.get() == FriendshipAmount::Evolve) { + drinks_needed = 6; + } + else { + drinks_needed = 9; + } + + for (int d = 0; d < drinks_needed; ++d) { + enter_cafe(env, context); + exit_cafe(env, context); + + ++stats.drinks_shared; + env.update_stats(); + } + + env.log("Switching to next Party Member"); + pbf_press_dpad(context, DPAD_RIGHT, 40ms, 40ms); + context.wait_for(100ms); + } + + env.log("Cafe Method Finished"); + } + else{ + env.log("Bench Method Started"); + int hang_outs_needed; + + //Each hangout on bench gives 10 friendship points. + // Evolve = 160 points = 16 hangouts + // Max = 255 points = 26 hangouts + if (FRIENDSHIP_AMOUNT.get() == FriendshipAmount::Evolve){ + hang_outs_needed = 16; + } + else{ + hang_outs_needed = 26; + } + + for (int i = 0; i < hang_outs_needed; ++i) + { + hang_out_bench(env, context); + exit_bench(env, context); + + ++stats.hang_out_sessions; + env.update_stats(); + } + } + + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); + GO_HOME_WHEN_DONE.run_end_of_program(context); +} + +} +} +} diff --git a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h new file mode 100644 index 0000000000..bb1c2d48eb --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h @@ -0,0 +1,70 @@ +/* Friendship Farmer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonLZA_FriendshipFarmer_H +#define PokemonAutomation_PokemonLZA_FriendshipFarmer_H + + +#include +#include "Common/Cpp/Options/SimpleIntegerOption.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" + +namespace PokemonAutomation{ + +template class ControllerContext; + +namespace NintendoSwitch{ + +class ProController; +using ProControllerContext = ControllerContext; + +namespace PokemonLZA { + +class FriendshipFarmer_Descriptor : public SingleSwitchProgramDescriptor { +public: + FriendshipFarmer_Descriptor(); + + class Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class FriendshipFarmer : public SingleSwitchProgramInstance, public ConfigOption::Listener{ +public: + ~FriendshipFarmer(); + FriendshipFarmer(); + + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + void enter_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void exit_bench(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void exit_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void hang_out_bench(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + + virtual void on_config_value_changed(void* object) override; + + enum class FarmingOption{ + Cafe, + Bench + }; + enum class FriendshipAmount{ + Evolve, + Max + }; + EnumDropdownOption FARMING_OPTION; + EnumDropdownOption FRIENDSHIP_AMOUNT; + SimpleIntegerOption NUM_PARTY_MEMBERS; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + EventNotificationOption NOTIFICATION_STATUS_OPTION; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif \ No newline at end of file diff --git a/SerialPrograms/SourceFiles.cmake b/SerialPrograms/SourceFiles.cmake index 4ca6c1cd24..ba880bdba9 100644 --- a/SerialPrograms/SourceFiles.cmake +++ b/SerialPrograms/SourceFiles.cmake @@ -1567,6 +1567,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonLZA/PokemonLZA_Panels.h Source/PokemonLZA/PokemonLZA_Settings.cpp Source/PokemonLZA/PokemonLZA_Settings.h + Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp + Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.h Source/PokemonLZA/Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.cpp Source/PokemonLZA/Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.h Source/PokemonLZA/Programs/Farming/PokemonLZA_MegaShardFarmer.cpp From 48d937d29176ca1a1cbb007ef065f5eef7cc72d0 Mon Sep 17 00:00:00 2001 From: Dalton-V Date: Sun, 9 Nov 2025 18:01:20 -0600 Subject: [PATCH 2/3] Fix typo --- SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp | 2 +- ...LZA_FreindshipFarmer.cpp => PokemonLZA_FriendshipFarmer.cpp} | 2 +- ...emonLZA_FreindshipFarmer.h => PokemonLZA_FriendshipFarmer.h} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename SerialPrograms/Source/PokemonLZA/Programs/Farming/{PokemonLZA_FreindshipFarmer.cpp => PokemonLZA_FriendshipFarmer.cpp} (99%) rename SerialPrograms/Source/PokemonLZA/Programs/Farming/{PokemonLZA_FreindshipFarmer.h => PokemonLZA_FriendshipFarmer.h} (100%) diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index d2e82255e1..2ea500ee09 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -19,7 +19,7 @@ #include "Programs/Trading/PokemonLZA_SelfBoxTrade.h" // Farming -#include "Programs/Farming/PokemonLZA_FreindshipFarmer.h" +#include "Programs/Farming/PokemonLZA_FriendshipFarmer.h" #include "Programs/Farming/PokemonLZA_RestaurantFarmer.h" #include "Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.h" #include "Programs/Farming/PokemonLZA_MegaShardFarmer.h" diff --git a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp similarity index 99% rename from SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp rename to SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp index bb0b99689e..fa515f4de7 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp @@ -4,7 +4,7 @@ * */ -#include "PokemonLZA_FreindshipFarmer.h" +#include "PokemonLZA_FriendshipFarmer.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/Exceptions/ScreenshotException.h" diff --git a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.h similarity index 100% rename from SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FreindshipFarmer.h rename to SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.h From 7b3cc35f3a2e2702889f6eac186f6f1099a97c88 Mon Sep 17 00:00:00 2001 From: Dalton-V Date: Tue, 11 Nov 2025 18:58:14 -0600 Subject: [PATCH 3/3] Implement OverworldPartySelectionWatcher --- .../Farming/PokemonLZA_FriendshipFarmer.cpp | 133 ++++++++++++++++-- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp index fa515f4de7..59c777d9b9 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_FriendshipFarmer.cpp @@ -12,11 +12,13 @@ #include "CommonFramework/Notifications/ProgramNotifications.h" #include "CommonFramework/ProgramStats/StatsTracking.h" #include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h" #include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_OverworldPartySelectionDetector.h" #include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h" namespace PokemonAutomation{ @@ -116,6 +118,8 @@ void FriendshipFarmer::on_config_value_changed(void* object) { void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + bool seen_selection_arrow = false; + while (true) { ButtonWatcher buttonA_watcher( @@ -137,6 +141,7 @@ void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProContro ); FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); BlueDialogWatcher blue_dialog_watcher(COLOR_BLUE, &env.console.overlay()); + BlackScreenOverWatcher black_screen(COLOR_BLUE); int ret = wait_until( env.console, context, @@ -147,10 +152,14 @@ void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProContro selection_arrow_watcher, white_dialog_watcher, blue_dialog_watcher, + black_screen } ); context.wait_for(100ms); + OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + int ret2 = 0; + switch (ret) { case 0: env.log("Detected A button."); @@ -162,6 +171,7 @@ void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProContro return; case 2: env.log("Detected selection arrow."); + seen_selection_arrow = true; pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; case 3: @@ -172,12 +182,37 @@ void FriendshipFarmer::enter_cafe(SingleSwitchProgramEnvironment& env, ProContro env.log("Detected blue dialog."); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; + case 5: + if (seen_selection_arrow){ + env.log("Detected loading screen. Entering cafe."); + continue; + } + + env.log("Detected day change."); + ret2 = wait_until( + env.console, context, + 30s, + { overworld } + ); + + if (ret2 == 0) { + env.log("Returned to overworld."); + continue; + } + + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "enter_cafe(): Unable to detect overworld after day/night change.", + env.console + ); default: ++stats.errors; env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "enter_cafe(): No recognized state after 60 seconds.", + "enter_cafe(): No recognized state after 10 seconds.", env.console ); } @@ -205,6 +240,7 @@ void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProContro ); FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); BlueDialogWatcher blue_dialog_watcher(COLOR_RED, &env.console.overlay()); + BlackScreenOverWatcher black_screen(COLOR_BLUE); int ret = wait_until( env.console, context, @@ -214,10 +250,14 @@ void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProContro selection_arrow_watcher, white_dialog_watcher, blue_dialog_watcher, + black_screen } ); context.wait_for(100ms); + OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + int ret2 = 0; + switch (ret) { case 0: env.log("Detected A button."); @@ -235,6 +275,26 @@ void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProContro env.log("Detected blue dialog."); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; + case 5: + env.log("Detected day change."); + ret2 = wait_until( + env.console, context, + 30s, + {overworld} + ); + + if (ret2 == 0){ + env.log("Returned to overworld."); + continue; + } + + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "exit_bench(): Unable to detect overworld after day/night change.", + env.console + ); default: if (seen_selection_arrow && ++reset_attempt <= 10) { env.log("Attempting to face the bench."); @@ -246,7 +306,7 @@ void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProContro env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "exit_bench(): No recognized state after 60 seconds.", + "exit_bench(): No recognized state after 10 seconds.", env.console ); } @@ -256,9 +316,6 @@ void FriendshipFarmer::exit_bench(SingleSwitchProgramEnvironment& env, ProContro void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); - int reset_attempt = 0; - bool seen_selection_arrow = false; - while (true) { ButtonWatcher buttonA_watcher( @@ -274,6 +331,7 @@ void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControl ); FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); BlueDialogWatcher blue_dialog_watcher(COLOR_RED, &env.console.overlay()); + BlackScreenOverWatcher black_screen(COLOR_BLUE); int ret = wait_until( env.console, context, @@ -283,10 +341,14 @@ void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControl selection_arrow_watcher, white_dialog_watcher, blue_dialog_watcher, + black_screen } ); context.wait_for(100ms); + OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + int ret2 = 0; + switch (ret) { case 0: env.log("Detected A button."); @@ -303,10 +365,16 @@ void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControl env.log("Detected blue dialog."); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; - default: - if (seen_selection_arrow && ++reset_attempt <= 10) { - env.log("No recognized state, possible day/night change. Waiting.."); - context.wait_for(1000ms); + case 5: + env.log("Detected loading screen while leaving cafe caused by day/night change. Waiting.."); + ret2 = wait_until( + env.console, context, + 30s, + { overworld } + ); + + if (ret2 == 0) { + env.log("Returned to overworld."); continue; } @@ -314,7 +382,15 @@ void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControl env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "exit_cafe(): No recognized state after 60 seconds.", + "exit_cafe(): Unable to detect overworld after day/night change.", + env.console + ); + default: + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "exit_cafe(): No recognized state after 10 seconds.", env.console ); } @@ -324,6 +400,7 @@ void FriendshipFarmer::exit_cafe(SingleSwitchProgramEnvironment& env, ProControl void FriendshipFarmer::hang_out_bench(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ FriendshipFarmer_Descriptor::Stats& stats = env.current_stats(); + bool seen_selection_arrow = false; while (true) { ButtonWatcher buttonA_watcher( @@ -345,6 +422,7 @@ void FriendshipFarmer::hang_out_bench(SingleSwitchProgramEnvironment& env, ProCo ); FlatWhiteDialogWatcher white_dialog_watcher(COLOR_WHITE, &env.console.overlay()); BlueDialogWatcher blue_dialog_watcher(COLOR_BLUE, &env.console.overlay()); + BlackScreenOverWatcher black_screen(COLOR_BLUE); int ret = wait_until( env.console, context, @@ -355,22 +433,28 @@ void FriendshipFarmer::hang_out_bench(SingleSwitchProgramEnvironment& env, ProCo selection_arrow_watcher, white_dialog_watcher, blue_dialog_watcher, + black_screen } ); context.wait_for(100ms); + OverworldPartySelectionWatcher overworld(COLOR_WHITE, &env.console.overlay()); + int ret2 = 0; + switch (ret) { case 0: env.log("Detected A button."); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; case 1: - env.log("Detected B button. Waiting 10 Seconds."); + env.log("Detected B button. Waiting 10 Seconds."); //Wait 10 seconds to gain friendship points. context.wait_for(10000ms); pbf_press_button(context, BUTTON_B, 80ms, 40ms); return; case 2: env.log("Detected selection arrow."); + seen_selection_arrow = true; + //Select second option to hang out on bench. pbf_press_dpad(context, DPAD_DOWN, 40ms, 40ms); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; @@ -382,12 +466,37 @@ void FriendshipFarmer::hang_out_bench(SingleSwitchProgramEnvironment& env, ProCo env.log("Detected blue dialog."); pbf_press_button(context, BUTTON_A, 80ms, 40ms); continue; + case 5: + if (seen_selection_arrow){ + env.log("Detected loading screen. Hanging out on bench."); + continue; + } + + env.log("Detected day change."); + ret2 = wait_until( + env.console, context, + 30s, + { overworld } + ); + + if (ret2 == 0) { + env.log("Returned to overworld."); + continue; + } + + ++stats.errors; + env.update_stats(); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "hang_out_bench(): Unable to detect overworld after day/night change.", + env.console + ); default: ++stats.errors; env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "hang_out_bench(): No recognized state after 60 seconds.", + "hang_out_bench(): No recognized state after 10 seconds.", env.console ); }