diff --git a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h index 6b4e8335cf..7bd7b6651a 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options.h @@ -59,6 +59,9 @@ 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; } }; 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..be30c7a080 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,28 @@ 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"); + + BossActionRow::on_config_value_changed(nullptr); + + action.add_listener(*this); +} + +void BossActionRow::on_config_value_changed(void* object){ + switch (action){ + case BossAction::CATCH_AND_STOP_PROGRAM: + save_on_the_go = false; + save_on_the_go.set_visibility(ConfigOptionState::DISABLED); + break; + case BossAction::CATCH_AND_STOP_IF_SHINY: + save_on_the_go.set_visibility(ConfigOptionState::ENABLED); + break; + } } @@ -72,7 +94,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..afe5434e31 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Options/PokemonSwSh_MaxLair_Options_BossAction.h @@ -7,6 +7,8 @@ #ifndef PokemonAutomation_PokemonSwSh_MaxLair_Options_BossAction_H #define PokemonAutomation_PokemonSwSh_MaxLair_Options_BossAction_H +#include +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" #include "Common/Cpp/Options/EnumDropdownOption.h" #include "Common/Cpp/Options/StaticTableOption.h" #include "CommonFramework/Options/LabelCellOption.h" @@ -24,19 +26,21 @@ enum class BossAction{ }; -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 ed323a7513..5e148a0fd5 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/PokemonSwSh_MaxLair_BossFinder.cpp @@ -152,6 +152,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..2016ab910f 100644 --- a/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp +++ b/SerialPrograms/Source/PokemonSwSh/MaxLair/Program/PokemonSwSh_MaxLair_Run_Entrance.cpp @@ -4,10 +4,13 @@ * */ -#include "CommonFramework/VideoPipeline/VideoFeed.h" -#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" -#include "CommonTools/Images/SolidColorTest.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" +#include "PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_YCommDetector.h" #include "PokemonSwSh_MaxLair_Run_Entrance.h" namespace PokemonAutomation{ @@ -20,7 +23,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]; @@ -36,22 +39,82 @@ void run_entrance( } } + 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; + } - 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); - } + WhiteDialogBoxWatcher dialog; + SelectionArrowFinder arrow(stream.overlay(), {0.462377, 0.332039, 0.388222, 0.640777}); + YCommIconWatcher overworld; + context.wait_for_all_requests(); + context.wait_for(100ms); + + int ret = wait_until( + stream, context, + std::chrono::seconds(10), + { + dialog, + arrow, + overworld, + } + ); + switch (ret){ + case 0: + stream.log("Detected dialog menu."); + pbf_press_button(context, BUTTON_B, 80ms, 160ms); + continue; + case 1:{ + stream.log("Detected arrow."); + if (!save_path){ + pbf_press_button(context, BUTTON_B, 80ms, 160ms); + continue; + } + const std::vector& arrows = arrow.last_detection(); + if (arrows.empty()){ + continue; + } + if (arrows[0].y > 0.56){ + pbf_press_button(context, BUTTON_A, 80ms, 160ms); + continue; + } - VideoSnapshot screen = stream.video().snapshot(); - ImageStats stats = image_stats(extract_box_reference(screen, box)); - if (!is_grey(stats, 400, 1000)){ - break; + // 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 + ); + } + case 2: + stream.log("Detected overworld."); + return; + default: + throw OperationFailedException( + ErrorReport::SEND_ERROR_REPORT, + "No recognized state after 10 seconds.", + stream + ); } } } 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 );