From e747f1e8d98f6d974e0a62411d344a86cec89092 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 7 Mar 2025 12:12:09 -0500 Subject: [PATCH 1/5] LGPE setup --- SerialPrograms/CMakeLists.txt | 2 + SerialPrograms/SerialPrograms.pro | 2 + SerialPrograms/Source/PanelLists.cpp | 2 + .../Source/PokemonLGPE/PokemonLGPE_Panels.cpp | 40 +++++++++++++++++++ .../Source/PokemonLGPE/PokemonLGPE_Panels.h | 27 +++++++++++++ 5 files changed, 73 insertions(+) create mode 100644 SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp create mode 100644 SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.h diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 3f0bef0a4a..9290e55fd9 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -1360,6 +1360,8 @@ file(GLOB MAIN_SOURCES Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h + Source/PokemonLGPE/PokemonLGPE_Panels.cpp + Source/PokemonLGPE/PokemonLGPE_Panels.h Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index f66a059d25..81dd1eb2cd 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -667,6 +667,7 @@ SOURCES += \ Source/PokemonLA/Resources/PokemonLA_NameDatabase.cpp \ Source/PokemonLA/Resources/PokemonLA_PokemonSprites.cpp \ Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp \ + Source/PokemonLGPE/PokemonLGPE_Panels.cpp \ Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp \ Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp \ Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.cpp \ @@ -1846,6 +1847,7 @@ HEADERS += \ Source/PokemonLA/Resources/PokemonLA_NameDatabase.h \ Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h \ Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h \ + Source/PokemonLGPE/PokemonLGPE_Panels.h \ Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h \ Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h \ Source/PokemonRSE/Inference/Sounds/PokemonRSE_ShinySoundDetector.h \ diff --git a/SerialPrograms/Source/PanelLists.cpp b/SerialPrograms/Source/PanelLists.cpp index de6fafbf13..1a4eae1de5 100644 --- a/SerialPrograms/Source/PanelLists.cpp +++ b/SerialPrograms/Source/PanelLists.cpp @@ -18,6 +18,7 @@ #include "PokemonHome/PokemonHome_Panels.h" #include "PokemonBDSP/PokemonBDSP_Panels.h" #include "PokemonLA/PokemonLA_Panels.h" +#include "PokemonLGPE/PokemonLGPE_Panels.h" #include "PokemonRSE/PokemonRSE_Panels.h" #include "PokemonSV/PokemonSV_Panels.h" #include "ZeldaTotK/ZeldaTotK_Panels.h" @@ -51,6 +52,7 @@ ProgramSelect::ProgramSelect(QWidget& parent, PanelHolder& holder) add(std::make_unique()); if (PreloadSettings::instance().DEVELOPER_MODE) { add(std::make_unique()); + add(std::make_unique()); } add(std::make_unique()); diff --git a/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp new file mode 100644 index 0000000000..af1a15a7fe --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp @@ -0,0 +1,40 @@ +/* Pokemon LGPE Panels + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/GlobalSettingsPanel.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonLGPE_Panels.h" + + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +PanelListFactory::PanelListFactory() + : PanelListDescriptor(Pokemon::STRING_POKEMON + " Let's Go Pikachu and Eevee") +{} + +std::vector PanelListFactory::make_panels() const{ + std::vector ret; + + ret.emplace_back("---- Settings ----"); + //ret.emplace_back(make_settings()); + + //ret.emplace_back("---- General ----"); + + ret.emplace_back("---- Shiny Hunting ----"); + //ret.emplace_back(make_single_switch_program()); + + return ret; +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.h b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.h new file mode 100644 index 0000000000..20bed0a1d2 --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.h @@ -0,0 +1,27 @@ +/* Pokemon LGPE Panels + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonLGPE_Panels_H +#define PokemonAutomation_PokemonLGPE_Panels_H + +#include "CommonFramework/Panels/PanelList.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +class PanelListFactory : public PanelListDescriptor{ +public: + PanelListFactory(); + virtual std::vector make_panels() const; +}; + + + +} +} +} +#endif From 058d45397fca79865dadb798a8ae5d85472384ba Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 7 Mar 2025 15:46:22 -0500 Subject: [PATCH 2/5] alolan trade work, shiny symbol detector work --- SerialPrograms/CMakeLists.txt | 4 + SerialPrograms/SerialPrograms.pro | 4 + .../PokemonLGPE_ShinySymbolDetector.cpp | 64 +++++ .../PokemonLGPE_ShinySymbolDetector.h | 36 +++ .../Source/PokemonLGPE/PokemonLGPE_Panels.cpp | 4 +- .../Programs/PokemonLGPE_AlolanTrade.cpp | 225 ++++++++++++++++++ .../Programs/PokemonLGPE_AlolanTrade.h | 52 ++++ 7 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp create mode 100644 SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h create mode 100644 SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp create mode 100644 SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 9290e55fd9..ee1f975e9c 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -1360,6 +1360,10 @@ file(GLOB MAIN_SOURCES Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h + Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp + Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h + Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp + Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h Source/PokemonLGPE/PokemonLGPE_Panels.cpp Source/PokemonLGPE/PokemonLGPE_Panels.h Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index 81dd1eb2cd..5ab09656f8 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -667,6 +667,8 @@ SOURCES += \ Source/PokemonLA/Resources/PokemonLA_NameDatabase.cpp \ Source/PokemonLA/Resources/PokemonLA_PokemonSprites.cpp \ Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp \ + Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp \ + Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp \ Source/PokemonLGPE/PokemonLGPE_Panels.cpp \ Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp \ Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp \ @@ -1847,6 +1849,8 @@ HEADERS += \ Source/PokemonLA/Resources/PokemonLA_NameDatabase.h \ Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h \ Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h \ + Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h \ + Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h \ Source/PokemonLGPE/PokemonLGPE_Panels.h \ Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h \ Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h \ diff --git a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp new file mode 100644 index 0000000000..7f0f896888 --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp @@ -0,0 +1,64 @@ +/* Shiny Symbol Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Cpp/Color.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTypes/ImageRGB32.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "CommonTools/Images/ImageFilter.h" +#include "PokemonLGPE_ShinySymbolDetector.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +ShinySymbolDetector::ShinySymbolDetector(Color color) + : m_box_star(0.204, 0.095, 0.033, 0.053) +{} +void ShinySymbolDetector::make_overlays(VideoOverlaySet& items) const{ + items.add(COLOR_RED, m_box_star); +} +bool ShinySymbolDetector::read(Logger& logger, const ImageViewRGB32& frame){ + const bool replace_color_within_range = true; + + //Filter out background + ImageRGB32 filtered_region = filter_rgb32_range( + extract_box_reference(frame, m_box_star), + combine_rgb(138, 97, 221), combine_rgb(200, 181, 239), Color(0), replace_color_within_range + ); + ImageStats stats = image_stats(filtered_region); + + /* + filtered_region.save("./filtered_only.png"); + cout << stats.average.r << endl; + cout << stats.average.g << endl; + cout << stats.average.b << endl; + */ + + /* + Shiny: + R: 196.632, G: 196.771, B: 145.863 + Not shiny: + R: 181.862, G: 180.686, B: 193.999 + */ + + if (stats.average.r + 100 > stats.average.b){ + return true; + } + return false; +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h new file mode 100644 index 0000000000..27eafb2484 --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h @@ -0,0 +1,36 @@ +/* Shiny Symbol Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonRSE_ShinyNumberDetector_H +#define PokemonAutomation_PokemonRSE_ShinyNumberDetector_H + +#include "Common/Cpp/AbstractLogger.h" +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +// After an in-game trade, the Pokemon's summary will appear. +// Red star for shiny. No star if not. +// Position is different when viewing in boxes. Do not use this for that. +class ShinySymbolDetector{ +public: + ShinySymbolDetector(Color color); + + virtual void make_overlays(VideoOverlaySet& items) const; + bool read(Logger& logger, const ImageViewRGB32& frame); + +private: + ImageFloatBox m_box_star; +}; + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp index af1a15a7fe..75bf953aba 100644 --- a/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp +++ b/SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp @@ -8,7 +8,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "PokemonLGPE_Panels.h" - +#include "Programs/PokemonLGPE_AlolanTrade.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -27,7 +27,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()); return ret; } diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp new file mode 100644 index 0000000000..37d62c4a36 --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp @@ -0,0 +1,225 @@ +/* LGPE Alolan Trade + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/StartupChecks/VideoResolutionCheck.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "NintendoSwitch/Controllers/NintendoSwitch_Joycon.h" +#include "Pokemon/Pokemon_Strings.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" +#include "PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h" +#include "PokemonLGPE_AlolanTrade.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +AlolanTrade_Descriptor::AlolanTrade_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonLGPE:AlolanTrade", + Pokemon::STRING_POKEMON + " LGPE", "Alolan Trade", + "", + "Shiny hunt Alolan forms by trading in-game.", + FeedbackType::NONE, + AllowCommandsWhenRunning::DISABLE_COMMANDS, + {ControllerFeature::NintendoSwitch_RightJoycon}, + FasterIfTickPrecise::NOT_FASTER + ) +{} + +struct AlolanTrade_Descriptor::Stats : public StatsTracker{ + Stats() + : trades(m_stats["Trades"]) + , resets(m_stats["Resets"]) + , shinies(m_stats["Shinies"]) + { + m_display_order.emplace_back("Trades"); + m_display_order.emplace_back("Resets"); + m_display_order.emplace_back("Shinies"); + } + std::atomic& trades; + std::atomic& resets; + std::atomic& shinies; +}; +std::unique_ptr AlolanTrade_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +AlolanTrade::AlolanTrade() + : NUM_TRADES( + "Number of Pokemon to trade:", + LockMode::UNLOCK_WHILE_RUNNING, + 30 + ) + , GO_HOME_WHEN_DONE(false) + , 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(NUM_TRADES); + //PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(NOTIFICATIONS); +} + +void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& scope){ + JoyconContext context(scope, env.console.controller()); + assert_16_9_720p_min(env.logger(), env.console); + AlolanTrade_Descriptor::Stats& stats = env.current_stats(); + + /* + WARNING: JOYCON TEST PROGRAM. Not well tested. Minimum infra to get this running. Bare minimum in general. + Use at your own risk, it won't skip update checks and the like. + FLASH RIGHT JOYCON. YOU NEED RIGHT JOYCON. YOU NEED THE HOME BUTTON. (this means no on-switch screenshots) + Also don't remap any of the buttons in the switch button mapping settings. Yet? + + Preconditions: + DO NOT have any Pokemon you want to keep in your boxes. Move them out to Home first. + Favoriting a Pokemon does not prevent it from being traded. + This must not be your first time doing the trade. (I've done all the trades, can't check first time trade behavior.) + + Setup: + Catch the Kanto variant of the target. + Stand in front of trade NPC. + Start the program in-game. + + Future additions?: + skip favorited pokemon - they have a symbol to indicate fav status (imo still risky to do, just move them out) + detect when all pokemon traded - trading screen will open but text will appear in the center + "you don't have any pokemon your trading partner wants" + detect dialog box, detect yes/no confirm box + actual enter game and start screen detectors + menu detectors, reset game from home, etc. + get rid of all this blindly mashing A in general...so need everything really. + */ + + //to check pokemon in menu boxes - not used + //Open menu - always defaults to center (Party) + /* Menu: + Play with Partner + Pokedex - Bag - Party - Communicate - Save + (Press Y for options) + + sort boxes by recently caught and press left to get to most recent pokemon + */ + + /* + pbf_press_button(context, BUTTON_A, 200ms, 2000ms); + pbf_press_button(context, BUTTON_HOME, 200ms, 2000ms); + pbf_move_joystick(context, 128, 0, 100ms, 100ms); + pbf_move_joystick(context, 128, 0, 100ms, 100ms); + pbf_move_joystick(context, 255, 128, 100ms, 100ms); + pbf_move_joystick(context, 128, 0, 100ms, 100ms); + pbf_press_button(context, BUTTON_X, 200ms, 2000ms); + */ + + bool shiny_found = false; + while (!shiny_found) { + //Run trades + for (uint16_t i = 0; i < NUM_TRADES; i++) { + //TODO: This is messy, pull it all out. run_trade()? + + //Talk to NPC, say Yes, select Pokemon from box. + BlackScreenOverWatcher trade_started(COLOR_RED); + int ret = run_until( + env.console, context, + [](JoyconContext& context){ + pbf_mash_button(context, BUTTON_A, 15000ms); + }, + {trade_started} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to start trade.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to start trade.", + env.console + ); + } + else { + env.log("Trade started."); + } + + //Wait for trade to complete. + BlackScreenOverWatcher trade_completed(COLOR_RED); + int ret2 = wait_until( + env.console, context, + std::chrono::seconds(120), + {trade_completed} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Did not detect end of trade.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Did not detect end of trade.", + env.console + ); + } + else { + env.log("Trade completed."); + } + + //After black screen fade is done, a summary will appear. + //pbf_wait(context, 250); + context.wait_for_all_requests(); + + VideoSnapshot screen = env.console.video().snapshot(); + ShinySymbolDetector shiny_checker(COLOR_YELLOW); + shiny_found = shiny_checker.read(env.console.logger(), screen); + + if (shiny_found) { + env.log("Shiny detected!"); + stats.shinies++; + send_program_status_notification(env, NOTIFICATION_SHINY, "Shiny found!", screen, true); + break; + } + else { + env.log("Not shiny."); + stats.trades++; + } + } + + if (!shiny_found) { + //Go to home, reset game + //How to handle sideways joycons vs in-game? What if a set is paired? + //Set as-is for now - I only have one ESP32, don't know how we're handling multiple joycons w/our usual home functions + + env.log("Out of Pokemon to trade. Resetting game."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Out of Pokemon to trade. Resetting game." + ); + } + } + + //GO_HOME_WHEN_DONE.run_end_of_program(context); + if (GO_HOME_WHEN_DONE) { + pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms); + } + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h new file mode 100644 index 0000000000..eae4398b23 --- /dev/null +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h @@ -0,0 +1,52 @@ +/* LGPE Alolan Trade + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonLGPE_AlolanTrade_H +#define PokemonAutomation_PokemonLGPE_AlolanTrade_H + +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "Common/Cpp/Options/SimpleIntegerOption.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonLGPE{ + +class AlolanTrade_Descriptor : public SingleSwitchProgramDescriptor{ +public: + AlolanTrade_Descriptor(); + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + + + +class AlolanTrade : public SingleSwitchProgramInstance{ +public: + AlolanTrade(); + virtual void program(SingleSwitchProgramEnvironment& env, CancellableScope& scope) override; + +private: + SimpleIntegerOption NUM_TRADES; + + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + + EventNotificationOption NOTIFICATION_SHINY; + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; +}; + + + + +} +} +} +#endif + + + From 1134e0391c9f9902e45efd85d9264dfe6c88a21c Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 7 Mar 2025 17:40:12 -0500 Subject: [PATCH 3/5] trading and open summary works --- .../PokemonLGPE_ShinySymbolDetector.cpp | 36 ++---- .../Programs/PokemonLGPE_AlolanTrade.cpp | 117 +++++++++++++----- 2 files changed, 93 insertions(+), 60 deletions(-) diff --git a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp index 7f0f896888..9680d11838 100644 --- a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp +++ b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp @@ -13,48 +13,32 @@ #include "CommonTools/Images/ImageFilter.h" #include "PokemonLGPE_ShinySymbolDetector.h" -//#include -//using std::cout; -//using std::endl; +#include +using std::cout; +using std::endl; namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonLGPE{ ShinySymbolDetector::ShinySymbolDetector(Color color) - : m_box_star(0.204, 0.095, 0.033, 0.053) + : m_box_star(0.666, 0.779, 0.028, 0.044) {} void ShinySymbolDetector::make_overlays(VideoOverlaySet& items) const{ items.add(COLOR_RED, m_box_star); } bool ShinySymbolDetector::read(Logger& logger, const ImageViewRGB32& frame){ - const bool replace_color_within_range = true; - - //Filter out background - ImageRGB32 filtered_region = filter_rgb32_range( - extract_box_reference(frame, m_box_star), - combine_rgb(138, 97, 221), combine_rgb(200, 181, 239), Color(0), replace_color_within_range - ); - ImageStats stats = image_stats(filtered_region); - - /* - filtered_region.save("./filtered_only.png"); - cout << stats.average.r << endl; - cout << stats.average.g << endl; - cout << stats.average.b << endl; - */ - /* Shiny: - R: 196.632, G: 196.771, B: 145.863 + Add infer box: (0.6660, 0.7790, 0.0280, 0.0440), RGB avg [159, 123, 125] avg sum 408 ratio [0.391, 0.301, 0.308] stddev [74.898, 54.696, 53.354] sum 182.948 crop size (54, 48) + Not shiny: - R: 181.862, G: 180.686, B: 193.999 + Add infer box: (0.6660, 0.7790, 0.0280, 0.0440), RGB avg [82, 113, 100] avg sum 295 ratio [0.276, 0.384, 0.340] stddev [15.477, 2.178, 2.648] sum 20.303 crop size (54, 48) */ - if (stats.average.r + 100 > stats.average.b){ - return true; - } - return false; + + const auto stats = image_stats(extract_box_reference(frame, m_box_star)); + return stats.stddev.sum() > 100; } diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp index 37d62c4a36..cea6aea6f4 100644 --- a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp @@ -59,8 +59,8 @@ std::unique_ptr AlolanTrade_Descriptor::make_stats() const{ AlolanTrade::AlolanTrade() : NUM_TRADES( "Number of Pokemon to trade:", - LockMode::UNLOCK_WHILE_RUNNING, - 30 + LockMode::LOCK_WHILE_RUNNING, + 30, 1 ) , GO_HOME_WHEN_DONE(false) , NOTIFICATION_SHINY( @@ -76,7 +76,7 @@ AlolanTrade::AlolanTrade() }) { PA_ADD_OPTION(NUM_TRADES); - //PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(NOTIFICATIONS); } @@ -89,12 +89,13 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& WARNING: JOYCON TEST PROGRAM. Not well tested. Minimum infra to get this running. Bare minimum in general. Use at your own risk, it won't skip update checks and the like. FLASH RIGHT JOYCON. YOU NEED RIGHT JOYCON. YOU NEED THE HOME BUTTON. (this means no on-switch screenshots) - Also don't remap any of the buttons in the switch button mapping settings. Yet? + Also don't remap any of the buttons in the switch button mapping settings. Yet? Could use this to add Home and Screenshot. Preconditions: DO NOT have any Pokemon you want to keep in your boxes. Move them out to Home first. Favoriting a Pokemon does not prevent it from being traded. This must not be your first time doing the trade. (I've done all the trades, can't check first time trade behavior.) + In your boxes, sort by ORDER CAUGHT Setup: Catch the Kanto variant of the target. @@ -111,26 +112,6 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& get rid of all this blindly mashing A in general...so need everything really. */ - //to check pokemon in menu boxes - not used - //Open menu - always defaults to center (Party) - /* Menu: - Play with Partner - Pokedex - Bag - Party - Communicate - Save - (Press Y for options) - - sort boxes by recently caught and press left to get to most recent pokemon - */ - - /* - pbf_press_button(context, BUTTON_A, 200ms, 2000ms); - pbf_press_button(context, BUTTON_HOME, 200ms, 2000ms); - pbf_move_joystick(context, 128, 0, 100ms, 100ms); - pbf_move_joystick(context, 128, 0, 100ms, 100ms); - pbf_move_joystick(context, 255, 128, 100ms, 100ms); - pbf_move_joystick(context, 128, 0, 100ms, 100ms); - pbf_press_button(context, BUTTON_X, 200ms, 2000ms); - */ - bool shiny_found = false; while (!shiny_found) { //Run trades @@ -160,7 +141,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& } //Wait for trade to complete. - BlackScreenOverWatcher trade_completed(COLOR_RED); + BlackScreenOverWatcher trade_completed(COLOR_YELLOW); int ret2 = wait_until( env.console, context, std::chrono::seconds(120), @@ -179,36 +160,104 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& env.log("Trade completed."); } - //After black screen fade is done, a summary will appear. - //pbf_wait(context, 250); + //Summary will appear the first time you trade in a session(?) Close that as well. + //Exit menu and dialog. + pbf_mash_button(context, BUTTON_B, 3000ms); context.wait_for_all_requests(); + stats.trades++; + env.update_stats(); + } + + //to check pokemon in menu boxes + //Open menu - always defaults to center (Party) + /* Menu: + Play with Partner + Pokedex - Bag - Party - Communicate - Save (these all have a colored line under when selected) + (Press Y for options) + + sort boxes by recently caught and press left to get to most recent pokemon + */ + + //Wait a bit. + pbf_press_button(context, BUTTON_X, 0ms, 2500ms); + context.wait_for_all_requests(); + + //Open menu, open party, open boxes + env.log("Opening boxes."); + pbf_press_button(context, BUTTON_X, 200ms, 500ms); + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + pbf_press_button(context, BUTTON_Y, 200ms, 1500ms); + context.wait_for_all_requests(); + + //Sort by order caught + env.log("Sorting by order caught."); + pbf_press_button(context, BUTTON_Y, 200ms, 1000ms); + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + context.wait_for_all_requests(); + + //Press left to go to last (most recent) Pokemon + env.log("Opening summary of most recent Pokemon."); + pbf_move_joystick(context, 0, 128, 100ms, 100ms); + context.wait_for_all_requests(); + + //View summary - it takes a moment to load, wait is below + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + pbf_move_joystick(context, 128, 255, 100ms, 100ms); + pbf_move_joystick(context, 128, 255, 100ms, 100ms); + pbf_press_button(context, BUTTON_A, 200ms, 100ms); + context.wait_for_all_requests(); + + //Wait. + pbf_press_button(context, BUTTON_X, 0ms, 5000ms); + context.wait_for_all_requests(); + + //Now check for shinies. Check everything that was traded. + for (uint16_t i = 0; i < NUM_TRADES; i++) { VideoSnapshot screen = env.console.video().snapshot(); ShinySymbolDetector shiny_checker(COLOR_YELLOW); - shiny_found = shiny_checker.read(env.console.logger(), screen); + bool check = shiny_checker.read(env.console.logger(), screen); - if (shiny_found) { + if (check) { env.log("Shiny detected!"); stats.shinies++; + env.update_stats(); send_program_status_notification(env, NOTIFICATION_SHINY, "Shiny found!", screen, true); - break; + shiny_found = true; } else { env.log("Not shiny."); - stats.trades++; } + + //Move left, check next. + pbf_move_joystick(context, 0, 128, 100ms, 100ms); + pbf_press_button(context, BUTTON_X, 0ms, 2000ms); + context.wait_for_all_requests(); } if (!shiny_found) { - //Go to home, reset game - //How to handle sideways joycons vs in-game? What if a set is paired? - //Set as-is for now - I only have one ESP32, don't know how we're handling multiple joycons w/our usual home functions + //TODO? Check if home button even exists before attempting to reset. + //This way, if on left joycon, stop the program and alert the user. env.log("Out of Pokemon to trade. Resetting game."); send_program_status_notification( env, NOTIFICATION_STATUS_UPDATE, "Out of Pokemon to trade. Resetting game." ); + + //TODO: Need to make proper GameEntry eventually + //Thankfully, Joycon is upright after going to home. + //Go to home and close game + pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms); + pbf_press_button(context, BUTTON_X, 200ms, 1000ms); + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); + + //Enter game from home + //break; + + stats.resets++; + env.update_stats(); } } From 75ed4e77e6cabb6a368430a147eb0209fd3f5eb5 Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 7 Mar 2025 19:27:08 -0500 Subject: [PATCH 4/5] cleanup and comments --- .../PokemonLGPE_ShinySymbolDetector.cpp | 7 +-- .../PokemonLGPE_ShinySymbolDetector.h | 5 +- .../Programs/PokemonLGPE_AlolanTrade.cpp | 47 +++++++++++-------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp index 9680d11838..2f4d3f67e1 100644 --- a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp +++ b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp @@ -29,13 +29,14 @@ void ShinySymbolDetector::make_overlays(VideoOverlaySet& items) const{ } bool ShinySymbolDetector::read(Logger& logger, const ImageViewRGB32& frame){ /* - Shiny: + Shiny (charizard): Add infer box: (0.6660, 0.7790, 0.0280, 0.0440), RGB avg [159, 123, 125] avg sum 408 ratio [0.391, 0.301, 0.308] stddev [74.898, 54.696, 53.354] sum 182.948 crop size (54, 48) - Not shiny: + Not shiny (chansey): Add infer box: (0.6660, 0.7790, 0.0280, 0.0440), RGB avg [82, 113, 100] avg sum 295 ratio [0.276, 0.384, 0.340] stddev [15.477, 2.178, 2.648] sum 20.303 crop size (54, 48) - */ + Only had the two to test with for now. + */ const auto stats = image_stats(extract_box_reference(frame, m_box_star)); return stats.stddev.sum() > 100; diff --git a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h index 27eafb2484..742f69a365 100644 --- a/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h +++ b/SerialPrograms/Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h @@ -14,9 +14,8 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonLGPE{ -// After an in-game trade, the Pokemon's summary will appear. -// Red star for shiny. No star if not. -// Position is different when viewing in boxes. Do not use this for that. +//Check for the red shiny star on a Pokemon's summary from the Party/Box menu. +//This does not work for the summary that appears after a catch. class ShinySymbolDetector{ public: ShinySymbolDetector(Color color); diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp index cea6aea6f4..d815b88aed 100644 --- a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp @@ -12,15 +12,12 @@ #include "CommonTools/StartupChecks/VideoResolutionCheck.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Controllers/NintendoSwitch_Joycon.h" +#include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" #include "Pokemon/Pokemon_Strings.h" #include "CommonTools/VisualDetectors/BlackScreenDetector.h" #include "PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h" #include "PokemonLGPE_AlolanTrade.h" -//#include -//using std::cout; -//using std::endl; - namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonLGPE{ @@ -86,19 +83,20 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& AlolanTrade_Descriptor::Stats& stats = env.current_stats(); /* - WARNING: JOYCON TEST PROGRAM. Not well tested. Minimum infra to get this running. Bare minimum in general. - Use at your own risk, it won't skip update checks and the like. - FLASH RIGHT JOYCON. YOU NEED RIGHT JOYCON. YOU NEED THE HOME BUTTON. (this means no on-switch screenshots) + WARNING: JOYCON TEST PROGRAM. Not well tested. Bare minimum in general. + + Only works with Right joycon atm. Do not update right joycon. + + Right joycon required for home button (this means no on-switch screenshots). Also don't remap any of the buttons in the switch button mapping settings. Yet? Could use this to add Home and Screenshot. Preconditions: DO NOT have any Pokemon you want to keep in your boxes. Move them out to Home first. Favoriting a Pokemon does not prevent it from being traded. - This must not be your first time doing the trade. (I've done all the trades, can't check first time trade behavior.) - In your boxes, sort by ORDER CAUGHT + This must not be your first time doing the trade. (I've done all the trades, so I can't check first time trade behavior.) Setup: - Catch the Kanto variant of the target. + Catch the Kanto variant of the target. Put this number in NUM_TRADES. Stand in front of trade NPC. Start the program in-game. @@ -116,7 +114,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& while (!shiny_found) { //Run trades for (uint16_t i = 0; i < NUM_TRADES; i++) { - //TODO: This is messy, pull it all out. run_trade()? + //TODO: This is messy, pull it all out? //Talk to NPC, say Yes, select Pokemon from box. BlackScreenOverWatcher trade_started(COLOR_RED); @@ -160,7 +158,8 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& env.log("Trade completed."); } - //Summary will appear the first time you trade in a session(?) Close that as well. + //Summary will appear the first time you trade in a session(?) + //Sometimes it appears anyway, don't know what determines it //Exit menu and dialog. pbf_mash_button(context, BUTTON_B, 3000ms); context.wait_for_all_requests(); @@ -169,14 +168,18 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& env.update_stats(); } - //to check pokemon in menu boxes + env.log("Done trading. Checking boxes."); + send_program_status_notification( + env, NOTIFICATION_STATUS_UPDATE, + "Done trading. Checking boxes." + ); + + //To check pokemon in menu boxes //Open menu - always defaults to center (Party) /* Menu: Play with Partner Pokedex - Bag - Party - Communicate - Save (these all have a colored line under when selected) (Press Y for options) - - sort boxes by recently caught and press left to get to most recent pokemon */ //Wait a bit. @@ -223,7 +226,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& env.log("Shiny detected!"); stats.shinies++; env.update_stats(); - send_program_status_notification(env, NOTIFICATION_SHINY, "Shiny found!", screen, true); + send_program_notification(env, NOTIFICATION_SHINY, COLOR_YELLOW, "Shiny found!", {}, "", screen, true); shiny_found = true; } else { @@ -236,6 +239,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& context.wait_for_all_requests(); } + /* if (!shiny_found) { //TODO? Check if home button even exists before attempting to reset. //This way, if on left joycon, stop the program and alert the user. @@ -250,15 +254,18 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& //Thankfully, Joycon is upright after going to home. //Go to home and close game pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms); - pbf_press_button(context, BUTTON_X, 200ms, 1000ms); - pbf_press_button(context, BUTTON_A, 200ms, 1000ms); - //Enter game from home - //break; + //TODO: + //joycon context->pro controller context? + start_game_from_home(env.console, context, true, 0, 0, std::chrono::milliseconds(2000)); stats.resets++; env.update_stats(); } + */ + + //Break for now since resetting the game doesn't work. + break; } //GO_HOME_WHEN_DONE.run_end_of_program(context); From c2b9474dc5b46b2962f57484e5f206e057af276e Mon Sep 17 00:00:00 2001 From: kichithewolf Date: Fri, 7 Mar 2025 23:10:42 -0500 Subject: [PATCH 5/5] pull out run_trade --- .../Programs/PokemonLGPE_AlolanTrade.cpp | 121 +++++++++--------- .../Programs/PokemonLGPE_AlolanTrade.h | 5 +- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp index d815b88aed..d014ea7f17 100644 --- a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp @@ -77,6 +77,56 @@ AlolanTrade::AlolanTrade() PA_ADD_OPTION(NOTIFICATIONS); } +void AlolanTrade::run_trade(SingleSwitchProgramEnvironment& env, JoyconContext& context){ + //Talk to NPC, say Yes, select Pokemon from box. + BlackScreenOverWatcher trade_started(COLOR_RED); + int ret = run_until( + env.console, context, + [](JoyconContext& context){ + pbf_mash_button(context, BUTTON_A, 15000ms); + }, + {trade_started} + ); + context.wait_for_all_requests(); + if (ret != 0){ + env.log("Failed to start trade.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to start trade.", + env.console + ); + } + else { + env.log("Trade started."); + } + + //Wait for trade to complete. + BlackScreenOverWatcher trade_completed(COLOR_YELLOW); + int ret2 = wait_until( + env.console, context, + std::chrono::seconds(120), + {trade_completed} + ); + context.wait_for_all_requests(); + if (ret2 != 0){ + env.log("Did not detect end of trade.", COLOR_RED); + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Did not detect end of trade.", + env.console + ); + } + else { + env.log("Trade completed."); + } + + //Summary will appear the first time you trade in a session(?) + //Sometimes it appears anyway, don't know what determines it + //Exit menu and dialog. + pbf_mash_button(context, BUTTON_B, 3000ms); + context.wait_for_all_requests(); +} + void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& scope){ JoyconContext context(scope, env.console.controller()); assert_16_9_720p_min(env.logger(), env.console); @@ -114,55 +164,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& while (!shiny_found) { //Run trades for (uint16_t i = 0; i < NUM_TRADES; i++) { - //TODO: This is messy, pull it all out? - - //Talk to NPC, say Yes, select Pokemon from box. - BlackScreenOverWatcher trade_started(COLOR_RED); - int ret = run_until( - env.console, context, - [](JoyconContext& context){ - pbf_mash_button(context, BUTTON_A, 15000ms); - }, - {trade_started} - ); - context.wait_for_all_requests(); - if (ret != 0){ - env.log("Failed to start trade.", COLOR_RED); - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "Failed to start trade.", - env.console - ); - } - else { - env.log("Trade started."); - } - - //Wait for trade to complete. - BlackScreenOverWatcher trade_completed(COLOR_YELLOW); - int ret2 = wait_until( - env.console, context, - std::chrono::seconds(120), - {trade_completed} - ); - context.wait_for_all_requests(); - if (ret2 != 0){ - env.log("Did not detect end of trade.", COLOR_RED); - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "Did not detect end of trade.", - env.console - ); - } - else { - env.log("Trade completed."); - } - - //Summary will appear the first time you trade in a session(?) - //Sometimes it appears anyway, don't know what determines it - //Exit menu and dialog. - pbf_mash_button(context, BUTTON_B, 3000ms); - context.wait_for_all_requests(); + run_trade(env, context); stats.trades++; env.update_stats(); @@ -183,7 +185,7 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& */ //Wait a bit. - pbf_press_button(context, BUTTON_X, 0ms, 2500ms); + pbf_wait(context, 2500ms); context.wait_for_all_requests(); //Open menu, open party, open boxes @@ -205,15 +207,14 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& pbf_move_joystick(context, 0, 128, 100ms, 100ms); context.wait_for_all_requests(); - //View summary - it takes a moment to load, wait is below + //View summary - it takes a moment to load pbf_press_button(context, BUTTON_A, 200ms, 1000ms); pbf_move_joystick(context, 128, 255, 100ms, 100ms); pbf_move_joystick(context, 128, 255, 100ms, 100ms); pbf_press_button(context, BUTTON_A, 200ms, 100ms); context.wait_for_all_requests(); - //Wait. - pbf_press_button(context, BUTTON_X, 0ms, 5000ms); + pbf_wait(context, 5000ms); context.wait_for_all_requests(); //Now check for shinies. Check everything that was traded. @@ -238,22 +239,20 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& pbf_press_button(context, BUTTON_X, 0ms, 2000ms); context.wait_for_all_requests(); } - /* if (!shiny_found) { - //TODO? Check if home button even exists before attempting to reset. - //This way, if on left joycon, stop the program and alert the user. - - env.log("Out of Pokemon to trade. Resetting game."); + env.log("Out of Pokemon to trade and no shiny found. Resetting game."); send_program_status_notification( env, NOTIFICATION_STATUS_UPDATE, - "Out of Pokemon to trade. Resetting game." + "Out of Pokemon to trade and no shiny found. Resetting game." ); //TODO: Need to make proper GameEntry eventually //Thankfully, Joycon is upright after going to home. //Go to home and close game pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms); + pbf_press_button(context, BUTTON_X, 200ms, 200ms); + pbf_press_button(context, BUTTON_A, 200ms, 1000ms); //TODO: //joycon context->pro controller context? @@ -263,14 +262,12 @@ void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& env.update_stats(); } */ - //Break for now since resetting the game doesn't work. break; } - //GO_HOME_WHEN_DONE.run_end_of_program(context); if (GO_HOME_WHEN_DONE) { - pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms); + pbf_press_button(context, BUTTON_HOME, 200ms, 1000ms); } send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); } diff --git a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h index eae4398b23..3fa44a0f06 100644 --- a/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h +++ b/SerialPrograms/Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonLGPE_AlolanTrade_H #define PokemonAutomation_PokemonLGPE_AlolanTrade_H +#include "NintendoSwitch/Controllers/NintendoSwitch_Joycon.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" #include "CommonFramework/Notifications/EventNotificationsTable.h" @@ -23,14 +24,14 @@ class AlolanTrade_Descriptor : public SingleSwitchProgramDescriptor{ virtual std::unique_ptr make_stats() const override; }; - - class AlolanTrade : public SingleSwitchProgramInstance{ public: AlolanTrade(); virtual void program(SingleSwitchProgramEnvironment& env, CancellableScope& scope) override; private: + void run_trade(SingleSwitchProgramEnvironment& env, JoyconContext& context); + SimpleIntegerOption NUM_TRADES; GoHomeWhenDoneOption GO_HOME_WHEN_DONE;