diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp index f67ce713e..6cc18e070 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp @@ -24,6 +24,39 @@ namespace PokemonLZA{ using namespace Pokemon; +StallBuyerRow::StallBuyerRow(int index, std::string&& ordinal) + : StaticTableRow(ordinal) + , item(LockMode::LOCK_WHILE_RUNNING, "The number of the " + ordinal + " item you want to purchase.") + , quantity(LockMode::LOCK_WHILE_RUNNING, 0, 0, 999) + , ordinal(std::move(ordinal)) + , index(index) +{ + PA_ADD_STATIC(item); + PA_ADD_OPTION(quantity); +} + +StallBuyerTable::StallBuyerTable() + : StaticTableOption("Number to Purchase:
The number of items you want to purchase.", LockMode::LOCK_WHILE_RUNNING) +{ + add_row(std::make_unique(0, "first")); + add_row(std::make_unique(1, "second")); + add_row(std::make_unique(2, "third")); + add_row(std::make_unique(3, "fourth")); + add_row(std::make_unique(4, "fifth")); + add_row(std::make_unique(5, "sixth")); + add_row(std::make_unique(6, "seventh")); + + finish_construction(); +} +std::vector StallBuyerTable::make_header() const{ + std::vector ret{ + "Item", + "Quantity" + }; + return ret; +} + + StallBuyer_Descriptor::StallBuyer_Descriptor() : SingleSwitchProgramDescriptor( "PokemonLZA:StallBuyer", @@ -55,26 +88,7 @@ std::unique_ptr StallBuyer_Descriptor::make_stats() const{ StallBuyer::StallBuyer() - : ITEM_POSITION( - "Item Position to Purchase:", - { - {ItemPosition::FirstItem, "FirstItem", "First Item" }, - {ItemPosition::SecondItem, "SecondItem", "Second Item" }, - {ItemPosition::ThirdItem, "ThirdItem", "Third Item" }, - {ItemPosition::FourthItem, "FourthItem", "Fourth Item" }, - {ItemPosition::FifthItem, "FifthItem", "Fifth Item" }, - {ItemPosition::SixthItem, "SixthItem", "Sixth Item" }, - {ItemPosition::SeventhItem, "SeventhItem", "Seventh Item" }, - }, - LockMode::LOCK_WHILE_RUNNING, - ItemPosition::FirstItem - ) - , NUM_PURCHASE( - "Number to Purchase:
The number of items you want to purchase.", - LockMode::LOCK_WHILE_RUNNING, - 100, 1, 999 - ) - , GO_HOME_WHEN_DONE(false) + : GO_HOME_WHEN_DONE(false) , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) , NOTIFICATIONS({ &NOTIFICATION_STATUS_UPDATE, @@ -82,7 +96,6 @@ StallBuyer::StallBuyer() &NOTIFICATION_ERROR_FATAL, }) { - PA_ADD_OPTION(ITEM_POSITION); PA_ADD_OPTION(NUM_PURCHASE); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); @@ -146,88 +159,109 @@ void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerConte StallBuyer_Descriptor::Stats& stats = env.current_stats(); assert_16_9_720p_min(env.logger(), env.console); - while (true) { - context.wait_for_all_requests(); + std::optional stall_amount_item; - 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.165, 0.045, 0.440} - ); - SelectionArrowWatcher confirm( - COLOR_YELLOW, &env.console.overlay(), - SelectionArrowType::RIGHT, - {0.665, 0.600, 0.145, 0.080} - ); - FlatWhiteDialogWatcher dialog(COLOR_RED, &env.console.overlay()); + for (StaticTableRow* row : NUM_PURCHASE.table()){ + StallBuyerRow& stall_buyer_row = static_cast(*row); - int ret = wait_until( - env.console, context, - 30000ms, - { - buttonA, - select, - confirm, - dialog, - } + for (int purchases = 0; purchases < stall_buyer_row.quantity; /*Do not increment, it will be done in the loop*/){ + context.wait_for_all_requests(); + + ButtonWatcher buttonA( + COLOR_RED, + ButtonType::ButtonA, + {0.1, 0.1, 0.8, 0.8}, + &env.console.overlay() ); - 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 item selection screen."); - int stall_amount_item = detect_stall_amount_item(env, stats); - env.log("Detected stall with " + std::to_string(stall_amount_item) + " items to sell."); - auto [direction, presses] = compute_needed_inputs(static_cast(ITEM_POSITION.get()), stall_amount_item); - for (int i = 0; i < presses; i++){ - pbf_press_dpad(context, direction, 160ms, 80ms); - } - 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; + SelectionArrowWatcher select( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + {0.715, 0.165, 0.045, 0.440} + ); + SelectionArrowWatcher confirm( + COLOR_YELLOW, &env.console.overlay(), + SelectionArrowType::RIGHT, + {0.665, 0.600, 0.145, 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 item selection screen."); + if (!stall_amount_item.has_value()){ + stall_amount_item = detect_stall_amount_item(env, stats); + env.log("Detected stall with " + std::to_string(stall_amount_item.value()) + " items to sell."); + for (StaticTableRow* item : NUM_PURCHASE.table()){ + StallBuyerRow& casted_item = static_cast(*item); + if (casted_item.index >= stall_amount_item.value() && casted_item.quantity > 0){ + throw UserSetupError( + env.logger(), + "Stall is of size " + std::to_string(stall_amount_item.value()) + " and Number to Purchase for " + + casted_item.ordinal + " item is " + std::to_string(casted_item.quantity) + "." + ); + } + } + } + auto [direction, presses] = compute_needed_inputs(stall_buyer_row.index, stall_amount_item.value()); + for (int i = 0; i < presses; i++){ + pbf_press_dpad(context, direction, 160ms, 80ms); + } + 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++; + purchases++; + env.update_stats(); - continue; + if (purchases == stall_buyer_row.quantity){ + std::stringstream ss; + ss << "Purchased " << stall_buyer_row.quantity << " of " << stall_buyer_row.ordinal << " item."; + send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE, ss.str()); + break; + } - case 3: - env.log("Detected white dialog."); - pbf_press_button(context, BUTTON_A, 160ms, 80ms); - continue; + continue; - default: - stats.errors++; - env.update_stats(); - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "No recognized state after 30 seconds.", - env.console - ); + 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 + ); + } } } + // 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); } diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h index 5be76c30b..02cf9802a 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h @@ -9,6 +9,7 @@ #include "Common/Cpp/Options/SimpleIntegerOption.h" #include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "CommonFramework/Options/LabelCellOption.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" @@ -18,6 +19,24 @@ namespace PokemonLZA{ +class StallBuyerRow : public StaticTableRow{ +public: + StallBuyerRow(int index, std::string&& ordinal); + + LabelCellOption item; + SimpleIntegerCell quantity; + std::string ordinal; + int index; +}; + +class StallBuyerTable : public StaticTableOption { +public: + StallBuyerTable(); + virtual std::vector make_header() const; +}; + + + class StallBuyer_Descriptor : public SingleSwitchProgramDescriptor{ public: StallBuyer_Descriptor(); @@ -35,17 +54,7 @@ class StallBuyer : public SingleSwitchProgramInstance{ virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; private: - enum class ItemPosition{ - FirstItem, - SecondItem, - ThirdItem, - FourthItem, - FifthItem, - SixthItem, - SeventhItem - }; - EnumDropdownOption ITEM_POSITION; - SimpleIntegerOption NUM_PURCHASE; + StallBuyerTable NUM_PURCHASE; GoHomeWhenDoneOption GO_HOME_WHEN_DONE; EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS;