diff --git a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp index 3a6ee4064..e6fc6bd06 100644 --- a/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp +++ b/SerialPrograms/Source/PokemonFRLG/PokemonFRLG_Panels.cpp @@ -10,7 +10,7 @@ #include "PokemonFRLG_Settings.h" -#include "Programs/ShinyHunting/PokemonFRLG_StarterReset.h" +#include "Programs/ShinyHunting/PokemonFRLG_GiftReset.h" #include "Programs/TestPrograms/PokemonFRLG_SoundListener.h" namespace PokemonAutomation{ @@ -32,7 +32,7 @@ std::vector PanelListFactory::make_panels() const{ //ret.emplace_back("---- General ----"); ret.emplace_back("---- Shiny Hunting ----"); - ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); if (PreloadSettings::instance().DEVELOPER_MODE){ diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp similarity index 55% rename from SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp rename to SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp index e3c1d32c4..b10b44464 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp @@ -1,4 +1,4 @@ -/* Starter Reset +/* Gift Reset * * From: https://github.com/PokemonAutomation/ * @@ -18,66 +18,79 @@ #include "PokemonFRLG/Inference/Menus/PokemonFRLG_StartMenuDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" #include "PokemonFRLG/PokemonFRLG_Navigation.h" -#include "PokemonFRLG_StarterReset.h" +#include "PokemonFRLG_GiftReset.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonFRLG{ -StarterReset_Descriptor::StarterReset_Descriptor() +GiftReset_Descriptor::GiftReset_Descriptor() : SingleSwitchProgramDescriptor( - "PokemonFRLG:StarterReset", - Pokemon::STRING_POKEMON + " FRLG", "Starter Reset", - "Programs/PokemonFRLG/StarterReset.html", - "Soft reset for a shiny starter.", + "PokemonFRLG:GiftReset", + Pokemon::STRING_POKEMON + " FRLG", "Gift Reset", + "Programs/PokemonFRLG/GiftReset.html", + "Soft reset for a shiny gift Pokemon.", ProgramControllerClass::StandardController_RequiresPrecision, FeedbackType::REQUIRED, AllowCommandsWhenRunning::DISABLE_COMMANDS ) {} -struct StarterReset_Descriptor::Stats : public StatsTracker{ +struct GiftReset_Descriptor::Stats : public StatsTracker{ Stats() : resets(m_stats["Resets"]) - , shinystarter(m_stats["Shiny Starter"]) + , shinies(m_stats["Shinies"]) , errors(m_stats["Errors"]) { m_display_order.emplace_back("Resets"); - m_display_order.emplace_back("Shiny Starter"); + m_display_order.emplace_back("Shinies"); m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); } std::atomic& resets; - std::atomic& shinystarter; + std::atomic& shinies; std::atomic& errors; }; -std::unique_ptr StarterReset_Descriptor::make_stats() const{ +std::unique_ptr GiftReset_Descriptor::make_stats() const{ return std::unique_ptr(new Stats()); } -StarterReset::StarterReset() - : GO_HOME_WHEN_DONE(true) - , NOTIFICATION_SHINY_STARTER( - "Shiny Starter", +GiftReset::GiftReset() + : TARGET( + "Target:
", + { + {Target::starters, "starters", "Bulbasaur / Squirtle / Charmander"}, + {Target::hitmon, "hitmon", "Magikarp, Hitmonlee / Hitmonchan"}, + {Target::eevee, "eevee", "Eevee"}, + {Target::lapras, "lapras", "Lapras"}, + {Target::fossils, "fossils", "Omanyte / Kabuto / Aerodactyl "}, + }, + LockMode::LOCK_WHILE_RUNNING, + Target::starters + ) + , GO_HOME_WHEN_DONE(true) + , NOTIFICATION_SHINY( + "Shiny", true, true, ImageAttachmentMode::JPG, {"Notifs", "Showcase"} ) , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) , NOTIFICATIONS({ - &NOTIFICATION_SHINY_STARTER, + &NOTIFICATION_SHINY, &NOTIFICATION_STATUS_UPDATE, &NOTIFICATION_PROGRAM_FINISH, }) { + PA_ADD_OPTION(TARGET); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); } //Pick up starter, say no to nickname -void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - StarterReset_Descriptor::Stats& stats = env.current_stats(); +void GiftReset::obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + GiftReset_Descriptor::Stats& stats = env.current_stats(); /* - Ah, STARTER is your choice... red adv arrow + Ah, starter is your choice... red adv arrow So you're claiming... YES/NO box rly quite energetic... no arrow player received the starter from... no arrow, jingle must complete @@ -86,7 +99,7 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro rival received the ... no arrow DONE */ - env.log("Obtaining starter."); + env.log("Obtaining Pokemon."); pbf_press_button(context, BUTTON_A, 320ms, 640ms); bool seen_selection_arrow = false; @@ -117,14 +130,24 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro case 1: env.log("Detected Selection Dialog. Pressing A."); if (!seen_selection_arrow) { - env.log("First selection box detected. YES to starter."); - seen_selection_arrow = true; - pbf_press_button(context, BUTTON_A, 320ms, 640ms); - //Skip past energetic and jingle - pbf_press_button(context, BUTTON_B, 320ms, 640ms); - pbf_wait(context, 500ms); - context.wait_for_all_requests(); + if (TARGET == Target::starters || TARGET == Target::hitmon) { + env.log("First selection box detected. YES to starter."); + seen_selection_arrow = true; + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + } else { + env.log("Selection box detected. NO to nickname."); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + context.wait_for_all_requests(); + return; + } + + if (TARGET == Target::starters) { + //Skip past energetic and jingle + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 500ms); + context.wait_for_all_requests(); + } pbf_press_button(context, BUTTON_B, 320ms, 640ms); } else { @@ -132,10 +155,12 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro pbf_press_button(context, BUTTON_B, 320ms, 640ms); //seen_nickname_arrow = true; - //Press B some to try and skip the rival's pickup - pbf_press_button(context, BUTTON_B, 320ms, 640ms); - pbf_press_button(context, BUTTON_B, 320ms, 640ms); - pbf_press_button(context, BUTTON_B, 320ms, 640ms); + if (TARGET == Target::starters) { + //Press B some to try and skip the rival's pickup + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + } context.wait_for_all_requests(); return; } @@ -159,9 +184,29 @@ void StarterReset::obtain_starter(SingleSwitchProgramEnvironment& env, ProContro context.wait_for_all_requests(); } +//Different as lapras has multiple no-red-arrow dialog boxes +void GiftReset::obtain_lapras(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + env.log("Obtaining Lapras."); + pbf_press_button(context, BUTTON_A, 320ms, 640ms); + + //At least 9 lines of dialog/No to nickname for Lapras + //At least 4 lines of dialog for fossils + //This takes care of the entire conversion+nickname+exit dialog + int limit = 10; + if (TARGET == Target::fossils) { + limit = 5; + } + for (int i = 0; i < limit; i++) { + pbf_press_button(context, BUTTON_B, 320ms, 640ms); + pbf_wait(context, 100ms); + context.wait_for_all_requests(); + } + context.wait_for_all_requests(); +} + //After declining to nickname, clear rival pickup and open your starter's summary -void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - StarterReset_Descriptor::Stats& stats = env.current_stats(); +void GiftReset::open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + GiftReset_Descriptor::Stats& stats = env.current_stats(); //From no to nickname to overworld StartMenuWatcher start_menu(COLOR_RED); @@ -169,9 +214,9 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll int ret = run_until( env.console, context, [](ProControllerContext& context) { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 10; i++) { pbf_press_button(context, BUTTON_B, 320ms, 640ms); - pbf_wait(context, 500ms); + pbf_wait(context, 100ms); context.wait_for_all_requests(); pbf_press_button(context, BUTTON_PLUS, 320ms, 640ms); } @@ -184,12 +229,21 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll env.update_stats(); OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, - "open_summary(): Unable to open Start menu.", + "open_summary(): Unable to open Start menu after 10 attempts.", env.console ); } - //No Pokedex yet, so 1 A press to open party menu + if (TARGET != Target::starters) { + //Pokedex, Pokemon, Bag, Trainer, Save, Option, Exit + env.log("Navigating to party menu."); + pbf_wait(context, 200ms); + context.wait_for_all_requests(); + pbf_press_dpad(context, DPAD_DOWN, 320ms, 320ms); + context.wait_for_all_requests(); + } //For starters, no Pokedex yet, do Pokemon is on top and we skip this + + //Open party menu pbf_press_button(context, BUTTON_A, 320ms, 640ms); BlackScreenOverWatcher blk1(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); @@ -210,9 +264,15 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll } context.wait_for_all_requests(); + //Press up twice to get to the last slot + if (TARGET != Target::starters) { + pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); + pbf_press_dpad(context, DPAD_UP, 320ms, 320ms); + } + //Two presses to open summary - pbf_press_button(context, BUTTON_A, 320ms, 640ms); - pbf_press_button(context, BUTTON_A, 320ms, 640ms); + pbf_press_button(context, BUTTON_A, 320ms, 320ms); + pbf_press_button(context, BUTTON_A, 320ms, 320ms); BlackScreenOverWatcher blk2(COLOR_RED, {0.282, 0.064, 0.448, 0.871}); int ret2 = wait_until( @@ -236,20 +296,28 @@ void StarterReset::open_summary(SingleSwitchProgramEnvironment& env, ProControll } -void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ +void GiftReset::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ //StartProgramChecks::check_performance_class_wired_or_wireless(context); - StarterReset_Descriptor::Stats& stats = env.current_stats(); + GiftReset_Descriptor::Stats& stats = env.current_stats(); /* * Settings: Text Speed fast. Default borders. - * Setup: Stand in front of the starter you want. Save the game. + * Setup: 5 pokemon in your party. None for starter. Stand in front of the pokemon. Save the game. + * For non-starters: move menu cursor back to the top (POKEDEX)! + * for starters, pokemon menu will be on top as it is added after picking + * For magikarp: you need money to buy it + * fossils: need to corner the scientist */ bool shiny_starter = false; while (!shiny_starter) { - obtain_starter(env, context); + if (TARGET != Target::lapras && TARGET != Target::fossils) { + obtain_pokemon(env, context); + } else { + obtain_lapras(env, context); + } open_summary(env, context); VideoSnapshot screen = env.console.video().snapshot(); @@ -258,12 +326,12 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon shiny_starter = shiny_checker.read(env.console.logger(), screen); if (shiny_starter) { - env.log("Shiny starter detected!"); - stats.shinystarter++; - send_program_notification(env, NOTIFICATION_SHINY_STARTER, COLOR_YELLOW, "Shiny starter found!", {}, "", screen, true); + env.log("Shiny found!"); + stats.shinies++; + send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", screen, true); break; } else { - env.log("Starter is not shiny."); + env.log("Pokemon is not shiny."); env.log("Soft resetting."); send_program_status_notification( env, NOTIFICATION_STATUS_UPDATE, @@ -271,6 +339,7 @@ void StarterReset::program(SingleSwitchProgramEnvironment& env, ProControllerCon ); soft_reset(env.program_info(), env.console, context); stats.resets++; + env.update_stats(); context.wait_for_all_requests(); } } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h similarity index 58% rename from SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h rename to SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h index 1f6bb3c80..b5baf3278 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h @@ -1,11 +1,11 @@ -/* Starter Reset +/* Gift Reset * * From: https://github.com/PokemonAutomation/ * */ -#ifndef PokemonAutomation_PokemonFRLG_StarterReset_H -#define PokemonAutomation_PokemonFRLG_StarterReset_H +#ifndef PokemonAutomation_PokemonFRLG_GiftReset_H +#define PokemonAutomation_PokemonFRLG_GiftReset_H #include "CommonFramework/Notifications/EventNotificationsTable.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" @@ -15,16 +15,16 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonFRLG{ -class StarterReset_Descriptor : public SingleSwitchProgramDescriptor{ +class GiftReset_Descriptor : public SingleSwitchProgramDescriptor{ public: - StarterReset_Descriptor(); + GiftReset_Descriptor(); struct Stats; virtual std::unique_ptr make_stats() const override; }; -class StarterReset : public SingleSwitchProgramInstance{ +class GiftReset : public SingleSwitchProgramInstance{ public: - StarterReset(); + GiftReset(); virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext &context) override; virtual void start_program_border_check( @@ -33,12 +33,22 @@ class StarterReset : public SingleSwitchProgramInstance{ ) override{} private: - void obtain_starter(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void obtain_pokemon(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void obtain_lapras(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void open_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + enum class Target{ + starters, + hitmon, + eevee, + lapras, + fossils, + }; + EnumDropdownOption TARGET; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; - EventNotificationOption NOTIFICATION_SHINY_STARTER; + EventNotificationOption NOTIFICATION_SHINY; EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS; }; diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index f0a23fa77..a47c8fd81 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1413,8 +1413,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/PokemonFRLG_Panels.h Source/PokemonFRLG/PokemonFRLG_Settings.cpp Source/PokemonFRLG/PokemonFRLG_Settings.h - Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.cpp - Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_StarterReset.h + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.cpp + Source/PokemonFRLG/Programs/ShinyHunting/PokemonFRLG_GiftReset.h Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.cpp Source/PokemonFRLG/Programs/TestPrograms/PokemonFRLG_SoundListener.h Source/PokemonHome/Inference/PokemonHome_BallReader.cpp