diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp
index b27ba1bcb..f67ce713e 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp
+++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.cpp
@@ -7,8 +7,10 @@
#include "CommonFramework/Exceptions/OperationFailedException.h"
#include "CommonFramework/Notifications/ProgramNotifications.h"
#include "CommonFramework/ProgramStats/StatsTracking.h"
+#include "CommonFramework/VideoPipeline/VideoFeed.h"
#include "CommonTools/Async/InferenceRoutines.h"
#include "CommonTools/StartupChecks/VideoResolutionCheck.h"
+#include "CommonTools/VisualDetectors/BlackScreenDetector.h"
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
#include "Pokemon/Pokemon_Strings.h"
#include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h"
@@ -67,11 +69,6 @@ StallBuyer::StallBuyer()
LockMode::LOCK_WHILE_RUNNING,
ItemPosition::FirstItem
)
- , NUM_ITEM(
- "Number of available items in the stall:
Number of available items in the stall.",
- LockMode::LOCK_WHILE_RUNNING,
- 6, 2, 7
- )
, NUM_PURCHASE(
"Number to Purchase:
The number of items you want to purchase.",
LockMode::LOCK_WHILE_RUNNING,
@@ -86,15 +83,57 @@ StallBuyer::StallBuyer()
})
{
PA_ADD_OPTION(ITEM_POSITION);
- PA_ADD_OPTION(NUM_ITEM);
PA_ADD_OPTION(NUM_PURCHASE);
PA_ADD_OPTION(GO_HOME_WHEN_DONE);
PA_ADD_OPTION(NOTIFICATIONS);
}
-std::pair compute_needed_inputs(int item_position, int num_item){
+int detect_stall_amount_item(SingleSwitchProgramEnvironment& env, StallBuyer_Descriptor::Stats& stats){
+ // When buying from a stall, the first item is always selected.
+ // Detect which one is currently selected (with white background)
+ // 0.700 as y is the bottom option, then each one is shifted by 0.072
+ ImageFloatBox seven_item_stall_box (0.858, 0.700 - 7 * 0.072, 0.024, 0.019);
+ ImageFloatBox six_item_stall_box (0.858, 0.700 - 6 * 0.072, 0.024, 0.019);
+ ImageFloatBox five_item_stall_box (0.858, 0.700 - 5 * 0.072, 0.024, 0.019);
+ ImageFloatBox two_item_stall_box (0.858, 0.700 - 2 * 0.072, 0.024, 0.019);
+
+ WhiteScreenDetector seven_item_stall_detector(COLOR_BLUE, seven_item_stall_box);
+ WhiteScreenDetector six_item_stall_detector(COLOR_BLUE, six_item_stall_box);
+ WhiteScreenDetector five_item_stall_detector(COLOR_BLUE, five_item_stall_box);
+ WhiteScreenDetector two_item_stall_detector(COLOR_BLUE, two_item_stall_box);
+
+ VideoSnapshot snapshot = env.console.video().snapshot();
+ bool is_seven_item_stall = seven_item_stall_detector.detect(snapshot);
+ bool is_six_item_stall = six_item_stall_detector.detect(snapshot);
+ bool is_five_item_stall = five_item_stall_detector.detect(snapshot);
+ bool is_two_item_stall = two_item_stall_detector.detect(snapshot);
+
+ int count = is_seven_item_stall + is_six_item_stall + is_five_item_stall + is_two_item_stall;
+ if (count == 1){
+ // Exactly one kind of stall detected
+ if (is_seven_item_stall){
+ return 7;
+ }else if (is_six_item_stall){
+ return 6;
+ }else if (is_five_item_stall){
+ return 5;
+ }else{
+ return 2;
+ }
+ }else{
+ stats.errors++;
+ env.update_stats();
+ OperationFailedException::fire(
+ ErrorReport::SEND_ERROR_REPORT,
+ "No recognized stall size.",
+ env.console
+ );
+ }
+}
+
+std::pair compute_needed_inputs(int item_position, int stall_amount_item){
int down_presses = item_position;
- int up_presses = num_item - item_position + 1;
+ int up_presses = stall_amount_item - item_position + 1;
if (down_presses <= up_presses){
return { DPAD_DOWN, down_presses };
@@ -106,13 +145,6 @@ std::pair compute_needed_inputs(int item_position, int num_it
void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
StallBuyer_Descriptor::Stats& stats = env.current_stats();
assert_16_9_720p_min(env.logger(), env.console);
- int item_position = static_cast(ITEM_POSITION.get());
- if (item_position >= NUM_ITEM){
- throw UserSetupError(
- env.logger(),
- "Item position to purchase must be less than or equal to number of available items in the stall."
- );
- }
while (true) {
context.wait_for_all_requests();
@@ -147,7 +179,6 @@ void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerConte
);
context.wait_for(100ms);
- auto [direction, presses] = compute_needed_inputs(item_position, NUM_ITEM);
switch (ret){
case 0:
env.log("Detected A button.");
@@ -155,13 +186,17 @@ void StallBuyer::program(SingleSwitchProgramEnvironment& env, ProControllerConte
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);
diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h
index f0e81bd6e..5be76c30b 100644
--- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h
+++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_StallBuyer.h
@@ -45,7 +45,6 @@ class StallBuyer : public SingleSwitchProgramInstance{
SeventhItem
};
EnumDropdownOption ITEM_POSITION;
- SimpleIntegerOption NUM_ITEM;
SimpleIntegerOption NUM_PURCHASE;
GoHomeWhenDoneOption GO_HOME_WHEN_DONE;
EventNotificationOption NOTIFICATION_STATUS_UPDATE;