mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-03 03:00:29 +08:00
Compare commits
6 Commits
feat/self_
...
rft/battle
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d434fb84c | ||
|
|
8681039067 | ||
|
|
dd36585b16 | ||
|
|
b58917c74b | ||
|
|
633fe73993 | ||
|
|
aa46d668d1 |
@@ -3783,6 +3783,14 @@
|
||||
"roi": [1060, 20, 80, 70],
|
||||
"maskRange": [170, 220]
|
||||
},
|
||||
"BattleTutorialDialog": {
|
||||
"maskRange": [1, 255]
|
||||
},
|
||||
"BattleDialogueLog": {
|
||||
"template": "DialogueLog.png",
|
||||
"roi": [27, 14, 64, 51]
|
||||
},
|
||||
"BattleResume": {},
|
||||
"BattlePause": {
|
||||
"algorithm": "JustReturn",
|
||||
"action": "ClickRect",
|
||||
@@ -4600,7 +4608,7 @@
|
||||
"BattleHasStarted": {
|
||||
"template": "empty.png",
|
||||
"roi": [1178, 33, 49, 39],
|
||||
"specialParams_Doc": "亮度阈值;点的数量",
|
||||
"specialParams_Doc": "亮度阈值; 点的数量",
|
||||
"specialParams": [245, 200]
|
||||
},
|
||||
"BattleSpeedButton": {
|
||||
|
||||
BIN
resource/template/BattleResume.png
Normal file
BIN
resource/template/BattleResume.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resource/template/BattleTutorialDialog.png
Normal file
BIN
resource/template/BattleTutorialDialog.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -258,7 +258,7 @@ bool asst::BattleHelper::update_deployment(bool init, const cv::Mat& reusable, b
|
||||
pause();
|
||||
// 在刚进入游戏的时候(画面刚刚完全亮起来的时候),点暂停是没反应的
|
||||
// 所以这里一直点,直到真的点上了为止
|
||||
if (!init || !check_pause_button()) {
|
||||
if (!init || !check_pause_button() || !check_in_battle()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::yield();
|
||||
@@ -545,12 +545,7 @@ bool asst::BattleHelper::check_pause_button(const cv::Mat& reusable)
|
||||
cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
|
||||
Matcher battle_flag_analyzer(image);
|
||||
battle_flag_analyzer.set_task_info("BattleOfficiallyBegin");
|
||||
bool ret = battle_flag_analyzer.analyze().has_value();
|
||||
|
||||
BattlefieldMatcher battle_flag_analyzer_2(image);
|
||||
auto battle_result_opt = battle_flag_analyzer_2.analyze();
|
||||
ret &= battle_result_opt && battle_result_opt->pause_button;
|
||||
return ret;
|
||||
return battle_flag_analyzer.analyze().has_value();
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::check_skip_plot_button(const cv::Mat& reusable)
|
||||
@@ -574,17 +569,31 @@ bool asst::BattleHelper::check_in_speed_up(const cv::Mat& reusable)
|
||||
return analyzer.analyze().has_value();
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::check_in_tutorial(const cv::Mat& image)
|
||||
{
|
||||
Matcher analyzer(image);
|
||||
analyzer.set_task_info("BattleTutorialDialog");
|
||||
return analyzer.analyze().has_value();
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::check_in_dialogue(const cv::Mat& image)
|
||||
{
|
||||
Matcher analyzer(image);
|
||||
analyzer.set_task_info("BattleDialogueLog");
|
||||
return analyzer.analyze().has_value();
|
||||
}
|
||||
|
||||
bool asst::BattleHelper::check_in_battle(const cv::Mat& reusable, bool weak)
|
||||
{
|
||||
cv::Mat image = reusable.empty() ? m_inst_helper.ctrler()->get_image() : reusable;
|
||||
if (weak) {
|
||||
BattlefieldMatcher analyzer(image);
|
||||
m_in_battle = analyzer.analyze().has_value();
|
||||
}
|
||||
else {
|
||||
bool in_battle = true;
|
||||
if (!weak) { // init时, weak = false
|
||||
check_skip_plot_button(image);
|
||||
m_in_battle = check_pause_button(image);
|
||||
in_battle &= check_pause_button(image);
|
||||
}
|
||||
BattlefieldMatcher analyzer(image);
|
||||
in_battle &= analyzer.analyze().has_value();
|
||||
m_in_battle = in_battle;
|
||||
return m_in_battle;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ protected:
|
||||
bool check_pause_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_skip_plot_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_in_speed_up(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_in_tutorial(const cv::Mat& image = cv::Mat());
|
||||
bool check_in_dialogue(const cv::Mat& image = cv::Mat());
|
||||
virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true);
|
||||
virtual bool wait_until_start(bool weak = true);
|
||||
bool wait_until_end(bool weak = true);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Common/AsstTypes.h"
|
||||
#include "Config/TaskData.h"
|
||||
#include "Task/Miscellaneous/BattleFormationTask.h"
|
||||
#include "Utils/ImageIo.hpp"
|
||||
#include "Utils/Logger.hpp"
|
||||
#include "Utils/NoWarningCV.h"
|
||||
@@ -23,6 +24,25 @@ asst::DebugTask::DebugTask(const AsstCallback& callback, Assistant* inst) :
|
||||
|
||||
bool asst::DebugTask::run()
|
||||
{
|
||||
auto image = imread(utils::path("C:/users/status102/desktop/fight.png"));
|
||||
auto image2 = imread(utils::path("C:/users/status102/desktop/fight2.png"));
|
||||
auto image_ = imread(utils::path("C:/users/status102/desktop/fight-.png"));
|
||||
bool ret = false;
|
||||
cv::Mat start = imread(utils::path("C:/users/status102/desktop/start.png"));
|
||||
cv::Mat out;
|
||||
|
||||
cv::cvtColor(image2, out, cv::COLOR_BGR2HSV);
|
||||
cv::cvtColor(image, out, cv::COLOR_BGR2HSV);
|
||||
cv::cvtColor(start, out, cv::COLOR_BGR2HSV);
|
||||
|
||||
Matcher battle_flag_analyzer(start);
|
||||
battle_flag_analyzer.set_task_info("BattleOfficiallyBegin");
|
||||
ret = battle_flag_analyzer.analyze().has_value();
|
||||
|
||||
BattlefieldMatcher battle_flag_analyzer_2(start);
|
||||
auto battle_result_opt = battle_flag_analyzer_2.analyze();
|
||||
ret &= battle_result_opt && battle_result_opt->pause_button && !battle_result_opt->is_pasuing;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -429,20 +429,40 @@ bool asst::BattleProcessTask::check_in_battle(const cv::Mat& reusable, bool weak
|
||||
LogTraceFunction;
|
||||
|
||||
cv::Mat image = reusable.empty() ? ctrler()->get_image() : reusable;
|
||||
|
||||
if (weak) {
|
||||
BattlefieldMatcher analyzer(image);
|
||||
auto result = analyzer.analyze();
|
||||
m_in_battle = result.has_value();
|
||||
if (m_in_battle && !result->pause_button) {
|
||||
if (check_skip_plot_button(image) && check_in_speed_up(image)) {
|
||||
speed_up();
|
||||
bool in_battle = true;
|
||||
if (!weak) { // init时, weak = false
|
||||
check_skip_plot_button(image);
|
||||
in_battle &= check_pause_button(image);
|
||||
}
|
||||
BattlefieldMatcher analyzer(image);
|
||||
analyzer.set_object_of_interest({ .pause_button_init = !weak });
|
||||
auto result = analyzer.analyze();
|
||||
in_battle &= result.has_value() && (weak || result->pause_button);
|
||||
if (in_battle) {
|
||||
if (!result->pause_button) {
|
||||
if (check_skip_plot_button(image) && check_in_speed_up(image)) { // 带右上角skip的关卡剧情
|
||||
speed_up(); // 跳过剧情后, 方舟会自动恢复1倍速, 如果是2倍速就恢复一下
|
||||
}
|
||||
}
|
||||
else if (result->is_pasuing && check_in_tutorial(image)) { // 关卡内教程暂停
|
||||
Log.info(__FUNCTION__, "In tutorial pause");
|
||||
while (!need_exit() && check_in_tutorial(image)) {
|
||||
cancel_oper_selection();
|
||||
image = ctrler()->get_image();
|
||||
}
|
||||
Log.info(__FUNCTION__, "Out tutorial pause");
|
||||
}
|
||||
}
|
||||
else {
|
||||
check_skip_plot_button(image);
|
||||
m_in_battle = check_pause_button(image);
|
||||
else if (check_in_dialogue(image)) {
|
||||
Log.info(__FUNCTION__, "In dialogue pause");
|
||||
while (!need_exit() && check_in_dialogue(image)) {
|
||||
cancel_oper_selection();
|
||||
image = ctrler()->get_image();
|
||||
}
|
||||
Log.info(__FUNCTION__, "Out dialogue pause");
|
||||
in_battle = m_in_battle; // 战斗开始/战斗中剧情, 不变更战斗状态
|
||||
}
|
||||
|
||||
m_in_battle = in_battle;
|
||||
return m_in_battle;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ BattlefieldMatcher::ResultOpt BattlefieldMatcher::analyze() const
|
||||
Result result;
|
||||
|
||||
if (m_object_of_interest.flag) {
|
||||
result.pause_button = pause_button_analyze();
|
||||
if (!result.pause_button && !hp_flag_analyze() && !kills_flag_analyze() && !cost_symbol_analyze()) {
|
||||
bool is_pausing = false;
|
||||
result.pause_button = pause_button_analyze(is_pausing);
|
||||
if (result.pause_button) {
|
||||
result.is_pasuing = is_pausing;
|
||||
}
|
||||
else if (!hp_flag_analyze() && !kills_flag_analyze() && !cost_symbol_analyze()) {
|
||||
// flag 表明当前画面是在战斗场景的,不在的就没必要识别了
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -350,19 +354,53 @@ std::optional<int> BattlefieldMatcher::costs_analyze() const
|
||||
return std::stoi(cost_str);
|
||||
}
|
||||
|
||||
bool BattlefieldMatcher::pause_button_analyze() const
|
||||
bool BattlefieldMatcher::pause_button_analyze(bool& is_pausing) const
|
||||
{
|
||||
auto task_ptr = Task.get("BattleHasStarted");
|
||||
cv::Mat roi = m_image(make_rect<cv::Rect>(task_ptr->roi));
|
||||
cv::Mat roi_gray;
|
||||
cv::cvtColor(roi, roi_gray, cv::COLOR_BGR2GRAY);
|
||||
cv::Mat bin;
|
||||
const int value_threshold = task_ptr->special_params[0];
|
||||
cv::threshold(roi_gray, bin, value_threshold, 255, cv::THRESH_BINARY);
|
||||
int count = cv::countNonZero(bin);
|
||||
const int count_threshold = task_ptr->special_params[1];
|
||||
Log.trace(__FUNCTION__, "count", count, "threshold", count_threshold);
|
||||
cv::Mat roi = m_image(make_rect<cv::Rect>(task_ptr->roi));
|
||||
cv::Mat roi_gray, bin;
|
||||
int count;
|
||||
|
||||
if (m_object_of_interest.pause_button_init) {
|
||||
cv::cvtColor(roi, roi_gray, cv::COLOR_BGR2GRAY);
|
||||
cv::threshold(roi_gray, bin, value_threshold, 255, cv::THRESH_BINARY);
|
||||
count = cv::countNonZero(bin);
|
||||
}
|
||||
else {
|
||||
cv::cvtColor(roi, roi_gray, cv::COLOR_BGR2HSV);
|
||||
cv::inRange(roi_gray, cv::Scalar { 0, 0, 120 }, cv::Scalar { 130, 30, 255 }, bin);
|
||||
count = cv::countNonZero(bin);
|
||||
}
|
||||
|
||||
// 区分暂停和恢复按钮
|
||||
// 暂停按钮是两条竖线,左右两侧会有较多的白点
|
||||
// 恢复按钮是一个三角形,右侧会有较多的白点,左侧较少
|
||||
// 将ROI区域分为左右两部分进行分析
|
||||
int width = bin.cols;
|
||||
int left_half = width / 2;
|
||||
|
||||
cv::Mat left_region = bin(cv::Rect(0, 0, left_half, bin.rows));
|
||||
cv::Mat right_region = bin(cv::Rect(left_half, 0, width - left_half, bin.rows));
|
||||
|
||||
int left_count = cv::countNonZero(left_region);
|
||||
int right_count = cv::countNonZero(right_region);
|
||||
float left_right_ratio = left_count / (float)(right_count + 1); // 避免除零
|
||||
LogTrace << __FUNCTION__ << "count" << count << "threshold" << count_threshold << "left count" << left_count
|
||||
<< "right count" << right_count << "ratio" << left_right_ratio;
|
||||
|
||||
// 根据左右非零像素比例判断按钮状态
|
||||
// 左右两边都有较多点,可能是暂停按钮(两条竖线)
|
||||
// 左边点较少,右边点较多,可能是恢复按钮(三角形)
|
||||
if (count > count_threshold) {
|
||||
// 阈值roi强关联, 2025.05.19 测试值: 暂停按钮0.99, 恢复按钮>3.3
|
||||
is_pausing = left_right_ratio > 2.0f;
|
||||
}
|
||||
|
||||
if (count > 800) {
|
||||
Log.warn("怪怪的");
|
||||
}
|
||||
#ifdef ASST_DEBUG
|
||||
cv::rectangle(m_image_draw, make_rect<cv::Rect>(task_ptr->roi), cv::Scalar(0, 0, 255), 2);
|
||||
cv::putText(
|
||||
|
||||
@@ -10,6 +10,7 @@ class BattlefieldMatcher : public VisionHelper
|
||||
public:
|
||||
struct ObjectOfInterest
|
||||
{
|
||||
bool pause_button_init = false; // 暂停按钮是否按战斗开始判断 (高阈值要求
|
||||
bool flag = true;
|
||||
bool deployment = false;
|
||||
bool kills = false;
|
||||
@@ -30,7 +31,8 @@ public:
|
||||
|
||||
// bool in_detail = false;
|
||||
bool speed_button = false;
|
||||
bool pause_button = false;
|
||||
bool pause_button = false; // 是否有暂停/恢复按钮
|
||||
bool is_pasuing = false; // 是否在暂停状态
|
||||
};
|
||||
|
||||
using ResultOpt = std::optional<Result>;
|
||||
@@ -47,7 +49,8 @@ public:
|
||||
protected:
|
||||
bool hp_flag_analyze() const;
|
||||
bool kills_flag_analyze() const;
|
||||
bool pause_button_analyze() const;
|
||||
// 判断是否存在暂停/恢复按钮, 如果存在, is_pausing 表示当前是否处于暂停状态
|
||||
bool pause_button_analyze(bool& is_pausing) const;
|
||||
|
||||
std::vector<battle::DeploymentOper> deployment_analyze() const; // 识别干员
|
||||
battle::Role oper_role_analyze(const Rect& roi) const;
|
||||
|
||||
Reference in New Issue
Block a user