diff --git a/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp b/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp index f505088994..7569dcab12 100644 --- a/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp +++ b/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.cpp @@ -40,6 +40,22 @@ void TrainerIdReader::make_overlays(VideoOverlaySet &items) const { uint16_t TrainerIdReader::read_tid( Logger& logger, Language language, const ImageViewRGB32& frame +){ + return uint16_t(read_tid_digits(logger, language, frame)); +} + +std::optional TrainerIdReader::try_read_tid( + Logger& logger, Language language, const ImageViewRGB32& frame +){ + const int tid = read_tid_digits(logger, language, frame); + if (tid < 0 || tid > 0xffff){ + return std::nullopt; + } + return uint16_t(tid); +} + +int TrainerIdReader::read_tid_digits( + Logger& logger, Language language, const ImageViewRGB32& frame ){ ImageViewRGB32 game_screen = extract_box_reference(frame, GameSettings::instance().GAME_BOX); @@ -52,7 +68,7 @@ uint16_t TrainerIdReader::read_tid( // waterfill segmentation + template matching // against the PokemonFRLG/Digits/0-9.png templates. - return uint16_t(read_digits_waterfill_template(logger, tid_region, DigitTemplateType::DialogBox)); + return read_digits_waterfill_template(logger, tid_region, DigitTemplateType::DialogBox); } diff --git a/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h b/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h index c763913704..87be48324e 100644 --- a/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h +++ b/SerialPrograms/Source/VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h @@ -34,8 +34,16 @@ class TrainerIdReader { uint16_t read_tid( Logger& logger, Language language, const ImageViewRGB32& frame ); + // As read_tid(), but nothing when the digits can't be read or are more + // than a TID can be, instead of a wrapped-around number. + std::optional try_read_tid( + Logger& logger, Language language, const ImageViewRGB32& frame + ); private: + // The digits read, or -1. + int read_tid_digits(Logger& logger, Language language, const ImageViewRGB32& frame); + Color m_color; ImageFloatBox m_box_tid; ImageFloatBox m_box_tid_spa; diff --git a/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.cpp b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.cpp new file mode 100644 index 0000000000..54af553f9b --- /dev/null +++ b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.cpp @@ -0,0 +1,105 @@ +/* Trainer Card + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedExceptionWithScreenshot.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonFramework/VideoPipeline/VideoOverlay.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "VideoGames/PokemonFRLG/Inference/Menus/PokemonFRLG_TrainerCardDetector.h" +#include "VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h" +#include "PokemonFRLG_StartMenuNavigation.h" +#include "PokemonFRLG_TrainerCard.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +void navigate_to_trainer_card(ConsoleHandle& console, ProControllerContext& context){ + int errors = 0; + while (true){ + if (errors >= 5){ + OperationFailedExceptionWithScreenshot::fire( + ErrorReportMode::SEND_ERROR_REPORT, "navigate_to_trainer_card(): failed 5 times to navigate to the Trainer Card", console + ); + } + + open_start_menu(console, context); + + // the positions are different before recieving a Pokemon and Pokedex + bool cursor = move_cursor_to_position( + console, context, SelectionArrowPositionStartMenu(1) + ); + if (!cursor){ + errors++; + pbf_mash_button(context, BUTTON_B, 2000ms); + continue; + } + + TrainerCardWatcher trainer_card(COLOR_RED); + context.wait_for_all_requests(); + int ret = run_until( + console, context, + [](ProControllerContext& context){ + pbf_press_button(context, BUTTON_A, 200ms, 2300ms); + pbf_press_button(context, BUTTON_A, 200ms, 2300ms); + }, + { trainer_card } + ); + + if (ret < 0){ + console.log("navigate_to_trainer_card(): failed to detect Trainer Card"); + errors++; + pbf_mash_button(context, BUTTON_B, 2000ms); + continue; + } + + break; + } +} + +namespace{ + +template +auto read_on_card(ConsoleHandle& console, ProControllerContext& context, Read read){ + TrainerIdReader reader; + VideoOverlaySet overlays(console.overlay()); + reader.make_overlays(overlays); + + VideoSnapshot screen = console.video().snapshot(); + console.log("Trainer Card detected."); + console.log("Reading TID..."); + auto tid = read(reader, screen); + + context.wait_for_all_requests(); + + return tid; +} + +} + +uint16_t read_trainer_card_tid(ConsoleHandle& console, ProControllerContext& context, Language language){ + uint16_t tid = read_on_card(console, context, [&](TrainerIdReader& reader, const VideoSnapshot& screen){ + return reader.read_tid(console.logger(), language, screen); + }); + console.log("TID: " + std::to_string(tid)); + return tid; +} + +std::optional try_read_trainer_card_tid(ConsoleHandle& console, ProControllerContext& context, Language language){ + std::optional tid = read_on_card(console, context, [&](TrainerIdReader& reader, const VideoSnapshot& screen){ + return reader.try_read_tid(console.logger(), language, screen); + }); + console.log(tid ? "TID: " + std::to_string(*tid) : std::string("TID: not read")); + return tid; +} + + + +} +} +} diff --git a/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h new file mode 100644 index 0000000000..c52c212cba --- /dev/null +++ b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h @@ -0,0 +1,41 @@ +/* Trainer Card + * + * From: https://github.com/PokemonAutomation/ + * + * Opening the Trainer Card from the overworld and reading the TID on it, + * as SID Helper does. Shared with AutoStory, which reads the TID for its + * Starter RNG plan. + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_TrainerCard_H +#define PokemonAutomation_PokemonFRLG_TrainerCard_H + +#include +#include +#include "CommonFramework/Language.h" +#include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" +#include "NintendoSwitch/NintendoSwitch_ConsoleHandle.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + + +// From the overworld, START then the player's entry, until the card shows. +// The entry is second only before the first Pokemon and the Pokedex. +// Throws OperationFailedException after 5 failed tries. +void navigate_to_trainer_card(ConsoleHandle& console, ProControllerContext& context); + +// The TID on the Trainer Card on screen. An unreadable TID comes back as +// TrainerIdReader::read_tid() returns it. +uint16_t read_trainer_card_tid(ConsoleHandle& console, ProControllerContext& context, Language language); +// Nothing when the TID can't be read. +std::optional try_read_trainer_card_tid(ConsoleHandle& console, ProControllerContext& context, Language language); + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp index 0c3ac9bd67..cb79d9241f 100644 --- a/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp +++ b/SerialPrograms/Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp @@ -18,6 +18,7 @@ #include "VideoGames/PokemonFRLG/Inference/PokemonFRLG_TrainerIdReader.h" #include "VideoGames/PokemonFRLG/PokemonFRLG_Navigation.h" #include "VideoGames/PokemonFRLG/Programs/PokemonFRLG_StartMenuNavigation.h" +#include "VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h" #include "PokemonFRLG_RngCalibration.h" #include "PokemonFRLG_SidHelper.h" @@ -168,65 +169,6 @@ void finish_intro_animations(SingleSwitchProgramEnvironment& env, ProControllerC context.wait_for_all_requests(); } -void navigate_to_trainer_card(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - int errors = 0; - while (true){ - if (errors >= 5){ - OperationFailedExceptionWithScreenshot::fire( - ErrorReportMode::SEND_ERROR_REPORT, "SidHelper(): failed 5 times to navigate to the Trainer Card", env.console - ); - } - - open_start_menu(env.console, context); - - // the positions are different before recieving a Pokemon and Pokedex - bool cursor = move_cursor_to_position( - env.console, context, SelectionArrowPositionStartMenu(1) - ); - if (!cursor){ - errors++; - pbf_mash_button(context, BUTTON_B, 2000ms); - continue; - } - - TrainerCardWatcher trainer_card(COLOR_RED); - context.wait_for_all_requests(); - int ret = run_until( - env.console, context, - [](ProControllerContext& context){ - pbf_press_button(context, BUTTON_A, 200ms, 2300ms); - pbf_press_button(context, BUTTON_A, 200ms, 2300ms); - }, - { trainer_card } - ); - - if (ret < 0){ - env.log("SidHelper(): failed to detect Trainer Card"); - errors++; - pbf_mash_button(context, BUTTON_B, 2000ms); - continue; - } - - break; - } -} - -uint16_t read_tid(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Language lang){ - TrainerIdReader reader; - VideoOverlaySet overlays(env.console.overlay()); - reader.make_overlays(overlays); - - VideoSnapshot screen = env.console.video().snapshot(); - env.log("Trainer Card detected."); - env.log("Reading TID..."); - uint16_t tid = reader.read_tid(env.logger(), lang, screen); - env.log("TID: " + std::to_string(tid)); - - context.wait_for_all_requests(); - - return tid; -} - std::vector> get_sid_messages( SingleSwitchProgramEnvironment& env, ProControllerContext& context, @@ -310,9 +252,9 @@ void SidHelper::program(SingleSwitchProgramEnvironment& env, ProControllerContex set_sid_from_name_screen(env, context, SID_DELAY, extra_press_at_end); finish_intro_animations(env, context); - navigate_to_trainer_card(env, context); + navigate_to_trainer_card(env.console, context); - uint16_t tid = read_tid(env, context, lang); + uint16_t tid = read_trainer_card_tid(env.console, context, lang); std::vector> sid_messages = get_sid_messages( env, context, tid, TARGET_ADVANCES, NUM_CANDIDATES diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 1ef2aaff87..ce1ea6f217 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1795,6 +1795,8 @@ file(GLOB LIBRARY_SOURCES Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_SafariOptimalAction.h Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_StartMenuNavigation.cpp Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_StartMenuNavigation.h + Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.cpp + Source/VideoGames/PokemonFRLG/Programs/PokemonFRLG_TrainerCard.h Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.cpp Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.h Source/VideoGames/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_EggRng.cpp