diff --git a/resource/tasks.json b/resource/tasks.json index 27fdf32d19..7f62d4b3e6 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3070,6 +3070,12 @@ "ClueGiveTo1stConfirm" ] }, + "BattleUseOper": { + "algorithm": "JustReturn", + "preDelay": 500, + "rearDelay": 0, + "Doc": "pre 是从点击干员,到干员信息弹出来等待的时间;rear是干员上场,到设置朝向之间等待的时间" + }, "BattleOpersFlag": { "templThreshold": 0.85, "roi": [ diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp index 04b886d634..2c8f064a12 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.cpp +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -1,5 +1,7 @@ #include "BattleImageAnalyzer.h" +#include + #include "MultiMatchImageAnalyzer.h" #include "MatchImageAnalyzer.h" #include "HashImageAnalyzer.h" @@ -8,7 +10,9 @@ bool asst::BattleImageAnalyzer::analyze() { - return opers_analyze(); + bool ret = opers_analyze(); + ret |= home_analyze(); + return ret; } const std::vector& asst::BattleImageAnalyzer::get_opers() const noexcept @@ -16,6 +20,11 @@ const std::vector& asst::BattleImageAnalyzer::g return m_opers; } +const std::vector& asst::BattleImageAnalyzer::get_homes() const noexcept +{ + return m_homes; +} + bool asst::BattleImageAnalyzer::opers_analyze() { LogTraceFunction; @@ -169,7 +178,8 @@ bool asst::BattleImageAnalyzer::oper_available_analyze(const Rect& roi) cv::Scalar avg = cv::mean(hsv); Log.trace("oper available, mean", avg[2]); - static int thres = std::dynamic_pointer_cast(Task.get("BattleOperAvailable"))->special_threshold; + static int thres = static_cast(std::dynamic_pointer_cast( + Task.get("BattleOperAvailable"))->special_threshold); if (avg[2] < thres) { return false; } @@ -178,10 +188,42 @@ bool asst::BattleImageAnalyzer::oper_available_analyze(const Rect& roi) bool asst::BattleImageAnalyzer::home_analyze() { - return false; -} + // 颜色转换 + cv::Mat hsv; + cv::cvtColor(m_image, hsv, cv::COLOR_BGR2HSV); + cv::Mat bin; + cv::inRange(hsv, cv::Scalar(106, 160, 200), cv::Scalar(107, 220, 255), bin); -bool asst::BattleImageAnalyzer::placed_analyze() -{ - return false; + // 开操作降噪 + cv::Mat morph_dst; + cv::Mat open_kernel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(1, 1)); + cv::morphologyEx(bin, morph_dst, cv::MORPH_OPEN, open_kernel); + + // 霍夫线 + std::vector lines; + cv::HoughLinesP(morph_dst, lines, 1, 60 * CV_PI / 180, 10, 20, 10); + + int left = INT_MAX, right = 0, top = INT_MAX, bottom = 0; + for (auto&& l : lines) { + left = (std::min)({ left, l[0], l[2] }); + right = (std::max)({ right, l[0], l[2] }); + top = (std::min)({ top, l[1], l[3] }); + bottom = (std::max)({ bottom, l[1], l[3] }); + +#ifdef ASST_DEBUG + cv::line(m_image_draw, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 3); +#endif + } + if (right == 0) { + Log.error("home recognition error"); + return false; + } + Rect home_rect(left, top, right - left, bottom - top); + m_homes.emplace_back(home_rect); + +#ifdef ASST_DEBUG + cv::rectangle(m_image_draw, utils::make_rect(home_rect), cv::Scalar(0, 255, 0), 5); +#endif + + return true; } diff --git a/src/MeoAssistant/BattleImageAnalyzer.h b/src/MeoAssistant/BattleImageAnalyzer.h index 1a454f03b3..6d49f145aa 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.h +++ b/src/MeoAssistant/BattleImageAnalyzer.h @@ -32,7 +32,8 @@ namespace asst virtual bool analyze() override; - const std::vector& get_opers() const noexcept; + virtual const std::vector& get_opers() const noexcept; + virtual const std::vector& get_homes() const noexcept; protected: bool opers_analyze(); // 识别干员 @@ -40,11 +41,11 @@ namespace asst int oper_cost_analyze(const Rect& roi); bool oper_available_analyze(const Rect& roi); bool home_analyze(); // 识别蓝色的家门 - bool placed_analyze(); // 识别可放置干员的位置 - std::vector m_opers; + std::vector m_opers; // 下方干员信息 + std::vector m_homes; // 蓝色的家门位置 - private: + protected: // 该分析器不支持外部设置ROI virtual void set_roi(const Rect& roi) noexcept override { diff --git a/src/MeoAssistant/BattlePerspectiveImageAnalyzer.cpp b/src/MeoAssistant/BattlePerspectiveImageAnalyzer.cpp new file mode 100644 index 0000000000..74d99b26b5 --- /dev/null +++ b/src/MeoAssistant/BattlePerspectiveImageAnalyzer.cpp @@ -0,0 +1,68 @@ +#include "BattlePerspectiveImageAnalyzer.h" + +#include "Logger.hpp" + +bool asst::BattlePerspectiveImageAnalyzer::analyze() +{ + home_analyze(); + return placed_analyze(); +} + +void asst::BattlePerspectiveImageAnalyzer::set_src_homes(std::vector src_homes) +{ + m_src_homes = std::move(src_homes); +} + +asst::Point asst::BattlePerspectiveImageAnalyzer::get_nearest_point() const noexcept +{ + return m_nearest_point; +} + +const std::vector& asst::BattlePerspectiveImageAnalyzer::get_homes() const noexcept +{ + return m_homes.empty() ? m_src_homes : m_homes; +} + +bool asst::BattlePerspectiveImageAnalyzer::placed_analyze() +{ + // 颜色转换 + cv::Mat hsv; + cv::cvtColor(m_image, hsv, cv::COLOR_BGR2HSV); + cv::Mat bin; + cv::inRange(hsv, cv::Scalar(60, 100, 60), cv::Scalar(80, 150, 120), bin); + + // 形态学降噪 + cv::Mat morph_dst = bin; + cv::Mat morph_kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(9, 9)); + cv::morphologyEx(morph_dst, morph_dst, cv::MORPH_CLOSE, morph_kernel); + cv::morphologyEx(morph_dst, morph_dst, cv::MORPH_OPEN, morph_kernel); + + const auto& all_homes = get_homes(); + if (all_homes.empty()) { + return false; + } + Rect home = all_homes.front(); + + int min_dist = INT_MAX; + for (int row = 0; row != morph_dst.rows; ++row) { + for (int col = 0; col != morph_dst.cols; ++col) { + cv::uint8_t value = morph_dst.at(row, col); + if (value) { + int dist = std::abs(home.x - col) + std::abs(home.y - row); + if (min_dist > dist) { + min_dist = dist; + m_nearest_point = Point(col, row); + } + } + } + } + if (m_nearest_point.x == 0 && m_nearest_point.y == 0) { + return false; + } + +#ifdef ASST_DEBUG + cv::circle(m_image_draw, cv::Point(m_nearest_point.x, m_nearest_point.y), 3, cv::Scalar(0, 255, 0)); +#endif + + return true; +} diff --git a/src/MeoAssistant/BattlePerspectiveImageAnalyzer.h b/src/MeoAssistant/BattlePerspectiveImageAnalyzer.h new file mode 100644 index 0000000000..66d6add1a3 --- /dev/null +++ b/src/MeoAssistant/BattlePerspectiveImageAnalyzer.h @@ -0,0 +1,25 @@ +#pragma once +#include "BattleImageAnalyzer.h" + +namespace asst +{ + // 战斗透视图片,一般是点击干员后的画面 + class BattlePerspectiveImageAnalyzer : public BattleImageAnalyzer + { + public: + using BattleImageAnalyzer::BattleImageAnalyzer; + virtual ~BattlePerspectiveImageAnalyzer() = default; + + virtual bool analyze() override; + virtual const std::vector& get_homes() const noexcept override; + + // 设置非透视状态下蓝色家门的位置 + void set_src_homes(std::vector src_homes); + Point get_nearest_point() const noexcept; + protected: + bool placed_analyze(); // 识别可放置干员的位置 + + std::vector m_src_homes; + Point m_nearest_point; + }; +} diff --git a/src/MeoAssistant/BattleTask.cpp b/src/MeoAssistant/BattleTask.cpp index 6cf61bfa92..882ff031c2 100644 --- a/src/MeoAssistant/BattleTask.cpp +++ b/src/MeoAssistant/BattleTask.cpp @@ -1,11 +1,112 @@ #include "BattleTask.h" #include "BattleImageAnalyzer.h" +#include "BattlePerspectiveImageAnalyzer.h" #include "Controller.h" +#include "TaskData.h" + +typedef asst::BattleImageAnalyzer::Role Role; +typedef asst::BattleImageAnalyzer::Oper Oper; bool asst::BattleTask::_run() { - BattleImageAnalyzer oper_analyzer(Ctrler.get_image()); - oper_analyzer.analyze(); - return false; + while (!need_exit() + && auto_battle()) { + ; + } + return true; +} + +bool asst::BattleTask::auto_battle() +{ + BattleImageAnalyzer oper_analyzer(Ctrler.get_image()); + if (!oper_analyzer.analyze()) { + return false; + } + const auto& opers = oper_analyzer.get_opers(); + if (opers.empty()) { + return true; + } + + static const std::array RoleOrder = { + Role::Pioneer, + Role::Sniper, + Role::Support, + Role::Warrior, + Role::Caster, + Role::Special, + Role::Medic, + Role::Tank, + Role::Drone + }; + const auto use_oper_task_ptr = Task.get("BattleUseOper"); + + // 点击当前最合适的干员 + + //auto oper_iter = std::find_first_of( + // opers.cbegin(), opers.cend(), RoleOrder.cbegin(), RoleOrder.cend(), + // [](const Oper& oper, const Role& role) -> bool { + // return oper.available && oper.role == role; + //}); + //if (oper_iter == opers.cend()) { + // return true; + //} + Oper opt_oper; + bool oper_found = false; + for (auto role : RoleOrder) { + for (const auto& oper : opers) { + if (!oper.available) { + continue; + } + if (oper.role == role) { + opt_oper = oper; + oper_found = true; + break; + } + } + if (oper_found) { + break; + } + } + + Ctrler.click(opt_oper.rect); + sleep(use_oper_task_ptr->pre_delay); + + // 将干员拖动到场上 + BattlePerspectiveImageAnalyzer placed_analyzer(Ctrler.get_image()); + placed_analyzer.set_src_homes(oper_analyzer.get_homes()); + if (!placed_analyzer.analyze()) { + return true; + } + Point nearest_point = placed_analyzer.get_nearest_point(); + Rect nearest_rect(nearest_point.x, nearest_point.y, 1, 1); + Ctrler.swipe(opt_oper.rect, nearest_rect, 1000); + sleep(use_oper_task_ptr->rear_delay); + + // 拖动干员朝向 + Rect home = placed_analyzer.get_homes().front(); + Point home_center((home.x + home.width) / 2, (home.y + home.height) / 2); + + switch (opt_oper.role) { + case Role::Medic: + Ctrler.swipe(nearest_point, home_center, 1000); + break; + case Role::Pioneer: + case Role::Warrior: + case Role::Sniper: + case Role::Support: + case Role::Special: + case Role::Tank: + case Role::Caster: + case Role::Drone: + default: + { + Point reverse_point; + reverse_point.x = nearest_point.x - home_center.x + nearest_point.x; + reverse_point.y = nearest_point.y - home_center.y + nearest_point.y; + Ctrler.swipe(nearest_point, reverse_point, 1000); + } + break; + } + return true; } diff --git a/src/MeoAssistant/BattleTask.h b/src/MeoAssistant/BattleTask.h index 1e7f6c4869..086badffcf 100644 --- a/src/MeoAssistant/BattleTask.h +++ b/src/MeoAssistant/BattleTask.h @@ -12,5 +12,7 @@ namespace asst protected: virtual bool _run() override; + + bool auto_battle(); }; } diff --git a/src/MeoAssistant/MeoAssistant.vcxproj b/src/MeoAssistant/MeoAssistant.vcxproj index 462c30c982..0b52ba17a2 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj +++ b/src/MeoAssistant/MeoAssistant.vcxproj @@ -25,6 +25,7 @@ + @@ -77,6 +78,7 @@ + diff --git a/src/MeoAssistant/MeoAssistant.vcxproj.filters b/src/MeoAssistant/MeoAssistant.vcxproj.filters index df94deef5f..69b801e044 100644 --- a/src/MeoAssistant/MeoAssistant.vcxproj.filters +++ b/src/MeoAssistant/MeoAssistant.vcxproj.filters @@ -243,6 +243,9 @@ 头文件\ImageAnalyzer\General + + 头文件\ImageAnalyzer\General + @@ -395,6 +398,9 @@ 源文件\ImageAnalyzer\General + + 源文件\ImageAnalyzer +