diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 8b6c4d982a..43b59c88b9 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -352,12 +352,6 @@ else() # macOS and Linux target_link_directories(SerialProgramsLib PUBLIC ${ONNXRUNTIME_LIBRARY_DIRS}) target_link_libraries(SerialProgramsLib PUBLIC ${ONNXRUNTIME_LINK_LIBRARIES}) endif() - # Find OpenCV - if(CMAKE_SYSTEM_PROCESSOR MATCHES "(arm64)|(ARM64)") - find_package(OpenCV REQUIRED HINTS "/opt/homebrew/opt/opencv/lib/cmake/opencv4/") - else() - find_package(OpenCV REQUIRED HINTS "/usr/local/opt/opencv/lib/cmake/opencv4/") - endif() else() # Linux # ONNX RUNTIME LINUX CONFIG # NOTE: users can specify their own ONNX_ROOT_PATH (this is the base folder for ONNX) on the command line when evoking cmake. @@ -435,26 +429,26 @@ else() # macOS and Linux else() message(FATAL_ERROR "Could not find ONNX Runtime headers or library.") endif() - # Find OpenCV - find_package(OpenCV REQUIRED HINTS "/usr/local/opt/opencv/lib/cmake/opencv4/") endif() # end Linux - include_directories(${OpenCV_INCLUDE_DIRS}) - link_directories(${OpenCV_LIBRARY_DIRS}) - target_link_libraries(SerialProgramsLib PRIVATE ${OpenCV_LIBS}) + # Find OpenCV + pkg_search_module(OpenCV REQUIRED opencv4 opencv) + target_include_directories(SerialProgramsLib SYSTEM PRIVATE ${OpenCV_INCLUDE_DIRS}) # "SYSTEM" to suppress warnings + target_link_directories(SerialProgramsLib PUBLIC ${OpenCV_LIBRARY_DIRS}) + target_link_libraries(SerialProgramsLib PUBLIC ${OpenCV_LINK_LIBRARIES}) #we hope to use our own Tesseract build in future so we can rid that dependency #but right now to run on Linux and Mac we need to use external Tesseract library - if (UNIX_LINK_TESSERACT) + pkg_search_module(TESSERACT tesseract) + pkg_search_module(LEPTONICA lept) + if (TESSERACT_FOUND AND LEPTONICA_FOUND) target_compile_definitions(SerialProgramsLib PRIVATE PA_TESSERACT UNIX_LINK_TESSERACT) - pkg_search_module(TESSERACT REQUIRED tesseract) - pkg_search_module(LEPTONICA REQUIRED lept) - include_directories(${TESSERACT_INCLUDE_DIRS}) - include_directories(${LEPTONICA_INCLUDE_DIRS}) - link_directories(${TESSERACT_LIBRARY_DIRS}) - link_directories(${LEPTONICA_LIBRARY_DIRS}) - target_link_libraries(SerialProgramsLib PRIVATE ${TESSERACT_LINK_LIBRARIES}) - target_link_libraries(SerialProgramsLib PRIVATE ${LEPTONICA_LINK_LIBRARIES}) + target_include_directories(SerialProgramsLib SYSTEM PRIVATE ${TESSERACT_INCLUDE_DIRS}) + target_include_directories(SerialProgramsLib SYSTEM PRIVATE ${LEPTONICA_INCLUDE_DIRS}) + target_link_directories(SerialProgramsLib PUBLIC ${TESSERACT_LIBRARY_DIRS}) + target_link_directories(SerialProgramsLib PUBLIC ${LEPTONICA_LIBRARY_DIRS}) + target_link_libraries(SerialProgramsLib PUBLIC ${TESSERACT_LINK_LIBRARIES}) + target_link_libraries(SerialProgramsLib PUBLIC ${LEPTONICA_LINK_LIBRARIES}) endif() pkg_search_module(DPP dpp=10.0.22) @@ -468,10 +462,6 @@ else() # macOS and Linux endif() if (APPLE) - # Add -Wno-c11-extensions to avoid clang gives - # /usr/local/Cellar/opencv/4.5.5_3/include/opencv4/opencv2/core/mat.inl.hpp:2116:9: error: '_Atomic' is a C11 extension - # when compiling OpenCV - # target_compile_options(SerialProgramsLib PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-c11-extensions) target_compile_options(SerialProgramsLib PRIVATE -Wall -Wextra -Wpedantic -Werror -Wshorten-64-to-32) # on macOS, need this framework to query OS API to control display sleep and system sleep behavior target_link_libraries(SerialProgramsLib PRIVATE "-framework IOKit -framework CoreFoundation") diff --git a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp index b4f2503dd0..0b6853a560 100644 --- a/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp +++ b/SerialPrograms/Source/PokemonLZA/PokemonLZA_Panels.cpp @@ -20,6 +20,7 @@ #include "Programs/ShinyHunting/PokemonLZA_ShinyHunt_OverworldReset.h" #include "Programs/ShinyHunting/PokemonLZA_BeldumHunter.h" #include "Programs/ShinyHunting/PokemonLZA_AutoFossil.h" +#include "Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.h" // Developer #include "Programs/TestPrograms/PokemonLZA_OverworldWatcher.h" @@ -52,6 +53,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- Shiny Hunting ----"); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); if (PreloadSettings::instance().DEVELOPER_MODE){ ret.emplace_back(make_single_switch_program()); diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.cpp b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.cpp new file mode 100644 index 0000000000..568593d4e2 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.cpp @@ -0,0 +1,177 @@ +/* Shiny Hunt - Wild Zone Entrance + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonTools/Async/InferenceRoutines.h" +#include "CommonTools/VisualDetectors/BlackScreenDetector.h" +#include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonLA/Inference/Sounds/PokemonLA_ShinySoundDetector.h" +#include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h" +#include "PokemonLZA/Programs/PokemonLZA_BasicNavigation.h" +#include "PokemonLZA_WildZoneEntrance.h" + +namespace PokemonAutomation { +namespace NintendoSwitch { +namespace PokemonLZA { + +using namespace Pokemon; + + +ShinyHunt_WildZoneEntrance_Descriptor::ShinyHunt_WildZoneEntrance_Descriptor() + : SingleSwitchProgramDescriptor("PokemonLZA:ShinyHunt-WildZoneEntrance", STRING_POKEMON + " LZA", + "Shiny Hunt - Wild Zone Entrance", + "Programs/PokemonLZA/ShinyHunt-WildZoneEntrance.html", + "Shiny hunt by repeatedly entering Wild Zone from its entrance.", + ProgramControllerClass::StandardController_NoRestrictions, FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS, {}) {} +class ShinyHunt_WildZoneEntrance_Descriptor::Stats : public StatsTracker { +public: + Stats() : resets(m_stats["Wild Zone"]), shinies(m_stats["Shiny Sounds"]), errors(m_stats["Errors"]) { + m_display_order.emplace_back("Wild Zone"); + m_display_order.emplace_back("Shiny Sounds"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + + m_aliases["Shinies"] = "Shiny Sounds"; + m_aliases["Shinies Detected"] = "Shiny Sounds"; + } + + std::atomic& resets; + std::atomic& shinies; + std::atomic& errors; +}; +std::unique_ptr ShinyHunt_WildZoneEntrance_Descriptor::make_stats() const { + return std::unique_ptr(new Stats()); +} + + +ShinyHunt_WildZoneEntrance::ShinyHunt_WildZoneEntrance() + : WALK_IN_ZONE("WALK IN ZONE:
Walk this long in the zone after passing through the gate.", + LockMode::UNLOCK_WHILE_RUNNING, "500 ms"), + SHINY_DETECTED("Shiny Detected", "", "2000 ms", ShinySoundDetectedAction::NOTIFY_ON_FIRST_ONLY), + NOTIFICATION_STATUS("Status Update", true, false, std::chrono::seconds(3600)), + NOTIFICATIONS({ + &NOTIFICATION_STATUS, + &SHINY_DETECTED.NOTIFICATIONS, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_RECOVERABLE, + &NOTIFICATION_ERROR_FATAL, + }) { + PA_ADD_STATIC(SHINY_REQUIRES_AUDIO); + PA_ADD_OPTION(WALK_IN_ZONE); + PA_ADD_OPTION(SHINY_DETECTED); + PA_ADD_OPTION(NOTIFICATIONS); +} + + +void run_to_gate(ConsoleHandle& console, ProControllerContext& context) { + ButtonWatcher buttonA(COLOR_RED, ButtonType::ButtonA, {0, 0, 1, 1}, &console.overlay()); + int ret = run_until(console, context, + [](ProControllerContext& context) { + for (int c = 0; c < 10; c++) { + pbf_move_left_joystick(context, 128, 0, 800ms, 200ms); + } + }, + {{buttonA}}); + + switch (ret) { + case 0: + break; + default: + OperationFailedException::fire(ErrorReport::SEND_ERROR_REPORT, + "run_to_gate(): Unable to detect entrance after 10 seconds.", console); + } +} + +void enter_wild_zone_entrance(ConsoleHandle& console, ProControllerContext& context, Milliseconds walk_in_zone) { + BlackScreenOverWatcher black_screen(COLOR_BLUE); + int ret = run_until(console, context, + [&](ProControllerContext& context) { + pbf_mash_button(context, BUTTON_B, 200ms); // dismiss menu if any + run_to_gate(console, context); + pbf_mash_button(context, BUTTON_A, 2000ms); + pbf_move_left_joystick(context, 128, 0, walk_in_zone, 200ms); + context.wait_for_all_requests(); + pbf_press_button(context, BUTTON_PLUS, 100ms, 100ms); // open map + }, + {{black_screen}}); + if (ret == 0) { + console.log("[WildZoneEntrance] Detected day/night change after entering."); + context.wait_for(std::chrono::milliseconds(2000)); + pbf_mash_button(context, BUTTON_B, 200ms); // dismiss menu if any + pbf_press_button(context, BUTTON_PLUS, 100ms, 100ms); // open map again + } + pbf_mash_button(context, BUTTON_A, 800ms); // teleporting or just mashing button + pbf_mash_button(context, BUTTON_B, 200ms); // in case need to dismiss map + context.wait_for_all_requests(); + context.wait_for(std::chrono::milliseconds(4000)); // TODO: differ NS1 wait time from NS2 +} + +void ShinyHunt_WildZoneEntrance::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + ShinyHunt_WildZoneEntrance_Descriptor::Stats& stats = + env.current_stats(); + + if (SHINY_DETECTED.ACTION == ShinySoundDetectedAction::NOTIFY_ON_ALL) { + throw UserSetupError(env.console, "Shiny would be detected/notified at most once. Choose one of the other 2 options"); + } + + uint8_t shiny_count = 0; + + { // first run with shiny detection + float shiny_coefficient = 1.0; + PokemonLA::ShinySoundDetector shiny_detector(env.console, [&](float error_coefficient) -> bool { + // Warning: This callback will be run from a different thread than this function. + shiny_count++; + stats.shinies++; + env.update_stats(); + shiny_coefficient = error_coefficient; + return true; + }); + + run_until(env.console, context, + [&](ProControllerContext& context) { + while (true) { + send_program_status_notification(env, NOTIFICATION_STATUS); + stats.resets++; + enter_wild_zone_entrance(env.console, context, WALK_IN_ZONE); + env.update_stats(); + } + }, + {{shiny_detector}}); + + context.wait_for(std::chrono::milliseconds(1000)); + + // when shiny sound is detected, it's most likely happened inside the zone + // now try to reset position + pbf_mash_button(context, BUTTON_B, 200ms); // dismiss menu if any + pbf_press_button(context, BUTTON_PLUS, 100ms, 100ms); // open map + pbf_mash_button(context, BUTTON_A, 600ms); // teleporting or just mashing button + pbf_mash_button(context, BUTTON_B, 200ms); // in case need to dismiss map + + SHINY_DETECTED.on_shiny_sound(env, env.console, context, shiny_count, shiny_coefficient); + } + + // continue with shiny sound detection is pointless + if (SHINY_DETECTED.ACTION != ShinySoundDetectedAction::STOP_PROGRAM) { + while (true) { + send_program_status_notification(env, NOTIFICATION_STATUS); + stats.resets++; + enter_wild_zone_entrance(env.console, context, WALK_IN_ZONE); + env.update_stats(); + } + } + + go_home(env.console, context); + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + + +} // namespace PokemonLZA +} // namespace NintendoSwitch +} // namespace PokemonAutomation diff --git a/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.h b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.h new file mode 100644 index 0000000000..a74d6272e2 --- /dev/null +++ b/SerialPrograms/Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.h @@ -0,0 +1,49 @@ +/* Shiny Hunt - Wild Zone Entrance + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonLZA_WildZoneEntrance_H +#define PokemonAutomation_PokemonLZA_WildZoneEntrance_H + +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "PokemonLA/Options/PokemonLA_ShinyDetectedAction.h" +#include "PokemonLZA/Options/PokemonLZA_ShinyDetectedAction.h" + +namespace PokemonAutomation { +namespace NintendoSwitch { +namespace PokemonLZA { + + +class ShinyHunt_WildZoneEntrance_Descriptor : public SingleSwitchProgramDescriptor { +public: + ShinyHunt_WildZoneEntrance_Descriptor(); + + class Stats; + virtual std::unique_ptr make_stats() const override; +}; + + +class ShinyHunt_WildZoneEntrance : public SingleSwitchProgramInstance { +public: + ShinyHunt_WildZoneEntrance(); + + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; + +private: + PokemonLA::ShinyRequiresAudioText SHINY_REQUIRES_AUDIO; + MillisecondsOption WALK_IN_ZONE; + + ShinySoundDetectedActionOption SHINY_DETECTED; + + EventNotificationOption NOTIFICATION_STATUS; + EventNotificationsOption NOTIFICATIONS; +}; + + +} // namespace PokemonLZA +} // namespace NintendoSwitch +} // namespace PokemonAutomation +#endif diff --git a/SerialPrograms/SourceFiles.cmake b/SerialPrograms/SourceFiles.cmake index 192d927d8e..2ffaa2804d 100644 --- a/SerialPrograms/SourceFiles.cmake +++ b/SerialPrograms/SourceFiles.cmake @@ -1579,6 +1579,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_BenchSit.h Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_OverworldReset.cpp Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_ShinyHunt_OverworldReset.h + Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.cpp + Source/PokemonLZA/Programs/ShinyHunting/PokemonLZA_WildZoneEntrance.h Source/PokemonLZA/Programs/TestPrograms/PokemonLZA_MoveBoxArrow.cpp Source/PokemonLZA/Programs/TestPrograms/PokemonLZA_MoveBoxArrow.h Source/PokemonLZA/Programs/TestPrograms/PokemonLZA_OverworldWatcher.cpp