Compare commits

...

6 Commits

Author SHA1 Message Date
pre-commit-ci[bot]
8d434fb84c chore: Auto update by pre-commit hooks [skip changelog] 2025-05-19 14:38:02 +00:00
status102
8681039067 chore: 点剧情
bug好多,再想想
2025-05-19 22:36:33 +08:00
status102
dd36585b16 fix: 逻辑取反 2025-05-19 20:59:56 +08:00
pre-commit-ci[bot]
b58917c74b chore: Auto update by pre-commit hooks [skip changelog] 2025-05-19 06:55:01 +00:00
status102
633fe73993 feat: 自动战斗自动点击剧情 2025-05-19 14:52:16 +08:00
status102
aa46d668d1 fix: 关卡内教程降低暂停按钮亮度,导致无法识别 2025-05-19 12:45:38 +08:00
9 changed files with 137 additions and 37 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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