From d8c1640b8b49d2afc0aace350727d5b6c8d8fef7 Mon Sep 17 00:00:00 2001 From: Gin <> Date: Sun, 1 Oct 2023 10:12:25 -0700 Subject: [PATCH] fix scatterbug program --- .../Map/PokemonSV_MapMenuDetector.cpp | 6 +- .../Inference/Map/PokemonSV_MapMenuDetector.h | 6 +- .../Programs/PokemonSV_Navigation.cpp | 118 ++++++++++++------ .../PokemonSV/Programs/PokemonSV_Navigation.h | 7 +- .../PokemonSV_ShinyHunt-Scatterbug.cpp | 35 ++++-- .../PokemonSV_ShinyHunt-Scatterbug.h | 1 + 6 files changed, 113 insertions(+), 60 deletions(-) diff --git a/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.cpp b/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.cpp index 6b313f4d0..b337e84c3 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.cpp +++ b/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.cpp @@ -43,9 +43,8 @@ bool MapFlyMenuDetector::detect(const ImageViewRGB32& screen) const{ MapFlyMenuWatcher::~MapFlyMenuWatcher() = default; -MapFlyMenuWatcher::MapFlyMenuWatcher(Color color, VideoOverlay& overlay) +MapFlyMenuWatcher::MapFlyMenuWatcher(Color color) : VisualInferenceCallback("MapFlyMenuWatcher") - , m_overlay(overlay) , m_detector(color) {} @@ -81,9 +80,8 @@ bool MapDestinationMenuDetector::detect(const ImageViewRGB32& screen) const{ MapDestinationMenuWatcher::~MapDestinationMenuWatcher() = default; -MapDestinationMenuWatcher::MapDestinationMenuWatcher(Color color, VideoOverlay& overlay) +MapDestinationMenuWatcher::MapDestinationMenuWatcher(Color color) : VisualInferenceCallback("MapDestinationMenuWatcher") - , m_overlay(overlay) , m_detector(color) {} diff --git a/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h b/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h index ac79eb47b..35635326d 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h +++ b/SerialPrograms/Source/PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h @@ -43,14 +43,13 @@ class MapFlyMenuDetector : public StaticScreenDetector{ // Watch for the "Fly here" menu to open on map class MapFlyMenuWatcher : public VisualInferenceCallback{ public: - MapFlyMenuWatcher(Color color, VideoOverlay& overlay); + MapFlyMenuWatcher(Color color); virtual ~MapFlyMenuWatcher(); virtual void make_overlays(VideoOverlaySet& items) const override; virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override; protected: - VideoOverlay& m_overlay; MapFlyMenuDetector m_detector; }; @@ -77,13 +76,12 @@ class MapDestinationMenuDetector : public StaticScreenDetector{ class MapDestinationMenuWatcher : public VisualInferenceCallback{ public: ~MapDestinationMenuWatcher(); - MapDestinationMenuWatcher(Color color, VideoOverlay& overlay); + MapDestinationMenuWatcher(Color color); virtual void make_overlays(VideoOverlaySet& items) const override; virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override; protected: - VideoOverlay& m_overlay; MapDestinationMenuDetector m_detector; }; diff --git a/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.cpp b/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.cpp index 3bd08b35a..640c645b4 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.cpp +++ b/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.cpp @@ -14,6 +14,7 @@ #include "PokemonSV/PokemonSV_Settings.h" #include "PokemonSV/Inference/Dialogs/PokemonSV_DialogDetector.h" #include "PokemonSV/Inference/Map/PokemonSV_MapDetector.h" +#include "PokemonSV/Inference/Map/PokemonSV_MapMenuDetector.h" #include "PokemonSV/Inference/Map/PokemonSV_MapPokeCenterIconDetector.h" #include "PokemonSV/Inference/Picnics/PokemonSV_PicnicDetector.h" #include "PokemonSV/Inference/PokemonSV_MainMenuDetector.h" @@ -30,8 +31,6 @@ namespace NintendoSwitch{ namespace PokemonSV{ - - void set_time_to_12am_from_home(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ DateReader reader; VideoOverlaySet overlays(console.overlay()); @@ -63,6 +62,27 @@ void day_skip_from_overworld(ConsoleHandle& console, BotBaseContext& context){ resume_game_from_home(console, context); } +void press_Bs_to_back_to_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ + context.wait_for_all_requests(); + OverworldWatcher overworld(COLOR_RED); + int ret = run_until( + console, context, + [](BotBaseContext& context){ + for (size_t c = 0; c < 10; c++){ + pbf_press_button(context, BUTTON_B, 20, 230); + } + }, + {overworld} + ); + if (ret < 0){ + throw OperationFailedException( + ErrorReport::SEND_ERROR_REPORT, console, + "press_Bs_to_back_to_overworld(): Unable to detect overworld after 10 button B presses.", + true + ); + } +} + void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ { OverworldWatcher overworld(COLOR_CYAN); @@ -143,7 +163,7 @@ void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, Bo } } -void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ +bool fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context, bool check_fly_menuitem){ context.wait_for_all_requests(); // Press A to bring up the promp dialog on choosing "Fly here", "Set as destination", "Never mind". pbf_press_button(context, BUTTON_A, 20, 130); @@ -153,45 +173,51 @@ void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, if (current_time() - start > std::chrono::minutes(2)){ throw OperationFailedException( ErrorReport::SEND_ERROR_REPORT, console, - "fly_to_overworld_from_map(): Failed to open map after 2 minutes.", + "fly_to_overworld_from_map(): Failed to fly from map after 2 minutes.", true ); } int ret = 0; - { - OverworldWatcher overworld(COLOR_CYAN); - WhiteButtonWatcher map(COLOR_RED, WhiteButton::ButtonY, {0.800, 0.118, 0.030, 0.060}); - GradientArrowWatcher spot_dialog_watcher(COLOR_YELLOW, GradientArrowType::RIGHT, {0.469, 0.500, 0.215, 0.150}); - PromptDialogWatcher confirm_watcher(COLOR_BLUE, {0.686, 0.494, 0.171, 0.163}); + OverworldWatcher overworld(COLOR_CYAN); + WhiteButtonWatcher map(COLOR_RED, WhiteButton::ButtonY, {0.800, 0.118, 0.030, 0.060}); + GradientArrowWatcher spot_dialog_watcher(COLOR_YELLOW, GradientArrowType::RIGHT, {0.469, 0.500, 0.215, 0.150}); + PromptDialogWatcher confirm_watcher(COLOR_BLUE, {0.686, 0.494, 0.171, 0.163}); + MapFlyMenuWatcher flyMenuItemWatcher(COLOR_BLACK); + MapDestinationMenuWatcher destinationMenuItemWatcher(COLOR_BLACK); - context.wait_for_all_requests(); - ret = wait_until( - console, context, - std::chrono::minutes(2), - {overworld, map, spot_dialog_watcher, confirm_watcher} - ); + context.wait_for_all_requests(); + + std::vector callbacks{overworld, map, spot_dialog_watcher, confirm_watcher}; + if (check_fly_menuitem){ + callbacks[2] = flyMenuItemWatcher; + callbacks.push_back(destinationMenuItemWatcher); } + + ret = wait_until(console, context, std::chrono::minutes(2), callbacks); context.wait_for(std::chrono::milliseconds(100)); switch (ret){ case 0: - console.log("Detected overworld."); - return; + console.log("Detected overworld. Fly successful."); + return true; case 1: - console.log("Detected map."); + console.log("Detected map. Pressing A to open map menu."); // Press A to bring up the promp dialog on choosing "Fly here", "Set as destination", "Never mind". pbf_press_button(context, BUTTON_A, 20, 130); continue; case 2: console.log("Detected fly here prompt dialog."); - console.overlay().add_log("Fly", COLOR_WHITE); + console.overlay().add_log("Fly"); pbf_press_button(context, BUTTON_A, 20, 130); continue; case 3: console.log("Detected fly confirmation prompt."); pbf_press_button(context, BUTTON_A, 20, 130); continue; - + case 4: + console.log("Detected no fly spot here."); + console.overlay().add_log("No fly spot", COLOR_RED); + return false; default: throw OperationFailedException( ErrorReport::SEND_ERROR_REPORT, console, @@ -380,24 +406,7 @@ void enter_box_system_from_overworld(const ProgramInfo& info, ConsoleHandle& con void leave_box_system_to_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ context.wait_for_all_requests(); console.log("Leave box system to overworld..."); - OverworldWatcher overworld(COLOR_RED); - context.wait_for_all_requests(); - int ret = run_until( - console, context, - [](BotBaseContext& context){ - for (size_t c = 0; c < 10; c++){ - pbf_press_button(context, BUTTON_B, 20, 230); - } - }, - {overworld} - ); - if (ret < 0){ - throw OperationFailedException( - ErrorReport::SEND_ERROR_REPORT, console, - "leave_box_system_to_overworld(): Unknown state after 10 button B presses.", - true - ); - } + press_Bs_to_back_to_overworld(info, console, context); } @@ -493,9 +502,9 @@ void leave_phone_to_overworld(const ProgramInfo& info, ConsoleHandle& console, B } } -// While in the current map zoom level, detect pokecenter icons and fly to the closest one. +// While in the current map zoom level, detect pokecenter icons and move the map cursor there. // Return true if succeed. Return false if no visible pokcenter on map -bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ +bool detect_closest_pokecenter_and_move_map_cursor_there(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ context.wait_for_all_requests(); const auto snapshot_frame = console.video().snapshot().frame; const size_t screen_width = snapshot_frame->width(); @@ -542,10 +551,37 @@ bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, C console.overlay().add_log("Move Cursor to PokeCenter", COLOR_WHITE); const uint16_t push_time = std::max(uint16_t(magnitude * scale + 0.5), uint16_t(3)); pbf_move_left_joystick(context, move_x, move_y, push_time, 30); - fly_to_overworld_from_map(info, console, context); + context.wait_for_all_requests(); return true; } +// While in the current map zoom level, detect pokecenter icons and fly to the closest one. +// Return true if succeed. Return false if no visible pokcenter on map +bool fly_to_visible_closest_pokecenter_cur_zoom_level(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ + const int max_try_count = 3; + for(int try_count = 0; try_count < max_try_count; ++try_count){ + if (!detect_closest_pokecenter_and_move_map_cursor_there(info, console, context)){ + return false; + } + bool check_fly_menuitem = true; + const bool success = fly_to_overworld_from_map(info, console, context, check_fly_menuitem); + if (success){ + return true; + } + if (try_count + 1 < max_try_count){ // if this is not the last try: + console.log("Failed to find the fly menuitem. Restart the closest Pokecenter travel process."); + press_Bs_to_back_to_overworld(info, console, context); + open_map_from_overworld(info, console, context); + } + } + // last try failed: + throw OperationFailedException( + ErrorReport::SEND_ERROR_REPORT, console, + "fly_to_visible_closest_pokecenter_cur_zoom_level(): tried three times to fly to pokecenter, but no \"Fly\" menuitem.", + true + ); +} + void fly_to_closest_pokecenter_on_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context){ // Zoom in one level onto the map. diff --git a/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.h b/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.h index 23a1da24a..a5cddf8b9 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.h +++ b/SerialPrograms/Source/PokemonSV/Programs/PokemonSV_Navigation.h @@ -31,7 +31,12 @@ void day_skip_from_overworld(ConsoleHandle& console, BotBaseContext& context); void open_map_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context); // From map, press A to fly to a travel spot. -void fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context); +// check_fly_menuitem == true: will detect if the "Fly" menuitem is available. Return false if no "Fly" menuitem. Return +// true if flying successful. +// check_fly_menuitem == false: will use GradientArrowDetector to check if a map menu is opened. No "Fly" menuitem check. +// The function always returns true. It throws an error in the case of no "Fly" menuitem. But the error message will be about +// timeout running the function. +bool fly_to_overworld_from_map(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context, bool check_fly_menuitem = false); // Assume the user can set up picnic at current location, start picnic from overworld. void picnic_from_overworld(const ProgramInfo& info, ConsoleHandle& console, BotBaseContext& context); diff --git a/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.cpp b/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.cpp index 810150877..160c7ae5e 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.cpp +++ b/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.cpp @@ -106,6 +106,12 @@ ShinyHuntScatterbug::ShinyHuntScatterbug() LockWhileRunning::LOCKED, false ) + , SKIP_SANDWICH( + "Whether to skip making sandwich:
" + "This is for debugging the program without waiting for sandwich making.", + LockWhileRunning::UNLOCKED, + false + ) , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) , NOTIFICATIONS({ &NOTIFICATION_STATUS_UPDATE, @@ -121,6 +127,7 @@ ShinyHuntScatterbug::ShinyHuntScatterbug() if (PreloadSettings::instance().DEVELOPER_MODE){ PA_ADD_OPTION(SAVE_DEBUG_VIDEO); PA_ADD_OPTION(DEBUG_WARP_TO_POKECENTER); + PA_ADD_OPTION(SKIP_SANDWICH); } PA_ADD_OPTION(LANGUAGE); PA_ADD_OPTION(SANDWICH_OPTIONS); @@ -205,7 +212,8 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg bool first_iteration = true; // a flag for the case that the action has finished but not yet returned to pokecenter bool returned_to_pokecenter = false; - while(action_finished == false && returned_to_pokecenter == false){ + while(action_finished == false || returned_to_pokecenter == false){ + // env.console.overlay().add_log("Calculate what to do next"); int ret = run_until( env.console, context, [&](BotBaseContext& context){ @@ -240,6 +248,7 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg m_encounter_watcher->throw_if_no_sound(); if (ret >= 0){ env.console.log("Detected battle.", COLOR_PURPLE); + env.console.overlay().add_log("Detected battle"); try{ bool caught, should_save; m_encounter_tracker->process_battle( @@ -255,7 +264,7 @@ void ShinyHuntScatterbug::handle_battles_and_back_to_pokecenter(SingleSwitchProg } } // Back on the overworld. - } // end while(action_finished == false) + } // end while(action_finished == false || returned_to_pokecenter == false) } // Start at Mesagoza South Gate pokecenter, make a sandwich, then use let's go repeatedly until 30 min passes. @@ -293,15 +302,20 @@ void ShinyHuntScatterbug::run_one_sandwich_iteration(SingleSwitchProgramEnvironm pbf_press_button(context, BUTTON_L, 50, 40); // Move forward pbf_move_left_joystick(context, 128, 0, 180, 0); - picnic_from_overworld(env.program_info(), env.console, context); - - pbf_move_left_joystick(context, 128, 0, 30, 40); - enter_sandwich_recipe_list(env.program_info(), env.console, context); - run_sandwich_maker(env, context, SANDWICH_OPTIONS); - last_sandwich_time = current_time(); - leave_picnic(env.program_info(), env.console, context); + if (!SKIP_SANDWICH){ + picnic_from_overworld(env.program_info(), env.console, context); + + pbf_move_left_joystick(context, 128, 0, 30, 40); + enter_sandwich_recipe_list(env.program_info(), env.console, context); + run_sandwich_maker(env, context, SANDWICH_OPTIONS); + last_sandwich_time = current_time(); + leave_picnic(env.program_info(), env.console, context); + } else { + last_sandwich_time = current_time(); + } } ); + env.console.overlay().add_log("Started Let's Go Paths"); save_if_needed(); // Which path to choose starting at the PokeCenter. @@ -314,11 +328,12 @@ void ShinyHuntScatterbug::run_one_sandwich_iteration(SingleSwitchProgramEnvironm for (;;path_id = (path_id + 1) % num_paths){ if (last_sandwich_time + std::chrono::minutes(30) < current_time()){ env.log("Sandwich expires."); - env.console.overlay().add_log("Sandwich expires", COLOR_WHITE); + env.console.overlay().add_log("Sandwich expires"); return; } env.console.log("Starting Let's Go hunting path...", COLOR_PURPLE); + env.console.overlay().add_log("Let's Go on Path " + std::to_string(path_id)); double hp = hp_watcher.last_known_value() * 100; if (0 < hp){ diff --git a/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.h b/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.h index 76f1b34a9..f98f67b76 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.h +++ b/SerialPrograms/Source/PokemonSV/Programs/ShinyHunting/PokemonSV_ShinyHunt-Scatterbug.h @@ -65,6 +65,7 @@ class ShinyHuntScatterbug : public SingleSwitchProgramInstance{ // Debug options BooleanCheckBoxOption SAVE_DEBUG_VIDEO; BooleanCheckBoxOption DEBUG_WARP_TO_POKECENTER; + BooleanCheckBoxOption SKIP_SANDWICH; EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS;