diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h index 6b4e8335cf..fc3696686d 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h @@ -59,6 +59,12 @@ class EndBattleDecider{ const PathStats& path_stats, bool any_shiny, bool boss_is_shiny ) const = 0; + + // For BossFinder: whether the boss is in the "save on the go" list. + virtual bool is_in_save_list(const std::string& boss_slug) const { return false; } + + // For Standard/StrongBoss: whether to keep a followed path when prompted. + //virtual bool should_keep_followed_path() const { return false; } }; diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.cpp b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.cpp index 61d467e4c6..60f454731c 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.cpp +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.cpp @@ -4,10 +4,14 @@ * */ +#include +#include //#include "Common/Compiler.h" //#include "Common/Cpp/Json/JsonValue.h" //#include "Common/Cpp/Json/JsonArray.h" //#include "Common/Cpp/Json/JsonObject.h" +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "Common/Cpp/Options/ConfigOption.h" //#include "CommonFramework/Globals.h" #include "Pokemon/Pokemon_Strings.h" #include "Pokemon/Resources/Pokemon_PokemonNames.h" @@ -49,10 +53,27 @@ BossActionRow::BossActionRow(std::string slug, const std::string& name_slug, con BossAction::CATCH_AND_STOP_IF_SHINY ) , ball(LockMode::UNLOCK_WHILE_RUNNING, "poke-ball") + , save_on_the_go(LockMode::UNLOCK_WHILE_RUNNING, false) { PA_ADD_STATIC(pokemon); add_option(action, "Action"); add_option(ball, "Ball"); + add_option(save_on_the_go, "Save Path"); + + save_on_the_go.set_visibility( + action == BossAction::CATCH_AND_STOP_IF_SHINY ? ConfigOptionState::ENABLED : ConfigOptionState::DISABLED + ); + + action.add_listener(*this); +} + +void BossActionRow::on_config_value_changed(void* object) { + if (action != BossAction::CATCH_AND_STOP_IF_SHINY) { + save_on_the_go = false; + } + save_on_the_go.set_visibility( + action == BossAction::CATCH_AND_STOP_IF_SHINY ? ConfigOptionState::ENABLED : ConfigOptionState::DISABLED + ); } @@ -72,7 +93,8 @@ std::vector BossActionTable::make_header() const{ std::vector ret{ STRING_POKEMON, "Action", - STRING_POKEBALL + STRING_POKEBALL, + "Save Path" }; return ret; } diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h index 3e228c5089..51869ca27b 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h @@ -11,6 +11,10 @@ #include "Common/Cpp/Options/StaticTableOption.h" #include "CommonFramework/Options/LabelCellOption.h" #include "PokemonSwSh/Options/PokemonSwSh_BallSelectOption.h" +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "Common/Cpp/Options/ConfigOption.h" +#include +#include namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -23,20 +27,23 @@ enum class BossAction{ CATCH_AND_STOP_IF_SHINY, }; +const EnumDropdownDatabase& BossAction_Database(); -class BossActionRow : public StaticTableRow{ +class BossActionRow : public StaticTableRow, private ConfigOption::Listener{ public: BossActionRow(std::string slug, const std::string& name_slug, const std::string& sprite_slug); + virtual void on_config_value_changed(void* object) override; LabelCellOption pokemon; EnumDropdownCell action; PokemonBallSelectCell ball; + BooleanCheckBoxCell save_on_the_go; }; class BossActionTable : public StaticTableOption{ public: BossActionTable(); - virtual std::vector make_header() const; + virtual std::vector make_header() const override; }; diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp b/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp index 1b503f2437..6a80a6de1f 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp @@ -151,6 +151,9 @@ class EndBattleDecider_BossFinder : public EndBattleDecider{ } throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Invalid enum."); } + virtual bool is_in_save_list(const std::string& boss_slug) const override { + return get_filter(boss_slug).save_on_the_go; + } private: diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp b/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp index a86a977f62..0e8e69eebd 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp @@ -8,7 +8,12 @@ #include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" #include "CommonTools/Images/SolidColorTest.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Inference/Pokemon_NameReader.h" +#include "Pokemon/Resources/Pokemon_PokemonNames.h" +#include "PokemonSwSh/MaxLair/Inference/PokemonSwSh_MaxLair_Detect_PokemonReader.h" #include "PokemonSwSh_MaxLair_Run_Entrance.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -20,7 +25,7 @@ void run_entrance( AdventureRuntime& runtime, ProgramEnvironment& env, size_t console_index, VideoStream& stream, ProControllerContext& context, - bool save_path, + bool followed_path, GlobalStateTracker& state_tracker ){ GlobalState& state = state_tracker[console_index]; @@ -35,24 +40,98 @@ void run_entrance( runtime.path_stats.clear(); } } - - - OverlayBoxScope box(stream.overlay(), {0.782, 0.850, 0.030, 0.050}); - - pbf_wait(context, 2000ms); - while (true){ - if (save_path){ - pbf_press_button(context, BUTTON_A, 160ms, 1000ms); - }else{ - pbf_press_button(context, BUTTON_B, 160ms, 1000ms); + + + context.wait_for(1000ms); + + // Get the boss slug + std::string boss_slug; + if (runtime.host_index < runtime.console_settings.active_consoles()) { + boss_slug = state_tracker.infer_actual_state(runtime.host_index).boss; + }; + + bool save_path = false; + + if (!followed_path) { + // Check if the user checked the box to save the path when running the BossFinder program + + if (!boss_slug.empty()) { + save_path = runtime.actions.is_in_save_list(boss_slug); + stream.log("Boss: " + boss_slug + ", should save: " + (save_path ? "Yes" : "No"), COLOR_BLUE); + }; + } else { + save_path = followed_path; + } + + // Overlay box to detect when a dialogue box, a Yes/No option to save a path or erasing a path if our list is full is present + OverlayBoxScope dialog_box(stream.overlay(), {0.78, 0.85, 0.03, 0.05}); + OverlayBoxScope yes_no_box(stream.overlay(), {0.68, 0.75, 0.135, 0.02}); + OverlayBoxScope paths_box(stream.overlay(), {0.685, 0.515, 0.13, 0.013}); + + // Timeout: 5 minutes + auto start_time = std::chrono::steady_clock::now(); + const auto timeout = std::chrono::minutes(5); + + while(true) { + auto now = std::chrono::steady_clock::now(); + if (now - start_time > timeout) { + stream.log("Entrance dialogue timed out after 5 minutes.", COLOR_RED); + throw OperationFailedException(ErrorReport::SEND_ERROR_REPORT, "Entrance dialogue timed out.", stream); } + context.wait_for_all_requests(); + context.wait_for(1000ms); VideoSnapshot screen = stream.video().snapshot(); - ImageStats stats = image_stats(extract_box_reference(screen, box)); - if (!is_grey(stats, 400, 1000)){ - break; - } + if (!screen) continue; + + ImageStats dialog_box_stats = image_stats(extract_box_reference(screen, dialog_box)); + + ImageStats yes_no_box_stats = image_stats(extract_box_reference(screen, yes_no_box)); + + ImageStats paths_box_stats = image_stats(extract_box_reference(screen, paths_box)); + + bool dialog_box_present = is_grey(dialog_box_stats, 400, 1000); + bool yes_no_box_present = is_white(yes_no_box_stats, 400, 10); + bool paths_box_present = is_white(paths_box_stats, 400, 10); + + if (paths_box_present && yes_no_box_present) { + + if (save_path) { + // List of bosses is full, stop the program + stream.log("Cannot save path – saved list is full. Stopping program.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::NO_ERROR_REPORT, + "Paths list is full. Program stopped.", + stream + ); + + } else { + stream.log("Not saving path"); + pbf_press_button(context, BUTTON_B, 160ms, 1000ms); + }; + + } else if (!paths_box_present && yes_no_box_present) { + + if (save_path) { + if (followed_path) { + stream.log("Keeping old path."); + pbf_press_button(context, BUTTON_A, 160ms, 1000ms); + } else { + std::string display_name = get_pokemon_name(boss_slug).display_name(); + stream.log("Saving new path"); + pbf_press_button(context, BUTTON_A, 160ms, 1000ms); + send_program_notification(env, runtime.notification_status, COLOR_BLUE, "Path Saved", {{"Boss: ", display_name}}, ""); + } + } else { + stream.log("Not saving new path"); + pbf_press_button(context, BUTTON_B, 160ms, 1000ms); + } + } else if (!dialog_box_present) { + return; + }; + + pbf_press_button(context, BUTTON_A, 160ms, 1000ms); } } diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.h b/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.h index 14c0d8a90c..cd9ae7c3ef 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.h +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.h @@ -22,7 +22,7 @@ void run_entrance( AdventureRuntime& runtime, ProgramEnvironment& env, size_t console_index, VideoStream& stream, ProControllerContext& context, - bool save_path, + bool followed_path, GlobalStateTracker& state_tracker );