mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 17:57:01 +08:00
* feat: 作业视频识别,基本功能完成 Squashed commit of the following: commit 3488507ebfb291b21ce9dae2d77536c39fc74840 Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 19:05:19 2023 +0800 chore: rename commit ad3446fd823db3ea5cd657cf216fcfdd7063ce0b Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 19:02:34 2023 +0800 perf: 优化视频识别结构 commit 5e5553e3720aa7c8c81d1667af96ffdc192d4281 Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 18:53:13 2023 +0800 fix: build error commit ffe850ea49008e2c7cef281159ef673ffae735f2 Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 18:40:46 2023 +0800 perf: 识别识别不需要连接模拟器 commit 56711ea3ded5b8832b9399591cb93064740ba23c Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 18:40:20 2023 +0800 perf: 优化视频读取性能 commit 1ce5ea3a61a6f270d834d1f2346832debd83f70f Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 16:19:08 2023 +0800 feat: WPF UI 初步集成视频识别 commit d741d0fbef48829e502d9b641d918969c94a7352 Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 15:18:58 2023 +0800 chore: 一些重命名 commit a14652773989c35788dff0262c70cbc4c8c0cd0d Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 14:35:48 2023 +0800 chore: rename and fix build error commit 5ea7d52fab67d7085aceb3147b6940781f5e4b93 Merge: 89db75e0b8bb725eAuthor: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 14:25:15 2023 +0800 Merge branch 'dev' into feat/video_to_json commit 89db75e05cea969417462dfcffb8e0b68233a199 Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 03:49:41 2023 +0800 chore: 导出json文件名 commit 6e1804b1dd49781dfeffc4a846321f8ebfff8b8c Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 03:47:38 2023 +0800 fix: 修复关卡识别错误 commit cc0dab1c1b0e2ff4ce2d62e3996516b13039be2b Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 03:31:39 2023 +0800 feat: 完成关卡名识别、重命名一些变量 commit bf1ab83bcfdeb64dad06c2cc3ac649c77baf317e Author: MistEO <mistereo@hotmail.com> Date: Sat Apr 1 02:23:24 2023 +0800 feat: 第一份作业导出 commit 76ae6262b64f7e7365cd1e7783b2cd4db61600ca Author: MistEO <mistereo@hotmail.com> Date: Fri Mar 31 21:43:00 2023 +0800 chore: rename commit 493cb9c3ab4718f15316c357080fec6930c89a97 Author: MistEO <mistereo@hotmail.com> Date: Fri Mar 31 20:13:45 2023 +0800 perf: 更新战斗识别相关模型 commit 623c80c76792a24c37d7dad5addb8c00341a5688 Author: MistEO <mistereo@hotmail.com> Date: Thu Mar 30 00:02:47 2023 +0800 refactor: 完成视频切片重构 commit 73bbf7c8727d911effa298ccff0a4ae29376baed Author: MistEO <mistereo@hotmail.com> Date: Tue Mar 28 23:54:39 2023 +0800 perf: 优化tensor转换 commit 13ba7a01e1c46ee1f0b6bea55ffaaf30fcf552c3 Merge: 48a4d765a0964485Author: MistEO <mistereo@hotmail.com> Date: Tue Mar 28 22:28:33 2023 +0800 Merge branch 'dev' into feat/video_to_json commit 48a4d765e2a1f1240e8f31352c21f58b565752d2 Author: MistEO <mistereo@hotmail.com> Date: Sat Mar 25 23:22:35 2023 +0800 fix: build error commit 02c80f4442e520998aff3b14fa4decbbc421b6c0 Author: MistEO <mistereo@hotmail.com> Date: Sat Mar 25 04:03:54 2023 +0800 perf: 优化技能ready识别模型 commit 7c15b54bd4a7c97772f27ac8bebaec97dd59f19c Author: MistEO <mistereo@hotmail.com> Date: Sat Mar 25 03:15:46 2023 +0800 perf: 重新训练干员方向检测模型 commit 450eea50a0601676e731411188a74cfc38f21a42 Author: MistEO <mistereo@hotmail.com> Date: Sat Mar 25 01:39:23 2023 +0800 feat: 画了一堆图 commit bf2bc66c6d52ecdc9f7ce0e7fc3a4891bbe59f7c Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 23:33:33 2023 +0800 feat: 完成干员部署方向识别 commit f3a0b4aee8e910d5ebc8e42486c5194bd6ffe51e Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 22:43:01 2023 +0800 feat: 输出draw,增加一些回调,修复干员位置检测 commit 772fd2dbeaef59812b563aef36808e15f8d33e08 Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 18:16:26 2023 +0800 feat: 初步识别上场干员 commit 4fda687bdcbc598c80576f741b518fae8fc87566 Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 17:29:57 2023 +0800 perf: 优化一些细节 commit c72a1d7c8a13c1d22d5e2598ae0db87e74d32c26 Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 01:10:29 2023 +0800 fix: 修复偶现误识别问题 commit 3021a7372dbad1f920aad346b49f115d4d2e073c Author: MistEO <mistereo@hotmail.com> Date: Wed Mar 22 01:01:21 2023 +0800 feat: 完成干员检测 commit e74072fbbeffd4c0cd7a4d67208a67797341f1fa Author: MistEO <mistereo@hotmail.com> Date: Tue Mar 21 01:34:20 2023 +0800 feat: 完成yolo干员检测模型集成 commit 27a10f0bd0529c71d09aa2434a1ef1fd0cac2449 Author: MistEO <mistereo@hotmail.com> Date: Mon Mar 20 20:39:44 2023 +0800 chore: rename commit 277a620819c1f368392c10f8d74a7b3e9f633a58 Author: MistEO <mistereo@hotmail.com> Date: Sun Mar 19 23:59:12 2023 +0800 feat: 完成编队->部署区识别 commit 9cfda5d295c34f37b592125898ec70105de77f7c Author: MistEO <mistereo@hotmail.com> Date: Sun Mar 19 02:02:11 2023 +0800 feat: 完成编队识别 MaaDeps 需要给 opencv features 加上 ffmpeg * fix: 调整部分参数适应硬解带来的颜色变化问题 * chore: 删除部分日志 * fix: build error * fix: 修复编队识别错误 * fix: 修复部署区识别错误(阈值问题 * fix: 修复初始部署区识别错误 * fix: 修复切片错误,修复部署区识别 * fix: 修了一些bug又制造了一些bug * fix: 修了一些bug又制造了一些bug * fix: typo * fix: 关键帧错误 * fix: 修复部署区偶现识别错误 * fix: 修复暂停按钮偶现识别错误 * feat: 新增技能识别,修复一些bug * fix: 修复变化判断错误 * chore: draw窗口优化
383 lines
11 KiB
C++
383 lines
11 KiB
C++
#include "BattleProcessTask.h"
|
|
|
|
#include "Utils/Ranges.hpp"
|
|
#include <chrono>
|
|
#include <future>
|
|
#include <thread>
|
|
|
|
#include "Utils/NoWarningCV.h"
|
|
|
|
#include "Config/Miscellaneous/BattleDataConfig.h"
|
|
#include "Config/Miscellaneous/CopilotConfig.h"
|
|
#include "Config/Miscellaneous/TilePack.h"
|
|
#include "Config/TaskData.h"
|
|
#include "Controller/Controller.h"
|
|
#include "Task/ProcessTask.h"
|
|
#include "Utils/Algorithm.hpp"
|
|
#include "Utils/ImageIo.hpp"
|
|
#include "Utils/Logger.hpp"
|
|
#include "Vision/MatchImageAnalyzer.h"
|
|
#include "Vision/Battle/BattleImageAnalyzer.h"
|
|
#include "Vision/Battle/BattleSkillReadyImageAnalyzer.h"
|
|
#include "Vision/OcrWithPreprocessImageAnalyzer.h"
|
|
|
|
using namespace asst::battle;
|
|
using namespace asst::battle::copilot;
|
|
|
|
asst::BattleProcessTask::BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain)
|
|
: AbstractTask(callback, inst, task_chain), BattleHelper(inst)
|
|
{}
|
|
|
|
bool asst::BattleProcessTask::_run()
|
|
{
|
|
LogTraceFunction;
|
|
clear();
|
|
|
|
if (!calc_tiles_info(m_stage_name)) {
|
|
Log.error("get stage info failed");
|
|
return false;
|
|
}
|
|
|
|
update_deployment(true);
|
|
to_group();
|
|
|
|
size_t action_size = get_combat_data().actions.size();
|
|
for (size_t i = 0; i < action_size && !need_exit() && m_in_battle; ++i) {
|
|
const auto& action = get_combat_data().actions.at(i);
|
|
do_action(action, i);
|
|
}
|
|
|
|
if (need_to_wait_until_end()) {
|
|
wait_until_end();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void asst::BattleProcessTask::clear()
|
|
{
|
|
BattleHelper::clear();
|
|
|
|
m_oper_in_group.clear();
|
|
m_in_bullet_time = false;
|
|
}
|
|
|
|
bool asst::BattleProcessTask::set_stage_name(const std::string& stage_name)
|
|
{
|
|
LogTraceFunction;
|
|
|
|
if (!BattleHelper::set_stage_name(stage_name)) {
|
|
json::value info = basic_info_with_what("UnsupportedLevel");
|
|
auto& details = info["details"];
|
|
details["level"] = m_stage_name;
|
|
callback(AsstMsg::SubTaskExtraInfo, info);
|
|
|
|
return false;
|
|
}
|
|
|
|
m_combat_data = Copilot.get_data();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool asst::BattleProcessTask::to_group()
|
|
{
|
|
std::unordered_map<std::string, std::vector<std::string>> groups;
|
|
for (const auto& [group_name, oper_list] : get_combat_data().groups) {
|
|
std::vector<std::string> oper_name_list;
|
|
ranges::transform(oper_list, std::back_inserter(oper_name_list), [](const auto& oper) { return oper.name; });
|
|
groups.emplace(group_name, std::move(oper_name_list));
|
|
}
|
|
|
|
std::unordered_set<std::string> char_set;
|
|
for (const auto& oper : m_cur_deployment_opers | views::values) {
|
|
char_set.emplace(oper.name);
|
|
}
|
|
|
|
auto result_opt = algorithm::get_char_allocation_for_each_group(groups, char_set);
|
|
if (!result_opt) {
|
|
return false;
|
|
}
|
|
m_oper_in_group = *result_opt;
|
|
|
|
std::unordered_map<std::string, std::string> ungrouped;
|
|
const auto& grouped_view = m_oper_in_group | views::values;
|
|
for (const auto& name : char_set) {
|
|
if (ranges::find(grouped_view, name) != grouped_view.end()) {
|
|
continue;
|
|
}
|
|
ungrouped.emplace(name, name);
|
|
}
|
|
m_oper_in_group.merge(ungrouped);
|
|
|
|
for (const auto& action : m_combat_data.actions) {
|
|
const std::string& action_name = action.name;
|
|
if (action_name.empty() || m_oper_in_group.contains(action_name)) {
|
|
continue;
|
|
}
|
|
m_oper_in_group.emplace(action_name, action_name);
|
|
}
|
|
|
|
for (const auto& [group_name, oper_name] : m_oper_in_group) {
|
|
auto& this_group = get_combat_data().groups[group_name];
|
|
// there is a build error on macOS
|
|
// https://github.com/MaaAssistantArknights/MaaAssistantArknights/actions/runs/3779762713/jobs/6425284487
|
|
const std::string& oper_name_for_lambda = oper_name;
|
|
auto iter = ranges::find_if(this_group, [&](const auto& oper) { return oper.name == oper_name_for_lambda; });
|
|
if (iter == this_group.end()) {
|
|
continue;
|
|
}
|
|
m_skill_usage.emplace(oper_name, iter->skill_usage);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, size_t action_index)
|
|
{
|
|
LogTraceFunction;
|
|
|
|
notify_action(action);
|
|
|
|
if (!wait_condition(action)) {
|
|
return false;
|
|
}
|
|
if (action.pre_delay > 0) {
|
|
sleep_and_do_strategy(action.pre_delay);
|
|
// 等待之后画面可能会变化,更新下干员信息
|
|
update_deployment();
|
|
}
|
|
|
|
bool ret = false;
|
|
const std::string& name = get_name_from_group(action.name);
|
|
const auto& location = action.location;
|
|
|
|
switch (action.type) {
|
|
case ActionType::Deploy:
|
|
ret = deploy_oper(name, location, action.direction);
|
|
if (ret) m_in_bullet_time = false;
|
|
break;
|
|
|
|
case ActionType::Retreat:
|
|
ret = m_in_bullet_time ? click_retreat() : (location.empty() ? retreat_oper(name) : retreat_oper(location));
|
|
if (ret) m_in_bullet_time = false;
|
|
break;
|
|
|
|
case ActionType::UseSkill:
|
|
ret = m_in_bullet_time ? click_skill() : (location.empty() ? use_skill(name) : use_skill(location));
|
|
if (ret) m_in_bullet_time = false;
|
|
break;
|
|
|
|
case ActionType::SwitchSpeed:
|
|
ret = speed_up();
|
|
break;
|
|
|
|
case ActionType::BulletTime:
|
|
ret = enter_bullet_time_for_next_action(action_index + 1, location, name);
|
|
if (ret) m_in_bullet_time = true;
|
|
break;
|
|
|
|
case ActionType::SkillUsage:
|
|
m_skill_usage[action.name] = action.modify_usage;
|
|
ret = true;
|
|
break;
|
|
|
|
case ActionType::Output:
|
|
// DoNothing
|
|
ret = true;
|
|
break;
|
|
|
|
case ActionType::MoveCamera:
|
|
ret = move_camera(action.distance);
|
|
break;
|
|
|
|
case ActionType::SkillDaemon:
|
|
ret = wait_until_end();
|
|
break;
|
|
default:
|
|
ret = do_derived_action(action_index);
|
|
break;
|
|
}
|
|
|
|
sleep_and_do_strategy(action.post_delay);
|
|
|
|
return ret;
|
|
}
|
|
|
|
const std::string& asst::BattleProcessTask::get_name_from_group(const std::string& action_name)
|
|
{
|
|
auto iter = m_oper_in_group.find(action_name);
|
|
if (iter != m_oper_in_group.cend()) {
|
|
return iter->second;
|
|
}
|
|
m_oper_in_group.emplace(action_name, action_name);
|
|
return m_oper_in_group.at(action_name);
|
|
}
|
|
|
|
void asst::BattleProcessTask::notify_action(const battle::copilot::Action& action)
|
|
{
|
|
const static std::unordered_map<ActionType, std::string> ActionNames = {
|
|
{ ActionType::Deploy, "Deploy" },
|
|
{ ActionType::UseSkill, "UseSkill" },
|
|
{ ActionType::Retreat, "Retreat" },
|
|
{ ActionType::SkillDaemon, "SkillDaemon" },
|
|
{ ActionType::SwitchSpeed, "SwitchSpeed" },
|
|
{ ActionType::SkillUsage, "SkillUsage" },
|
|
{ ActionType::BulletTime, "BulletTime" },
|
|
{ ActionType::Output, "Output" },
|
|
{ ActionType::MoveCamera, "MoveCamera" },
|
|
{ ActionType::DrawCard, "DrawCard" },
|
|
{ ActionType::CheckIfStartOver, "CheckIfStartOver" },
|
|
};
|
|
|
|
json::value info = basic_info_with_what("CopilotAction");
|
|
info["details"] |= json::object {
|
|
{ "action", ActionNames.at(action.type) },
|
|
{ "target", action.name },
|
|
{ "doc", action.doc },
|
|
{ "doc_color", action.doc_color },
|
|
};
|
|
callback(AsstMsg::SubTaskExtraInfo, info);
|
|
}
|
|
|
|
bool asst::BattleProcessTask::wait_condition(const Action& action)
|
|
{
|
|
cv::Mat image;
|
|
auto update_image_if_empty = [&]() {
|
|
if (image.empty()) {
|
|
image = ctrler()->get_image();
|
|
check_in_battle(image);
|
|
}
|
|
};
|
|
auto do_strategy_and_update_image = [&]() {
|
|
do_strategic_action(image);
|
|
image = ctrler()->get_image();
|
|
};
|
|
|
|
if (action.cost_changes != 0) {
|
|
update_image_if_empty();
|
|
update_cost(image);
|
|
int pre_cost = m_cost;
|
|
|
|
while (!need_exit()) {
|
|
update_cost(image);
|
|
if (action.cost_changes != 0) {
|
|
if ((pre_cost + action.cost_changes < 0) ? (m_cost <= pre_cost + action.cost_changes)
|
|
: (m_cost >= pre_cost + action.cost_changes)) {
|
|
break;
|
|
}
|
|
}
|
|
if (!check_in_battle(image)) {
|
|
return false;
|
|
}
|
|
do_strategy_and_update_image();
|
|
}
|
|
}
|
|
|
|
if (m_kills < action.kills) {
|
|
update_image_if_empty();
|
|
while (!need_exit() && m_kills < action.kills) {
|
|
update_kills(image);
|
|
if (m_kills >= action.kills) {
|
|
break;
|
|
}
|
|
if (!check_in_battle(image)) {
|
|
return false;
|
|
}
|
|
do_strategy_and_update_image();
|
|
}
|
|
}
|
|
|
|
if (action.costs) {
|
|
update_image_if_empty();
|
|
while (!need_exit()) {
|
|
update_cost(image);
|
|
if (m_cost >= action.costs) {
|
|
break;
|
|
}
|
|
if (!check_in_battle(image)) {
|
|
return false;
|
|
}
|
|
do_strategy_and_update_image();
|
|
}
|
|
}
|
|
|
|
// 计算有几个干员在cd
|
|
if (action.cooling >= 0) {
|
|
update_image_if_empty();
|
|
while (!need_exit()) {
|
|
if (!update_deployment(false, image)) {
|
|
return false;
|
|
}
|
|
size_t cooling_count = ranges::count_if(m_cur_deployment_opers | views::values,
|
|
[](const auto& oper) -> bool { return oper.cooling; });
|
|
if (cooling_count == static_cast<size_t>(action.cooling)) {
|
|
break;
|
|
}
|
|
do_strategy_and_update_image();
|
|
}
|
|
}
|
|
|
|
// 部署干员还有额外等待费用够或 CD 转好
|
|
if (!m_in_bullet_time && action.type == ActionType::Deploy) {
|
|
const std::string& name = get_name_from_group(action.name);
|
|
update_image_if_empty();
|
|
while (!need_exit()) {
|
|
if (!update_deployment(false, image)) {
|
|
return false;
|
|
}
|
|
if (auto iter = m_cur_deployment_opers.find(name);
|
|
iter != m_cur_deployment_opers.cend() && iter->second.available) {
|
|
break;
|
|
}
|
|
do_strategy_and_update_image();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool asst::BattleProcessTask::enter_bullet_time_for_next_action(size_t next_index, const Point& location,
|
|
const std::string& name)
|
|
{
|
|
LogTraceFunction;
|
|
|
|
if (next_index > get_combat_data().actions.size()) {
|
|
Log.error("Bullet time does not have the next step!");
|
|
return false;
|
|
}
|
|
const auto& next_action = get_combat_data().actions.at(next_index);
|
|
|
|
bool ret = false;
|
|
switch (next_action.type) {
|
|
case ActionType::Deploy:
|
|
ret = click_oper_on_deployment(name);
|
|
break;
|
|
|
|
case ActionType::UseSkill:
|
|
case ActionType::Retreat:
|
|
ret = location.empty() ? click_oper_on_battlefield(name) : click_oper_on_battlefield(location);
|
|
break;
|
|
|
|
default:
|
|
Log.error("Bullet time 's next step is not deploy, skill or retreat!");
|
|
return false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void asst::BattleProcessTask::sleep_and_do_strategy(unsigned millisecond)
|
|
{
|
|
LogTraceScope(__FUNCTION__ + std::string(" ms: ") + std::to_string(millisecond));
|
|
|
|
using namespace std::chrono_literals;
|
|
const auto start = std::chrono::steady_clock::now();
|
|
const auto delay = millisecond * 1ms;
|
|
|
|
while (!need_exit() && std::chrono::steady_clock::now() - start < delay) {
|
|
do_strategic_action();
|
|
std::this_thread::yield();
|
|
}
|
|
}
|