mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 01:40:46 +08:00
feat.初步完成战斗上干员
This commit is contained in:
@@ -3070,6 +3070,12 @@
|
||||
"ClueGiveTo1stConfirm"
|
||||
]
|
||||
},
|
||||
"BattleUseOper": {
|
||||
"algorithm": "JustReturn",
|
||||
"preDelay": 500,
|
||||
"rearDelay": 0,
|
||||
"Doc": "pre 是从点击干员,到干员信息弹出来等待的时间;rear是干员上场,到设置朝向之间等待的时间"
|
||||
},
|
||||
"BattleOpersFlag": {
|
||||
"templThreshold": 0.85,
|
||||
"roi": [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "BattleImageAnalyzer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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::Oper>& asst::BattleImageAnalyzer::get_opers() const noexcept
|
||||
@@ -16,6 +20,11 @@ const std::vector<asst::BattleImageAnalyzer::Oper>& asst::BattleImageAnalyzer::g
|
||||
return m_opers;
|
||||
}
|
||||
|
||||
const std::vector<asst::Rect>& 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<MatchTaskInfo>(Task.get("BattleOperAvailable"))->special_threshold;
|
||||
static int thres = static_cast<int>(std::dynamic_pointer_cast<MatchTaskInfo>(
|
||||
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<cv::Vec4i> 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<cv::Rect>(home_rect), cv::Scalar(0, 255, 0), 5);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace asst
|
||||
|
||||
virtual bool analyze() override;
|
||||
|
||||
const std::vector<Oper>& get_opers() const noexcept;
|
||||
virtual const std::vector<Oper>& get_opers() const noexcept;
|
||||
virtual const std::vector<Rect>& 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<Oper> m_opers;
|
||||
std::vector<Oper> m_opers; // 下方干员信息
|
||||
std::vector<Rect> m_homes; // 蓝色的家门位置
|
||||
|
||||
private:
|
||||
protected:
|
||||
// 该分析器不支持外部设置ROI
|
||||
virtual void set_roi(const Rect& roi) noexcept override
|
||||
{
|
||||
|
||||
68
src/MeoAssistant/BattlePerspectiveImageAnalyzer.cpp
Normal file
68
src/MeoAssistant/BattlePerspectiveImageAnalyzer.cpp
Normal file
@@ -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<Rect> 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::Rect>& 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<cv::uint8_t>(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;
|
||||
}
|
||||
25
src/MeoAssistant/BattlePerspectiveImageAnalyzer.h
Normal file
25
src/MeoAssistant/BattlePerspectiveImageAnalyzer.h
Normal file
@@ -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<Rect>& get_homes() const noexcept override;
|
||||
|
||||
// 设置非透视状态下蓝色家门的位置
|
||||
void set_src_homes(std::vector<Rect> src_homes);
|
||||
Point get_nearest_point() const noexcept;
|
||||
protected:
|
||||
bool placed_analyze(); // 识别可放置干员的位置
|
||||
|
||||
std::vector<Rect> m_src_homes;
|
||||
Point m_nearest_point;
|
||||
};
|
||||
}
|
||||
@@ -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<Role, 9> 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;
|
||||
}
|
||||
|
||||
@@ -12,5 +12,7 @@ namespace asst
|
||||
|
||||
protected:
|
||||
virtual bool _run() override;
|
||||
|
||||
bool auto_battle();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<ClInclude Include="AsstMsg.h" />
|
||||
<ClInclude Include="AutoRecruitTask.h" />
|
||||
<ClInclude Include="BattleImageAnalyzer.h" />
|
||||
<ClInclude Include="BattlePerspectiveImageAnalyzer.h" />
|
||||
<ClInclude Include="BattleTask.h" />
|
||||
<ClInclude Include="CreditShopImageAnalyzer.h" />
|
||||
<ClInclude Include="DronesForShamareTaskPlugin.h" />
|
||||
@@ -77,6 +78,7 @@
|
||||
<ClCompile Include="AbstractTaskPlugin.cpp" />
|
||||
<ClCompile Include="AutoRecruitTask.cpp" />
|
||||
<ClCompile Include="BattleImageAnalyzer.cpp" />
|
||||
<ClCompile Include="BattlePerspectiveImageAnalyzer.cpp" />
|
||||
<ClCompile Include="BattleTask.cpp" />
|
||||
<ClCompile Include="CreditShopImageAnalyzer.cpp" />
|
||||
<ClCompile Include="CreditShoppingTask.cpp" />
|
||||
|
||||
@@ -243,6 +243,9 @@
|
||||
<ClInclude Include="HashImageAnalyzer.h">
|
||||
<Filter>头文件\ImageAnalyzer\General</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BattlePerspectiveImageAnalyzer.h">
|
||||
<Filter>头文件\ImageAnalyzer\General</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Controller.cpp">
|
||||
@@ -395,6 +398,9 @@
|
||||
<ClCompile Include="HashImageAnalyzer.cpp">
|
||||
<Filter>源文件\ImageAnalyzer\General</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BattlePerspectiveImageAnalyzer.cpp">
|
||||
<Filter>源文件\ImageAnalyzer</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\resource\config.json">
|
||||
|
||||
Reference in New Issue
Block a user