feat.初步完成战斗上干员

This commit is contained in:
MistEO
2022-01-19 00:41:06 +08:00
parent a34590195d
commit 859d41726f
9 changed files with 267 additions and 14 deletions

View File

@@ -3070,6 +3070,12 @@
"ClueGiveTo1stConfirm"
]
},
"BattleUseOper": {
"algorithm": "JustReturn",
"preDelay": 500,
"rearDelay": 0,
"Doc": "pre 是从点击干员到干员信息弹出来等待的时间rear是干员上场到设置朝向之间等待的时间"
},
"BattleOpersFlag": {
"templThreshold": 0.85,
"roi": [

View File

@@ -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;
}

View File

@@ -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
{

View 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;
}

View 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;
};
}

View File

@@ -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;
}

View File

@@ -12,5 +12,7 @@ namespace asst
protected:
virtual bool _run() override;
bool auto_battle();
};
}

View File

@@ -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" />

View File

@@ -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">