diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index a3edb13d2..890ae5df7 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -14,6 +14,7 @@ #include "Programs/ShinyHunting/PokemonFRLG_LegendaryReset.h" #include "Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h" #include "Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h" +#include "Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" namespace PokemonAutomation{ @@ -40,6 +41,7 @@ std::vector PanelListFactory::make_panels() const{ if (PreloadSettings::instance().DEVELOPER_MODE){ ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.cpp new file mode 100644 index 000000000..1b47950a2 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.cpp @@ -0,0 +1,243 @@ +/* Shiny Hunt - Overworld + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" +#include "CommonTools/StartupChecks/StartProgramChecks.h" +#include "Pokemon/Pokemon_Strings.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" +#include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" +#include "PokemonFRLG/Inference/Sounds/PokemonFRLG_ShinySoundDetector.h" +#include "PokemonFRLG/PokemonFRLG_Navigation.h" +#include "PokemonFRLG_ShinyHunt-Overworld.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +ShinyHuntOverworld_Descriptor::ShinyHuntOverworld_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonFRLG:OverworldReset", + Pokemon::STRING_POKEMON + " FRLG", "Shiny Hunt - Overworld", + "Programs/PokemonFRLG/OverworldReset.html", + "Shiny hunt Overworld Pokemon.", + ProgramControllerClass::StandardController_NoRestrictions, + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS + ) +{} + +struct ShinyHuntOverworld_Descriptor::Stats : public StatsTracker{ + Stats() + : encounters(m_stats["Encounters"]) + , shinies(m_stats["Shinies"]) + , errors(m_stats["Errors"]) + { + m_display_order.emplace_back("Encounters"); + m_display_order.emplace_back("Shinies"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + std::atomic& encounters; + std::atomic& shinies; + std::atomic& errors; +}; +std::unique_ptr ShinyHuntOverworld_Descriptor::make_stats() const { + return std::unique_ptr(new Stats()); +} + +ShinyHuntOverworld::ShinyHuntOverworld() + : TRIGGER_METHOD( + "Maneuver:
How to trigger an encounter", + { + {TriggerMethod::HORIZONTAL_NO_BIAS, "horizontal-none", "Move left/right. (no bias)"}, + {TriggerMethod::HORIZONTAL_BIAS_LEFT, "horizontal-left", "Move left/right. (bias left)"}, + {TriggerMethod::HORIZONTAL_BIAS_RIGHT, "horizontal-right", "Move left/right. (bias right)"}, + {TriggerMethod::VERTICAL_NO_BIAS, "vertical-none", "Move up/down. (no bias)"}, + {TriggerMethod::VERTICAL_BIAS_UP, "vertical-up", "Move up/down. (bias up)"}, + {TriggerMethod::VERTICAL_BIAS_DOWN, "vertical-down", "Move up/down. (bias down)"} + }, + LockMode::LOCK_WHILE_RUNNING, + TriggerMethod::HORIZONTAL_NO_BIAS + ) + , MOVE_DURATION0( + "Move Duration:
Move in each direction for this long before turning around.", + LockMode::LOCK_WHILE_RUNNING, + "1000 ms" + ) + , TAKE_VIDEO("Take Video:
Record a video when the shiny is found.", LockMode::UNLOCK_WHILE_RUNNING, true) + , GO_HOME_WHEN_DONE(true) + , 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_STATIC(SHINY_REQUIRES_AUDIO); + PA_ADD_OPTION(TRIGGER_METHOD); + PA_ADD_OPTION(MOVE_DURATION0); + PA_ADD_OPTION(TAKE_VIDEO); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void ShinyHuntOverworld::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + ShinyHuntOverworld_Descriptor::Stats& stats = env.current_stats(); + + /* + * Settings: Text Speed fast. Audio required. + * Setup: Stand in grass. + * Lead can't be shiny. + */ + + while (true){ + bool encounter_found = find_encounter(env, context); + + if (!encounter_found){ + stats.errors += 1; + env.update_stats(); + break; + } + + //handle_encounter will wait for "POKEMON appeared!" + bool encounter_shiny = handle_encounter(env.console, context, true); + if (encounter_shiny){ + stats.shinies++; + env.update_stats(); + send_program_notification( + env, + NOTIFICATION_SHINY, + COLOR_YELLOW, + "Shiny found!", + {}, "", + env.console.video().snapshot(), + true + ); + if (TAKE_VIDEO){ + pbf_press_button(context, BUTTON_CAPTURE, 2000ms, 0ms); + } + break; + } + + flee_battle(env.console, context); + context.wait_for_all_requests(); + + stats.encounters++; + env.update_stats(); + send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE); + } + + if (GO_HOME_WHEN_DONE){ + pbf_press_button(context, BUTTON_HOME, 200ms, 1000ms); + } + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + +bool ShinyHuntOverworld::find_encounter(SingleSwitchProgramEnvironment& env, ProControllerContext& context) const +{ + BlackScreenWatcher battle_entered(COLOR_RED, { 0.282, 0.064, 0.448, 0.871 }); + + Milliseconds normal_duration = MOVE_DURATION0; + Milliseconds biased_duration = MOVE_DURATION0.get() + 200ms; + Milliseconds mash_duration = normal_duration - 64ms; + + int ret = 1; + while (ret != 0) + { + switch (TRIGGER_METHOD) { + case TriggerMethod::HORIZONTAL_NO_BIAS: + ret = run_until( + env.console, context, + [normal_duration, mash_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { -1, 0 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { +1, 0 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + case TriggerMethod::HORIZONTAL_BIAS_LEFT: + ret = run_until( + env.console, context, + [normal_duration, mash_duration, biased_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { -1, 0 }, 0ms, biased_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { +1, 0 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + case TriggerMethod::HORIZONTAL_BIAS_RIGHT: + ret = run_until( + env.console, context, + [normal_duration, mash_duration, biased_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { +1, 0 }, 0ms, biased_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { -1, 0 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + case TriggerMethod::VERTICAL_NO_BIAS: + ret = run_until( + env.console, context, + [normal_duration, mash_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { 0, +1 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { 0, -1 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + case TriggerMethod::VERTICAL_BIAS_UP: + ret = run_until( + env.console, context, + [normal_duration, mash_duration, biased_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { 0, +1 }, 0ms, biased_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { 0, -1 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + case TriggerMethod::VERTICAL_BIAS_DOWN: + ret = run_until( + env.console, context, + [normal_duration, mash_duration, biased_duration](ProControllerContext& context) { + ssf_press_left_joystick(context, { 0, -1 }, 0ms, biased_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + ssf_press_left_joystick(context, { 0, +1 }, 0ms, normal_duration); + ssf_mash1_button(context, BUTTON_B, mash_duration); + }, + { battle_entered } + ); + break; + default:; + } + } + + return true; +} + +} +} +} \ No newline at end of file diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h new file mode 100644 index 000000000..90033c870 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h @@ -0,0 +1,69 @@ +/* Shiny Hunt - Overworld + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_ShinyHuntOverworld_H +#define PokemonAutomation_PokemonFRLG_ShinyHuntOverworld_H + +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" +#include "Common/Cpp/Options/EnumDropdownOption.h" +#include "Common/Cpp/Options/SimpleIntegerOption.h" +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "PokemonLA/Options/PokemonLA_ShinyDetectedAction.h" + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +class ShinyHuntOverworld_Descriptor : public SingleSwitchProgramDescriptor{ +public: + ShinyHuntOverworld_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class ShinyHuntOverworld : public SingleSwitchProgramInstance{ +public: + ShinyHuntOverworld(); + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; + + virtual void start_program_border_check( + VideoStream& stream, + FeedbackType feedback_type + ) override { + } + +private: + bool find_encounter(SingleSwitchProgramEnvironment& env, ProControllerContext& context) const; + + enum class TriggerMethod { + HORIZONTAL_NO_BIAS, + HORIZONTAL_BIAS_LEFT, + HORIZONTAL_BIAS_RIGHT, + VERTICAL_NO_BIAS, + VERTICAL_BIAS_UP, + VERTICAL_BIAS_DOWN + }; + + + PokemonLA::ShinyRequiresAudioText SHINY_REQUIRES_AUDIO; + EnumDropdownOption TRIGGER_METHOD; + MillisecondsOption MOVE_DURATION0; + + BooleanCheckBoxOption TAKE_VIDEO; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + +} +} +} +#endif \ No newline at end of file diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index e084f5b64..dc4c4e7ac 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1428,6 +1428,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_LegendaryRunAway.h Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.cpp Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_PrizeCornerReset.h + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.cpp + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_ShinyHunt-Overworld.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h Source/PokemonHome/Inference/PokemonHome_BallReader.cpp