From 79d353d8eb34106f36d834c02f035fd0306dd2b1 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Tue, 21 Oct 2025 14:08:44 -0400 Subject: [PATCH 1/3] LZA clothing buyer --- .../Source/PokemonLZA/PokemonLZA_Panels.cpp | 2 + .../Programs/PokemonLZA_ClothingBuyer.cpp | 184 ++++++++++++++++++ .../Programs/PokemonLZA_ClothingBuyer.h | 45 +++++ SerialPrograms/SourceFiles.cmake | 2 + 4 files changed, 233 insertions(+) create mode 100644 SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp create mode 100644 SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index 892ed6afdc..8991d91676 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -11,6 +11,7 @@ #include "PokemonLZA_Settings.h" #include "Programs/PokemonLZA_RestaurantFarmer.h" +#include "Programs/PokemonLZA_ClothingBuyer.h" #include "Programs/TestPrograms/PokemonLZA_OverworldWatcher.h" namespace PokemonAutomation{ @@ -31,6 +32,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()); if (PreloadSettings::instance().DEVELOPER_MODE){ diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp new file mode 100644 index 0000000000..693bca9883 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp @@ -0,0 +1,184 @@ +/* Clothing Buyer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/Exceptions/OperationFailedException.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_SelectionArrowDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" +#include "PokemonLZA_ClothingBuyer.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + +using namespace Pokemon; + +ClothingBuyer_Descriptor::ClothingBuyer_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonLZA:ClothingBuyer", + STRING_POKEMON + " LZA", "Clothing Buyer", + "Programs/PokemonLZA/ClothingBuyer.html", + "Buy all the clothing in a store.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +ClothingBuyer::ClothingBuyer() + : NUM_CATEGORY( + "Number of Categories:
The number of categories of clothing the shop has.", + LockMode::LOCK_WHILE_RUNNING, + 1, 1, 5 + ) + , 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(NUM_CATEGORY); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + assert_16_9_720p_min(env.logger(), env.console); + + /* + * Similar to SV clothing buyer. + * Start at the top of the first category in clothing shop menu. + * Buy everything in num of categories, can set num category, will wrap around. + * Stop conditions: hit category count + * Can't detect out of money, same dialog box as already bought. + * Hairstyles all seem to be hat compatible in this game. Tried w/pigtails, which wasn't compatible in SV. + */ + + uint8_t category_rotation_count = 0; + //bool finish_program = false; + while (category_rotation_count < NUM_CATEGORY){ + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + + //In sub-menu for item. + while (true) { + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + + SelectionArrowWatcher buy_yes_no( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.714, 0.599, 0.033, 0.066 } + ); + FlatWhiteDialogWatcher already_bought(COLOR_RED, &env.console.overlay()); + int ret = wait_until( + env.console, context, + std::chrono::seconds(2), + { buy_yes_no, already_bought } + ); + switch (ret) { + case 0: + env.log("Purchase item."); + //Purchase Y/N detected. + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + + //Would you like to wear this out today? (No.) + pbf_press_button(context, BUTTON_B, 160ms, 80ms); + + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Clothing purchased. Selecting next item." + ); + break; + case 1: + //Item already bought or out of money. Close dialog. + env.log("Item already bought or out of money."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Item already bought or out of money." + ); + pbf_press_button(context, BUTTON_A, 160ms, 80ms); + break; + default: + env.log("Error looking for purchase prompt."); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Error looking for purchase prompt.", + env.console + ); + break; + } + context.wait_for_all_requests(); + + env.log("Moving on to next item."); + pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); + context.wait_for_all_requests(); + + //Check if we are back at the top of the sub-menu + SelectionArrowWatcher top_category_item( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + { 0.034, 0.465, 0.033, 0.071 } + ); + int retCatTop = wait_until( + env.console, context, + 1000ms, + { top_category_item } + ); + if (retCatTop == 0) { + env.log("Reached top of the item sub-menu."); + break; + } + } + + //Exit sub-menu + pbf_press_button(context, BUTTON_B, 160ms, 200ms); + context.wait_for_all_requests(); + + //Next item on top menu + env.log("Moving on to next item."); + pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); + context.wait_for_all_requests(); + + + //Check if category complete + SelectionArrowWatcher top_item( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + {0.033, 0.350, 0.033, 0.070} + ); + int retTop = wait_until( + env.console, context, + std::chrono::seconds(1), + { top_item } + ); + if (retTop == 0){ + env.log("Reached top of category."); + if (NUM_CATEGORY > 1){ + env.log("Category rotation set. Moving to next category."); + pbf_press_button(context, BUTTON_R, 80ms, 100ms); + context.wait_for_all_requests(); + category_rotation_count++; + }else{ + env.log("No category rotation. Ending program."); + break; + } + } + } + env.log("Category num hit. Ending program."); + GO_HOME_WHEN_DONE.run_end_of_program(context); + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h new file mode 100644 index 0000000000..116cc37f01 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h @@ -0,0 +1,45 @@ +/* Clothing Buyer + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonLZA_ClothingBuyer_H +#define PokemonAutomation_PokemonLZA_ClothingBuyer_H + +#include "CommonFramework/ImageTypes/ImageRGB32.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 ClothingBuyer_Descriptor : public SingleSwitchProgramDescriptor{ +public: + ClothingBuyer_Descriptor(); +}; + +class ClothingBuyer : public SingleSwitchProgramInstance{ +public: + ClothingBuyer(); + + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + SimpleIntegerOption NUM_CATEGORY; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + + + + + +} +} +} +#endif diff --git a/SerialPrograms/SourceFiles.cmake b/SerialPrograms/SourceFiles.cmake index bd9faf9795..062f821efd 100644 --- a/SerialPrograms/SourceFiles.cmake +++ b/SerialPrograms/SourceFiles.cmake @@ -1547,6 +1547,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonLZA/PokemonLZA_Panels.h Source/PokemonLZA/PokemonLZA_Settings.cpp Source/PokemonLZA/PokemonLZA_Settings.h + Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp + Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp Source/PokemonLZA/Programs/PokemonLZA_GameEntry.h Source/PokemonLZA/Programs/PokemonLZA_RestaurantFarmer.cpp From ef78724beb4640695d9a50151bb171e4f287363e Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Tue, 21 Oct 2025 15:18:51 -0400 Subject: [PATCH 2/3] timing adjustments --- .../Programs/PokemonLZA_ClothingBuyer.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp index 693bca9883..7cbf836f51 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp @@ -70,7 +70,7 @@ void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerCo //In sub-menu for item. while (true) { - pbf_press_button(context, BUTTON_A, 160ms, 80ms); + pbf_press_button(context, BUTTON_A, 160ms, 180ms); SelectionArrowWatcher buy_yes_no( COLOR_YELLOW, &env.console.overlay(), @@ -87,10 +87,14 @@ void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerCo case 0: env.log("Purchase item."); //Purchase Y/N detected. - pbf_press_button(context, BUTTON_A, 160ms, 80ms); + pbf_press_button(context, BUTTON_A, 160ms, 500ms); + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); //Would you like to wear this out today? (No.) - pbf_press_button(context, BUTTON_B, 160ms, 80ms); + pbf_press_button(context, BUTTON_B, 160ms, 500ms); + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); send_program_status_notification( env, NOTIFICATION_STATUS_UPDATE, @@ -104,7 +108,7 @@ void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerCo env, NOTIFICATION_STATUS_UPDATE, "Item already bought or out of money." ); - pbf_press_button(context, BUTTON_A, 160ms, 80ms); + pbf_press_button(context, BUTTON_A, 160ms, 180ms); break; default: env.log("Error looking for purchase prompt."); @@ -118,7 +122,7 @@ void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerCo context.wait_for_all_requests(); env.log("Moving on to next item."); - pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); + pbf_press_dpad(context, DPAD_DOWN, 80ms, 180ms); context.wait_for_all_requests(); //Check if we are back at the top of the sub-menu @@ -132,6 +136,7 @@ void ClothingBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerCo 1000ms, { top_category_item } ); + context.wait_for_all_requests(); if (retCatTop == 0) { env.log("Reached top of the item sub-menu."); break; From 8cfc0f4226ee1e97678ba115e8a277491b646820 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Tue, 21 Oct 2025 16:24:00 -0400 Subject: [PATCH 3/3] update max categories --- .../Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp index 7cbf836f51..89a029689c 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp @@ -36,7 +36,7 @@ ClothingBuyer::ClothingBuyer() : NUM_CATEGORY( "Number of Categories:
The number of categories of clothing the shop has.", LockMode::LOCK_WHILE_RUNNING, - 1, 1, 5 + 1, 1, 9 ) , GO_HOME_WHEN_DONE(false) , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600))