diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 9a25654e6f..5beefac7ec 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -1339,6 +1339,8 @@ file(GLOB MAIN_SOURCES Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp + Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_StarterReset.cpp diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp index 41542b7cc5..c4397e9888 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.cpp @@ -10,6 +10,7 @@ #include "CommonTools/Async/InferenceRoutines.h" #include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" #include "PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.h" #include "PokemonRSE/PokemonRSE_Settings.h" @@ -89,7 +90,7 @@ void flee_battle(VideoStream& stream, ProControllerContext& context) { } } -bool handle_encounter(VideoStream& stream, ProControllerContext& context) { +bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool send_out_lead) { float shiny_coefficient = 1.0; ShinySoundDetector shiny_detector(stream.logger(), [&](float error_coefficient) -> bool{ shiny_coefficient = error_coefficient; @@ -130,27 +131,30 @@ bool handle_encounter(VideoStream& stream, ProControllerContext& context) { } stream.log("Shiny not found."); - //Send out lead, no shiny detection needed. - BattleMenuWatcher battle_menu(COLOR_RED); - stream.log("Sending out lead Pokemon."); - pbf_press_button(context, BUTTON_A, 40, 40); + if (send_out_lead) { + //Send out lead, no shiny detection needed. + BattleMenuWatcher battle_menu(COLOR_RED); + stream.log("Sending out lead Pokemon."); + pbf_press_button(context, BUTTON_A, 40, 40); - int ret = wait_until( - stream, context, - std::chrono::seconds(15), - {{battle_menu}} - ); - if (ret == 0) { - stream.log("Battle menu detecteed!"); - } else { - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "handle_encounter(): Did not detect battle menu.", - stream + int ret = wait_until( + stream, context, + std::chrono::seconds(15), + { {battle_menu} } ); + if (ret == 0) { + stream.log("Battle menu detecteed!"); + } + else { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "handle_encounter(): Did not detect battle menu.", + stream + ); + } + pbf_wait(context, 125); + context.wait_for_all_requests(); } - pbf_wait(context, 125); - context.wait_for_all_requests(); return false; } diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.h b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.h index 5cd73acf92..f614b1ad58 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.h +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Navigation.h @@ -26,8 +26,9 @@ void soft_reset(const ProgramInfo& info, VideoStream& stream, ProControllerConte void flee_battle(VideoStream& stream, ProControllerContext& context); // After press A/walking up to enter a battle, run this handle the battle start and to check if opponent is shiny. -// Use flee_battle or soft_reset after this, depending on game. -bool handle_encounter(VideoStream& stream, ProControllerContext& context); +// Set send_out_lead to true and then use flee_battle() after if game is Emerald. +// For R/S, send_out_lead as false and then soft_reset() to save time. +bool handle_encounter(VideoStream& stream, ProControllerContext& context, bool send_out_lead); } diff --git a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp index d394b7fff3..9d873c8d94 100644 --- a/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp +++ b/SerialPrograms/Source/PokemonRSE/PokemonRSE_Panels.cpp @@ -11,6 +11,7 @@ #include "PokemonRSE_Settings.h" #include "Programs/ShinyHunting/PokemonRSE_AudioStarterReset.h" +#include "Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h" #include "Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h" #include "Programs/ShinyHunting/PokemonRSE_StarterReset.h" @@ -38,6 +39,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Shiny Hunting (Emerald) ----"); + ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp new file mode 100644 index 0000000000..20047cfe34 --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.cpp @@ -0,0 +1,529 @@ +/* Legendary Hunt - Emerald + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Cpp/PrettyPrint.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "Pokemon/Pokemon_Strings.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" +#include "PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h" +#include "PokemonRSE/PokemonRSE_Navigation.h" +#include "PokemonRSE_LegendaryHunt-Emerald.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +LegendaryHuntEmerald_Descriptor::LegendaryHuntEmerald_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonRSE:LegendaryHuntEmerald", + Pokemon::STRING_POKEMON + " RSE", "Legendary Hunt (Emerald)", + "ComputerControl/blob/master/Wiki/Programs/PokemonRSE/LegendaryHuntEmerald.md", + "Use the Run Away method to shiny hunt legendaries in Emerald.", + FeedbackType::VIDEO_AUDIO, + AllowCommandsWhenRunning::DISABLE_COMMANDS, + {SerialPABotBase::OLD_NINTENDO_SWITCH_DEFAULT_REQUIREMENTS} + ) +{} + +struct LegendaryHuntEmerald_Descriptor::Stats : public StatsTracker{ + Stats() + : resets(m_stats["Resets"]) + , shinies(m_stats["Shinies"]) + { + m_display_order.emplace_back("Resets"); + m_display_order.emplace_back("Shinies"); + } + std::atomic& resets; + std::atomic& shinies; +}; +std::unique_ptr LegendaryHuntEmerald_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +LegendaryHuntEmerald::LegendaryHuntEmerald() + : TARGET( + "Target:
", + { + {Target::regis, "regis", "Regirock/Regice/Registeel"}, + {Target::groudon, "groudon", "Groudon"}, + {Target::kyogre, "kyogre", "Kyogre"}, + {Target::hooh, "hooh", "Ho-Oh"}, + {Target::lugia, "lugia", "Lugia"}, + }, + LockMode::LOCK_WHILE_RUNNING, + Target::regis + ) + , NOTIFICATION_SHINY( + "Shiny Found", + true, true, ImageAttachmentMode::JPG, + {"Notifs", "Showcase"} + ) + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_SHINY, + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + }) +{ + PA_ADD_OPTION(TARGET); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void LegendaryHuntEmerald::reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + //turn around, walk down 4/until black screen over + BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 120); + pbf_press_dpad(context, DPAD_DOWN, 120, 20); + pbf_wait(context, 300); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + pbf_wait(context, 50); + context.wait_for_all_requests(); + + //turn around, up one/black screen over + int ret2 = run_until( + env.console, context, + [](ProControllerContext& context){ + pbf_press_dpad(context, DPAD_UP, 120, 20); + pbf_wait(context, 300); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + //walk back up to the regi + ssf_press_button(context, BUTTON_B, 0, 60); + pbf_press_dpad(context, DPAD_UP, 60, 20); + + context.wait_for_all_requests(); +} + +void LegendaryHuntEmerald::reset_groudon(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + //Turn left. Take 10 steps. + ssf_press_button(context, BUTTON_B, 0, 180); + pbf_press_dpad(context, DPAD_LEFT, 180, 20); + + //Turn up. Take 14 steps. (Bump into wall.) + ssf_press_button(context, BUTTON_B, 0, 240); + pbf_press_dpad(context, DPAD_UP, 240, 20); + + //Turn right. Take 2 steps. + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_RIGHT, 40, 20); + + //Turn up. Take 8 steps (Bump into wall.) + ssf_press_button(context, BUTTON_B, 0, 140); + pbf_press_dpad(context, DPAD_UP, 140, 20); + + //Turn left. Take 4 steps. + ssf_press_button(context, BUTTON_B, 0, 80); + pbf_press_dpad(context, DPAD_LEFT, 80, 20); + context.wait_for_all_requests(); + + //Turn down. Exit. Black screen over. + BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_DOWN, 90, 20); + pbf_wait(context, 300); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + //Reverse above steps. + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret2 = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_UP, 90, 20); + pbf_wait(context, 300); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + ssf_press_button(context, BUTTON_B, 0, 80); + pbf_press_dpad(context, DPAD_RIGHT, 80, 20); + + ssf_press_button(context, BUTTON_B, 0, 140); + pbf_press_dpad(context, DPAD_DOWN, 140, 20); + + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_LEFT, 40, 20); + + ssf_press_button(context, BUTTON_B, 0, 240); + pbf_press_dpad(context, DPAD_DOWN, 240, 20); + + ssf_press_button(context, BUTTON_B, 0, 180); + pbf_press_dpad(context, DPAD_RIGHT, 180, 20); + + context.wait_for_all_requests(); +} + +void LegendaryHuntEmerald::reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + //Turn down. Take 1 step. + ssf_press_button(context, BUTTON_B, 0, 20); + pbf_press_dpad(context, DPAD_DOWN, 20, 20); + + //Turn right. Take 9 steps. + ssf_press_button(context, BUTTON_B, 0, 160); + pbf_press_dpad(context, DPAD_RIGHT, 160, 20); + + //Turn up. 13 steps. Wall. + ssf_press_button(context, BUTTON_B, 0, 220); + pbf_press_dpad(context, DPAD_UP, 220, 20); + + //Turn left. 4 steps. Wall. + ssf_press_button(context, BUTTON_B, 0, 80); + pbf_press_dpad(context, DPAD_LEFT, 80, 20); + + //Turn up. 10 steps. + ssf_press_button(context, BUTTON_B, 0, 180); + pbf_press_dpad(context, DPAD_UP, 180, 20); + + //Turn right. 6 steps. + ssf_press_button(context, BUTTON_B, 0, 110); + pbf_press_dpad(context, DPAD_RIGHT, 110, 20); + + //Turn down. Exit. Black screen over. + BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_DOWN, 90, 20); + pbf_wait(context, 300); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + int ret2 = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_UP, 90, 20); + pbf_wait(context, 300); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + ssf_press_button(context, BUTTON_B, 0, 110); + pbf_press_dpad(context, DPAD_LEFT, 110, 20); + + ssf_press_button(context, BUTTON_B, 0, 180); + pbf_press_dpad(context, DPAD_DOWN, 180, 20); + + ssf_press_button(context, BUTTON_B, 0, 80); + pbf_press_dpad(context, DPAD_RIGHT, 80, 20); + + ssf_press_button(context, BUTTON_B, 0, 220); + pbf_press_dpad(context, DPAD_DOWN, 220, 20); + + ssf_press_button(context, BUTTON_B, 0, 160); + pbf_press_dpad(context, DPAD_LEFT, 160, 20); + + ssf_press_button(context, BUTTON_B, 0, 20); + pbf_press_dpad(context, DPAD_UP, 20, 20); + + context.wait_for_all_requests(); +} + +void LegendaryHuntEmerald::reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //Turn around, 10 steps down + ssf_press_button(context, BUTTON_B, 0, 180); + pbf_press_dpad(context, DPAD_DOWN, 180, 20); + + //Turn right, take 1 step. Wait for black screen over. + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 30); + pbf_press_dpad(context, DPAD_RIGHT, 30, 20); + pbf_wait(context, 300); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //turn left, take one step. now turn back right and take a step. wait for black screen over. + int ret2 = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_LEFT, 40, 20); + + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_RIGHT, 40, 20); + pbf_wait(context, 300); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + //reverse above steps, but only take 9 steps up + //doesn't really matter since we want to trigger the encounter anyway + ssf_press_button(context, BUTTON_B, 0, 30); + pbf_press_dpad(context, DPAD_LEFT, 30, 20); + + ssf_press_button(context, BUTTON_B, 0, 170); + pbf_press_dpad(context, DPAD_UP, 170, 20); + + context.wait_for_all_requests(); +} + +void LegendaryHuntEmerald::reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + BlackScreenOverWatcher exit_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //Turn around, 5 steps down + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_DOWN, 90, 20); + + //Turn right, 3 steps right. Wait for black screen over. + int ret = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_RIGHT, 90, 20); + pbf_wait(context, 300); + }, + {exit_area} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to exit area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to exit area.", + env.console + ); + } + else { + env.log("Left area."); + } + + BlackScreenOverWatcher enter_area(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); + //turn up, take one step. then turn back down and take a step. wait for black screen over. + int ret2 = run_until( + env.console, context, + [](ProControllerContext& context){ + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_UP, 40, 20); + + ssf_press_button(context, BUTTON_B, 0, 40); + pbf_press_dpad(context, DPAD_DOWN, 40, 20); + pbf_wait(context, 300); + }, + {enter_area} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Failed to enter area.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to enter area.", + env.console + ); + } + else { + env.log("Entered area."); + } + + //reverse above steps + ssf_press_button(context, BUTTON_B, 0, 70); + pbf_press_dpad(context, DPAD_LEFT, 70, 20); + + ssf_press_button(context, BUTTON_B, 0, 90); + pbf_press_dpad(context, DPAD_UP, 90, 20); + + context.wait_for_all_requests(); +} + +void LegendaryHuntEmerald::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + LegendaryHuntEmerald_Descriptor::Stats& stats = env.current_stats(); + + /* + * Text speed fast, battle animations off + * smoke ball or fast pokemon req. no entry effects. + * + * Don't need to worry about PokeNav or random encounters for any of these targets. + * + * Stand in front of Regis/Ho-Oh/Lugia. Save the game. + */ + + while (true) { + switch (TARGET) { + case Target::hooh: + case Target::kyogre: + case Target::groudon: + //Step forward to start the encounter. + pbf_press_dpad(context, DPAD_UP, 20, 50); + break; + //case Target::groudon: //Step up is easier. + // pbf_press_dpad(context, DPAD_RIGHT, 20, 50); + // break; + //case Target::kyogre: + // pbf_press_dpad(context, DPAD_LEFT, 20, 50); + // break; + } + //handle_encounter presses A already for everything else + + bool legendary_shiny = handle_encounter(env.console, context, true); + if (legendary_shiny) { + stats.shinies++; + env.update_stats(); + send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", env.console.video().snapshot(), true); + break; + } + env.log("No shiny found."); + flee_battle(env.console, context); + + //Close out dialog box + pbf_mash_button(context, BUTTON_B, 250); + context.wait_for_all_requests(); + + //Exit and re-enter the room + switch (TARGET) { + case Target::regis: + reset_regi(env, context); + break; + case Target::groudon: + reset_groudon(env, context); + break; + case Target::kyogre: + reset_kyogre(env, context); + break; + case Target::hooh: + reset_hooh(env, context); + break; + case Target::lugia: + reset_lugia(env, context); + break; + default: + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Invalid target!", + env.console + ); + break; + } + + stats.resets++; + env.update_stats(); + } + + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + +} +} +} diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h new file mode 100644 index 0000000000..81e0854c1f --- /dev/null +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_LegendaryHunt-Emerald.h @@ -0,0 +1,56 @@ +/* Legendary Hunt - Emerald + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonRSE_LegendaryHuntEmerald_H +#define PokemonAutomation_PokemonRSE_LegendaryHuntEmerald_H + +#include "Common/Cpp/Options/SimpleIntegerOption.h" +#include "Common/Cpp/Options/TimeExpressionOption.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonRSE{ + +class LegendaryHuntEmerald_Descriptor : public SingleSwitchProgramDescriptor{ +public: + LegendaryHuntEmerald_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class LegendaryHuntEmerald : public SingleSwitchProgramInstance{ +public: + LegendaryHuntEmerald(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + enum class Target{ + regis, + groudon, + kyogre, + hooh, + lugia, + }; + EnumDropdownOption TARGET; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; + + void reset_regi(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void reset_groudon(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void reset_kyogre(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void reset_hooh(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void reset_lugia(SingleSwitchProgramEnvironment& env, ProControllerContext& context); +}; + +} +} +} +#endif + diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp index 0dd03d8da0..62e3a463ce 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.cpp @@ -50,7 +50,17 @@ std::unique_ptr ShinyHuntDeoxys_Descriptor::make_stats() const{ } ShinyHuntDeoxys::ShinyHuntDeoxys() - : WALK_UP_DOWN_TIME0( + : STARTPOS( + "Starting Position:
", + { + {StartPos::boat, "boat", "Boat/Walk up"}, + {StartPos::rock_unsolved, "rock_unsolved", "Triangle rock, puzzle is unsolved"}, + {StartPos::rock_solved, "rock_solved", "Triangle rock, puzzle is already solved"}, + }, + LockMode::LOCK_WHILE_RUNNING, + StartPos::rock_unsolved + ) + , WALK_UP_DOWN_TIME0( "Walk up/down time
Spend this long to run up to the triangle rock.", LockMode::LOCK_WHILE_RUNNING, "3520 ms" @@ -67,6 +77,7 @@ ShinyHuntDeoxys::ShinyHuntDeoxys() &NOTIFICATION_PROGRAM_FINISH, }) { + PA_ADD_OPTION(STARTPOS); PA_ADD_OPTION(WALK_UP_DOWN_TIME0); PA_ADD_OPTION(NOTIFICATIONS); } @@ -195,19 +206,37 @@ void ShinyHuntDeoxys::program(SingleSwitchProgramEnvironment& env, ProController * Would have to handle both PokeNav and random encounters for ray/others? Why is PokeNav a thing??? */ - while (true) { - env.log("Walking up to Deoxys."); - //Walk up to the triangle rock from the ship. No bike allowed. - ssf_press_button(context, BUTTON_B, 0ms, WALK_UP_DOWN_TIME0); - pbf_press_dpad(context, DPAD_UP, WALK_UP_DOWN_TIME0, 160ms); - context.wait_for_all_requests(); - - solve_puzzle(env, context); + bool first_run = true; - //Start battle. - pbf_press_button(context, BUTTON_A, 20, 40); + while (true) { + if (first_run) { + switch (STARTPOS) { + case StartPos::rock_solved: + env.log("StartPos: Already in position."); + break; + case StartPos::boat: + env.log("StartPos: Walking up to Deoxys."); + //Walk up to the triangle rock from the ship. No bike allowed. + ssf_press_button(context, BUTTON_B, 0ms, WALK_UP_DOWN_TIME0); + pbf_press_dpad(context, DPAD_UP, WALK_UP_DOWN_TIME0, 160ms); + context.wait_for_all_requests(); + case StartPos::rock_unsolved: + env.log("StartPos: Solving puzzle."); + solve_puzzle(env, context); + break; + default: + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Invalid starting position selected.", + env.console + ); + break; + } + first_run = false; + } - bool legendary_shiny = handle_encounter(env.console, context); + //Start battle + bool legendary_shiny = handle_encounter(env.console, context, true); if (legendary_shiny) { stats.shinies++; env.update_stats(); @@ -222,10 +251,20 @@ void ShinyHuntDeoxys::program(SingleSwitchProgramEnvironment& env, ProController context.wait_for_all_requests(); //Walk down from the triangle rock to the ship. + env.log("Walking down to ship."); ssf_press_button(context, BUTTON_B, 0ms, WALK_UP_DOWN_TIME0); pbf_press_dpad(context, DPAD_DOWN, WALK_UP_DOWN_TIME0, 160ms); context.wait_for_all_requests(); + env.log("Walking up to Deoxys rock."); + //Walk up to the triangle rock from the ship. Bike is not allowed on Birth Island. + ssf_press_button(context, BUTTON_B, 0ms, WALK_UP_DOWN_TIME0); + pbf_press_dpad(context, DPAD_UP, WALK_UP_DOWN_TIME0, 160ms); + context.wait_for_all_requests(); + + env.log("Solving puzzle."); + solve_puzzle(env, context); + stats.resets++; env.update_stats(); } diff --git a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h index 8b895898f1..1cbfdeae00 100644 --- a/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h +++ b/SerialPrograms/Source/PokemonRSE/Programs/ShinyHunting/PokemonRSE_ShinyHunt-Deoxys.h @@ -28,6 +28,13 @@ class ShinyHuntDeoxys : public SingleSwitchProgramInstance{ virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; private: + enum class StartPos{ + boat, + rock_unsolved, + rock_solved, + }; + EnumDropdownOption STARTPOS; + MillisecondsOption WALK_UP_DOWN_TIME0; EventNotificationOption NOTIFICATION_SHINY;