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;