diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index fca234ed97..c52b918b2d 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -10,6 +10,7 @@ #include "PokemonLZA_Settings.h" +#include "Programs/PokemonLZA_BeldumHunter.h" #include "Programs/PokemonLZA_ClothingBuyer.h" #include "Programs/PokemonLZA_RestaurantFarmer.h" #include "Programs/PokemonLZA_ShinyHunt_Bench.h" @@ -38,6 +39,7 @@ std::vector PanelListFactory::make_panels() const{ if (PreloadSettings::instance().DEVELOPER_MODE){ ret.emplace_back("---- Shiny Hunting ----"); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); } diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.cpp index f37681a28a..74e6345838 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.cpp @@ -28,10 +28,10 @@ GameSettings::GameSettings() LockMode::LOCK_WHILE_RUNNING, "40 s" ) - , ENTER_GAME_MASH( + , ENTER_GAME_MASH0( "Enter Game Mash:
Mash A for this long to enter the game.", LockMode::LOCK_WHILE_RUNNING, - "5000 ms" + "3000 ms" ) , ENTER_GAME_WAIT( "Enter Game Wait:
Wait this long for the game to enter the overworld.", @@ -46,7 +46,7 @@ GameSettings::GameSettings() PA_ADD_STATIC(m_start_game_timings); PA_ADD_OPTION(START_GAME_WAIT); - PA_ADD_OPTION(ENTER_GAME_MASH); + PA_ADD_OPTION(ENTER_GAME_MASH0); PA_ADD_OPTION(ENTER_GAME_WAIT); PA_ADD_STATIC(m_advanced_options); diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.h b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.h index 29817d5198..dac97374d5 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.h +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Settings.h @@ -26,7 +26,7 @@ class GameSettings : public BatchOption{ SectionDividerOption m_start_game_timings; MillisecondsOption START_GAME_WAIT; - MillisecondsOption ENTER_GAME_MASH; + MillisecondsOption ENTER_GAME_MASH0; MillisecondsOption ENTER_GAME_WAIT; SectionDividerOption m_advanced_options; diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.cpp new file mode 100644 index 0000000000..2f71c7542d --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.cpp @@ -0,0 +1,210 @@ +/* Beldum Hunter + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#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 "Pokemon/Pokemon_Strings.h" +#include "PokemonLZA/PokemonLZA_Settings.h" +#include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h" +#include "PokemonLZA/Programs/PokemonLZA_GameEntry.h" +#include "PokemonLA/Inference/Sounds/PokemonLA_ShinySoundDetector.h" +#include "PokemonLZA_BeldumHunter.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + +using namespace Pokemon; + +BeldumHunter_Descriptor::BeldumHunter_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonLZA:ShinyHunt-Beldum", + STRING_POKEMON + " LZA", "Shiny Hunt - Beldum", + "Programs/PokemonLZA/BeldumHunter.html", + "Repeatedly enter Lysandre Labs to shiny hunt Beldum.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::VIDEO_AUDIO, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} +class BeldumHunter_Descriptor::Stats : public StatsTracker, public PokemonLA::ShinyStatIncrementer{ +public: + Stats() + : attempts(m_stats["Attempts"]) + , errors(m_stats["Errors"]) + , shinies(m_stats["Shinies"]) + { + m_display_order.emplace_back("Attempts"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + m_display_order.emplace_back("Shinies"); + } + virtual void add_shiny() override{ + shinies++; + } + + std::atomic& attempts; + std::atomic& errors; + std::atomic& shinies; +}; +std::unique_ptr BeldumHunter_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + + +BeldumHunter::BeldumHunter() + : TAKE_VIDEO("Take Video:", LockMode::UNLOCK_WHILE_RUNNING, true) + , NOTIFICATION_SHINY( + "Shiny Found", + true, true, ImageAttachmentMode::JPG, + {"Notifs", "Showcase"} + ) + , NOTIFICATION_STATUS("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_SHINY, + &NOTIFICATION_STATUS, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_RECOVERABLE, + &NOTIFICATION_ERROR_FATAL, + }) +{ + PA_ADD_STATIC(SHINY_REQUIRES_AUDIO); + PA_ADD_OPTION(TAKE_VIDEO); + PA_ADD_OPTION(NOTIFICATIONS); +} + +bool BeldumHunter::run_iteration(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + BeldumHunter_Descriptor::Stats& stats = env.current_stats(); + stats.attempts++; + { + float shiny_coefficient = 1.0; + PokemonLA::ShinySoundDetector shiny_detector(env.logger(), [&](float error_coefficient) -> bool { + shiny_coefficient = error_coefficient; + return true; + }); + + BlackScreenOverWatcher entered(COLOR_RED, { 0.074, 0.044, 0.826, 0.278 }); + + env.log("Entering the lab."); + context.wait_for_all_requests(); + int ret = run_until( + env.console, context, + [&](ProControllerContext& context) { + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 128, 0, 128, 128, 100); + pbf_wait(context, 5000ms); + }, + { {entered} } + ); + if (ret == 0) { + env.log("Entered the lab."); + } else { + env.log("Failed to enter the lab."); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to warp.", + env.console + ); + } + pbf_wait(context, 1000ms); + context.wait_for_all_requests(); + + int res = run_until( + env.console, context, + [&](ProControllerContext& context) { + + env.log("Go straight toward the elevator."); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 128, 0, 128, 128, 460); + + env.log("Go left to where the Noivern spawns, then forward and then left."); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 0, 128, 128, 128, 300); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 128, 0, 128, 128, 80); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 0, 128, 128, 128, 180); + + env.log("Through the Houndoom room and down the hallway."); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 128, 255, 128, 128, 140); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 0, 128, 128, 128, 200); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 128, 0, 128, 128, 340); + + env.log("Final hallway to Beldum room."); + pbf_controller_state(context, BUTTON_B, DPAD_NONE, 0, 128, 128, 128, 400); + pbf_press_button(context, BUTTON_L, 40ms, 40ms); + context.wait_for_all_requests(); + }, + { {shiny_detector} } + ); + shiny_detector.throw_if_no_sound(); + + if (res == 0) { + env.log("Shiny detected!"); + if (TAKE_VIDEO) { + pbf_press_button(context, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 0); + } + + return true; + } + } + env.console.log("No shiny detected. Resetting."); + + pbf_press_button(context, BUTTON_HOME, 160ms, 3000ms); + reset_game_from_home(env, env.console, context, false); + + return false; +} + + +void BeldumHunter::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + BeldumHunter_Descriptor::Stats& stats = env.current_stats(); + + /* + * Setup: Face the opening to the labs. Save the game. + * + * Program will enter the Labs and then run to the Beldum room. + * No shiny, reset the game. Repeat. + * Every time we enter the Beldum will reroll. We reset the game instead of running back + * due to the wild Pokemon spawns. + * This can also hunt Noivern, Houndour, and Houndoom. + */ + + while (true){ + env.update_stats(); + send_program_status_notification(env, NOTIFICATION_STATUS); + try{ + bool shiny_found = run_iteration(env, context); + if (shiny_found) { + stats.shinies++; + env.update_stats(); + + pbf_press_button(context, BUTTON_HOME, 160ms, 3000ms); + + send_program_notification(env, NOTIFICATION_SHINY, + COLOR_YELLOW, "Shiny sound detected!", {}, + "", env.console.video().snapshot(), true); + break; + } + }catch (OperationFailedException& e){ + stats.errors++; + e.send_notification(env, NOTIFICATION_ERROR_RECOVERABLE); + + pbf_press_button(context, BUTTON_HOME, 160ms, 3000ms); + reset_game_from_home(env, env.console, context, false); + } + } + + env.update_stats(); + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.h new file mode 100644 index 0000000000..efb7e61482 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.h @@ -0,0 +1,51 @@ +/* Beldum Hunter + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonLZA_BeldumHunter_H +#define PokemonAutomation_PokemonLZA_BeldumHunter_H + +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "PokemonLA/Options/PokemonLA_ShinyDetectedAction.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLZA{ + + +class BeldumHunter_Descriptor : public SingleSwitchProgramDescriptor{ +public: + BeldumHunter_Descriptor(); + + class Stats; + virtual std::unique_ptr make_stats() const override; +}; + + +class BeldumHunter : public SingleSwitchProgramInstance{ +public: + BeldumHunter(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + bool run_iteration(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + +private: + PokemonLA::ShinyRequiresAudioText SHINY_REQUIRES_AUDIO; + + BooleanCheckBoxOption TAKE_VIDEO; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS; + EventNotificationsOption NOTIFICATIONS; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp index 330f1d522b..2f338a9945 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp @@ -4,7 +4,13 @@ * */ +#include "CommonFramework/Tools/ErrorDumper.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" #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 "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" #include "PokemonLZA/PokemonLZA_Settings.h" #include "PokemonLZA_GameEntry.h" @@ -12,16 +18,81 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonLZA{ +bool reset_game_to_gamemenu( + ConsoleHandle& console, ProControllerContext& context +){ + from_home_close_and_reopen_game(console, context, true); + + // Now the game has opened: + return openedgame_to_gamemenu(console, context, GameSettings::instance().START_GAME_WAIT); +} + +// From the game menu screen (where "Press A" is displayed to enter the game), +// mash A to enter the game and wait until the black screen is gone. +bool gamemenu_to_ingame( + VideoStream& stream, ProControllerContext& context, + Milliseconds mash_duration, Milliseconds enter_game_timeout +){ + stream.log("Mashing A to enter game..."); + BlackScreenOverWatcher detector(COLOR_RED, {0.074, 0.044, 0.826, 0.278}); + pbf_mash_button(context, BUTTON_A, mash_duration); + context.wait_for_all_requests(); + stream.log("Waiting to enter game..."); + int ret = wait_until( + stream, context, + std::chrono::milliseconds(enter_game_timeout), + {{detector}} + ); + if (ret == 0){ + stream.log("Entered game!"); + return true; + }else{ + stream.log("Timed out waiting to enter game.", COLOR_RED); + return false; + } +} + +bool reset_game_from_home( + ProgramEnvironment& env, + ConsoleHandle& console, ProControllerContext& context, + bool backup_save, + Milliseconds enter_game_mash, + Milliseconds enter_game_timeout, + Milliseconds post_wait_time +){ + bool ok = true; + ok &= reset_game_to_gamemenu(console, context); + + if (backup_save){ + console.log("Loading backup save!"); + pbf_wait(context, 1000ms); + ssf_press_dpad(context, DPAD_UP, 0ms, 200ms); + ssf_press_button(context, BUTTON_B | BUTTON_X, 1000ms, 200ms); + } + + ok &= gamemenu_to_ingame( + console, context, + enter_game_mash, + enter_game_timeout + ); + if (!ok){ + dump_image(console.logger(), env.program_info(), console.video(), "StartGame"); + } + console.log("Entered game! Waiting out grace period."); + pbf_wait(context, post_wait_time); + context.wait_for_all_requests(); + return ok; +} bool reset_game_from_home( ProgramEnvironment& env, ConsoleHandle& console, ProControllerContext& context, bool backup_save, Milliseconds post_wait_time ){ - return PokemonLA::reset_game_from_home( + return reset_game_from_home( env, console, context, backup_save, - GameSettings::instance().ENTER_GAME_MASH, + GameSettings::instance().ENTER_GAME_MASH0, GameSettings::instance().ENTER_GAME_WAIT, post_wait_time ); diff --git a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.h b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.h index e0eecf635a..cb4bad6196 100644 --- a/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.h +++ b/SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_GameEntry.h @@ -9,7 +9,6 @@ #include "NintendoSwitch/Controllers/NintendoSwitch_ProController.h" #include "NintendoSwitch/NintendoSwitch_ConsoleHandle.h" -#include "PokemonLA/Programs/PokemonLA_GameEntry.h" namespace PokemonAutomation{ class ProgramEnvironment; @@ -22,11 +21,9 @@ using namespace std::chrono_literals; // From Switch Home menu, reset game and wait until the game menu screen (where // "Press A" is displayed to enter the game) is shown. -inline bool reset_game_to_gamemenu( +bool reset_game_to_gamemenu( ConsoleHandle& console, ProControllerContext& context -){ - return PokemonLA::reset_game_to_gamemenu(console, context); -} +); // From Switch Home menu, start game and wait until the player character // appears in game. @@ -36,6 +33,14 @@ bool reset_game_from_home( ProgramEnvironment& env, ConsoleHandle& console, ProControllerContext& context, bool backup_save, + Milliseconds enter_game_mash, + Milliseconds enter_game_timeout, + Milliseconds post_wait_time +); +bool reset_game_from_home( + ProgramEnvironment& env, + ConsoleHandle& console, ProControllerContext& context, + bool backup_save = false, Milliseconds post_wait_time = 1000ms ); diff --git a/SerialPrograms/SourceFiles.cmake b/SerialPrograms/SourceFiles.cmake index 70970f9aac..bff02c3609 100644 --- a/SerialPrograms/SourceFiles.cmake +++ b/SerialPrograms/SourceFiles.cmake @@ -1553,6 +1553,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonLZA/PokemonLZA_Settings.h Source/PokemonLZA/Programs/PokemonLZA_BasicNavigation.cpp Source/PokemonLZA/Programs/PokemonLZA_BasicNavigation.h + Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.cpp + Source/PokemonLZA/Programs/PokemonLZA_BeldumHunter.h Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.cpp Source/PokemonLZA/Programs/PokemonLZA_ClothingBuyer.h Source/PokemonLZA/Programs/PokemonLZA_GameEntry.cpp