From a9ff54a3d60e87f1ff3fdd522cf5913ebd925f2e Mon Sep 17 00:00:00 2001 From: MistEO Date: Tue, 22 Mar 2022 21:20:46 +0800 Subject: [PATCH] =?UTF-8?q?feat.=E5=AE=8C=E6=88=90=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=8A=84=E4=BD=9C=E4=B8=9A-=E9=83=A8=E7=BD=B2=E5=B9=B2?= =?UTF-8?q?=E5=91=98=E5=85=A8=E9=83=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/battle/test.json | 3 + resource/tasks.json | 20 +++ src/MeoAssistant/BattleImageAnalyzer.cpp | 33 ++++- src/MeoAssistant/BattleImageAnalyzer.h | 18 +-- src/MeoAssistant/BattleProcessTask.cpp | 157 ++++++++++++++--------- src/MeoAssistant/BattleProcessTask.h | 4 + 6 files changed, 163 insertions(+), 72 deletions(-) diff --git a/resource/battle/test.json b/resource/battle/test.json index 9cb5478ed2..4c18465410 100644 --- a/resource/battle/test.json +++ b/resource/battle/test.json @@ -45,6 +45,9 @@ ], "direction": 1 }, + { + "type": 100 + }, { "name": "临光", "location": [ diff --git a/resource/tasks.json b/resource/tasks.json index 87d82cc2fc..9ebb09209a 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3265,6 +3265,16 @@ 60 ] }, + "BattleSpeedUp": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 1070, + 20, + 60, + 60 + ] + }, "BattlePause": { "algorithm": "JustReturn", "action": "ClickRect", @@ -3307,6 +3317,16 @@ 255 ] }, + "BattleCostData": { + "algorithm": "OcrDetect", + "text": [], + "roi": [ + 1197, + 492, + 83, + 48 + ] + }, "BattleUseOper": { "algorithm": "JustReturn", "preDelay": 500, diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp index 4cd35e089c..bbf73c586f 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.cpp +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -42,7 +42,15 @@ bool asst::BattleImageAnalyzer::analyze() ret &= kills_analyze(); } - return true; + if (m_target & Target::Cost) { + ret &= cost_analyze(); + } + + if (m_target & Target::Vacancies) { + ret &= vacancies_analyze(); + } + + return ret; } const std::vector& asst::BattleImageAnalyzer::get_opers() const noexcept @@ -108,7 +116,7 @@ bool asst::BattleImageAnalyzer::opers_analyze() oper.role = oper_role_analyze(role_rect); Rect cost_rect = flag_mrect.rect.move(cost_move); - oper.cost = oper_cost_analyze(cost_rect); + //oper.cost = oper_cost_analyze(cost_rect); oper.index = index++; m_opers.emplace_back(std::move(oper)); @@ -381,7 +389,7 @@ bool asst::BattleImageAnalyzer::kills_analyze() // 这里的结果应该是 "击杀数/总的敌人数",例如 "0/41" std::string kills_res = kills_analyzer.get_result().front().text; - int pos = kills_res.find('/'); + size_t pos = kills_res.find('/'); if (pos == std::string::npos) { return false; } @@ -400,7 +408,24 @@ bool asst::BattleImageAnalyzer::kills_analyze() bool asst::BattleImageAnalyzer::cost_analyze() { - return false; + OcrImageAnalyzer cost_analyzer(m_image); + cost_analyzer.set_task_info("BattleKillsFlag"); + + if (!cost_analyzer.analyze()) { + return false; + } + + cost_analyzer.sort_result_by_score(); + std::string cost_str = cost_analyzer.get_result().front().text; + + if (cost_str.empty() || + !std::all_of(cost_str.cbegin(), cost_str.cend(), + [](char c) -> bool {return std::isdigit(c);})) { + return false; + } + m_cost = std::stoi(cost_str); + + return true; } bool asst::BattleImageAnalyzer::vacancies_analyze() diff --git a/src/MeoAssistant/BattleImageAnalyzer.h b/src/MeoAssistant/BattleImageAnalyzer.h index 4de90424d2..29c67b96ea 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.h +++ b/src/MeoAssistant/BattleImageAnalyzer.h @@ -8,15 +8,16 @@ namespace asst class BattleImageAnalyzer : public AbstractImageAnalyzer { public: - enum Target // 需要识别的目标 + enum Target // 需要识别的目标 { - HP, // 剩余生命值 - Home, // 蓝色的家门 - Oper, // 下方的干员信息 - Skill, // cd 转好了可以使用的技能 - Kills, // 击杀数 - Cost, // 费用 - Vacancies, // 剩余可部署干员数 + None = 0, + HP = 1, // 剩余生命值 + Home = 2, // 蓝色的家门 + Oper = 4, // 下方的干员信息 + Skill = 8, // cd 转好了可以使用的技能 + Kills = 16, // 击杀数 + Cost = 32, // 费用 + Vacancies = 64, // 剩余可部署干员数 // 肉鸽模式需要用到的识别 Roguelike = Home | HP | Oper | Skill }; @@ -55,6 +56,7 @@ namespace asst std::vector m_ready_skills; // 可以释放的技能(Rect实际是干员的位置) int m_hp = 0; // 剩余生命值 int m_kills = 0; // 击杀数 + int m_cost = 0; // 部署费用 protected: // 该分析器不支持外部设置ROI diff --git a/src/MeoAssistant/BattleProcessTask.cpp b/src/MeoAssistant/BattleProcessTask.cpp index 2355e06dc7..0c37177bba 100644 --- a/src/MeoAssistant/BattleProcessTask.cpp +++ b/src/MeoAssistant/BattleProcessTask.cpp @@ -28,7 +28,7 @@ bool asst::BattleProcessTask::_run() ; } - for (const BattleAction& action : m_actions.actions) { + for (const auto& action : m_actions.actions) { do_action(action); } @@ -59,18 +59,18 @@ bool asst::BattleProcessTask::get_stage_info() bool asst::BattleProcessTask::analyze_opers_preview() { - BattleImageAnalyzer analyzer(m_ctrler->get_image()); - analyzer.set_target(BattleImageAnalyzer::Target::Oper); - if (!analyzer.analyze()) { + BattleImageAnalyzer oper_analyzer(m_ctrler->get_image()); + oper_analyzer.set_target(BattleImageAnalyzer::Target::Oper); + if (!oper_analyzer.analyze()) { return false; } // TODO: 干员头像出来之后,还要过 2 秒左右才可以点击,这里可能还要加个延时 battle_pause(); - auto opers = analyzer.get_opers(); + auto opers = oper_analyzer.get_opers(); for (size_t i = 0; i != opers.size(); ++i) { - const auto& cur_oper = analyzer.get_opers(); + const auto& cur_oper = oper_analyzer.get_opers(); size_t offset = opers.size() > cur_oper.size() ? opers.size() - cur_oper.size() : 0; m_ctrler->click(cur_oper.at(i - offset).rect); @@ -95,8 +95,8 @@ bool asst::BattleProcessTask::analyze_opers_preview() m_opers_info.emplace(std::move(oper_name), std::move(opers.at(i))); // 干员特别多的时候,任意干员被点开,都会导致下方的干员图标被裁剪和移动。所以这里需要重新识别一下 - analyzer.set_image(image); - analyzer.analyze(); + oper_analyzer.set_image(image); + oper_analyzer.analyze(); } battle_pause(); cancel_selection(); @@ -112,10 +112,6 @@ bool asst::BattleProcessTask::update_opers_info() return false; } const auto& cur_opers_info = analyzer.get_opers(); - // 干员数量没有变化,无需更新 - if (cur_opers_info.size() == m_opers_info.size()) { - return true; - } decltype(m_opers_info) pre_opers_info; m_opers_info.swap(pre_opers_info); @@ -124,12 +120,12 @@ bool asst::BattleProcessTask::update_opers_info() for (const auto& cur_oper : cur_opers_info) { int left_index = 0; int right_index = 0; - // 干员数变多了,干员可能位于 [之前的位置, 之前的位置 + |size_change|] 之间 + // 干员数变多了,之前的干员可能位于 [当前位置 - |size_change|, 当前位置 ] 之间 if (is_increased) { - left_index = static_cast(cur_oper.index); - right_index = static_cast(cur_oper.index) + size_change; + left_index = static_cast(cur_oper.index) - size_change; + right_index = static_cast(cur_oper.index); } - // 干员数减少了,干员可能位于 [之前的位置 - |size_change|, 之前的位置] 之间 + // 干员数减少了,之前的干员可能位于 [当前位置 , 当前位置 + |size_change|] 之间 else { left_index = static_cast(cur_oper.index); right_index = static_cast(cur_oper.index) - size_change; // size_change 是 负值 @@ -143,25 +139,34 @@ bool asst::BattleProcessTask::update_opers_info() ranged_iters.emplace_back(iter); } } - MatchImageAnalyzer avatar_analyzer(cur_oper.avatar); - avatar_analyzer.set_task_info("BattleAvatarData"); - decltype(ranged_iters)::value_type matched_iter; - MatchRect matched_result; - // 遍历比较,得分最高的那个就说明是对应的那个 - for (const auto& iter : ranged_iters) { - avatar_analyzer.set_templ(iter->second.avatar); - if (!avatar_analyzer.analyze()) { - continue; - } - if (matched_result.score < avatar_analyzer.get_result().score) { - matched_result = avatar_analyzer.get_result(); - matched_iter = iter; + decltype(ranged_iters)::value_type matched_iter = ranged_iters.front(); + + if (ranged_iters.empty()) { + Log.error("ranged_iters empty", left_index, right_index); + return false; + } + else if (ranged_iters.size() > 1) { + MatchImageAnalyzer avatar_analyzer(cur_oper.avatar); + avatar_analyzer.set_task_info("BattleAvatarData"); + MatchRect matched_result; + + // 遍历比较,得分最高的那个就说明是对应的那个 + for (const auto& iter : ranged_iters) { + avatar_analyzer.set_templ(iter->second.avatar); + if (!avatar_analyzer.analyze()) { + continue; + } + if (matched_result.score < avatar_analyzer.get_result().score) { + matched_result = avatar_analyzer.get_result(); + matched_iter = iter; + } } } - if (matched_result.score == 0) { - continue; + else { + ; // 只有一个干员的时候就不用计算,就是唯一的那个 } + auto temp_oper = cur_oper; temp_oper.name = matched_iter->first; // 保存当前干员信息 @@ -175,19 +180,65 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) if (!wait_condition(action)) { return false; } - while (!update_opers_info()) { - ; + + switch (action.type) { + case BattleActionType::Deploy: + return oper_deploy(action); + break; + case BattleActionType::Retreat: + break; + case BattleActionType::UseSkill: + break; + case BattleActionType::SwitchSpeed: + return battle_speedup(); + break; + case BattleActionType::SlowMode: + break; } + return false; +} + +bool asst::BattleProcessTask::wait_condition(const BattleAction& action) +{ + if (action.kills_condition <= 0) { + return true; + } + + constexpr int analyze_target = BattleImageAnalyzer::Target::Kills; + + while (true) { + auto image = m_ctrler->get_image(); + BattleImageAnalyzer analyzer(image); + analyzer.set_target(analyze_target); + + if (!analyzer.analyze()) { + return true; + } + if (action.kills_condition <= analyzer.get_kills()) { + return true; + } + } + + return false; +} + +bool asst::BattleProcessTask::oper_deploy(const BattleAction& action) +{ const auto use_oper_task_ptr = Task.get("BattleUseOper"); const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper"); - // TODO,临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换 - auto iter = m_opers_info.find(action.group_name); - if (iter == m_opers_info.cend()) { - Log.error("battle opers group", action.group_name, "not found"); - return false; - } + decltype(m_opers_info)::iterator iter; + do { + update_opers_info(); + + // TODO,临时调试方案。正式版本这里需要对 group_name -> oper_name 做一个转换 + iter = m_opers_info.find(action.group_name); + if (iter == m_opers_info.cend()) { + Log.error("battle opers group", action.group_name, "not found"); + return false; + } + } while (!iter->second.available); // 点击干员 Rect oper_rect = iter->second.rect; @@ -201,6 +252,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) sleep(use_oper_task_ptr->rear_delay); + // 拖动干员朝向 if (action.direction != BattleDeployDirection::No) { static const std::unordered_map DirectionMapping = { { BattleDeployDirection::Right, Point(1, 0)}, @@ -210,7 +262,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) { BattleDeployDirection::No, Point(0, 0)}, }; - // 计算往哪边拖动(干员朝向) + // 计算往哪边拖动 Point direction = DirectionMapping.at(action.direction); // 将方向转换为实际的 swipe end 坐标点 @@ -231,31 +283,16 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) return true; } -bool asst::BattleProcessTask::wait_condition(const BattleAction& action) -{ - constexpr int analyze_target = BattleImageAnalyzer::Target::Kills | BattleImageAnalyzer::Target::Vacancies | BattleImageAnalyzer::Target::Cost; - - while (true) { - auto image = m_ctrler->get_image(); - BattleImageAnalyzer analyzer(image); - analyzer.set_target(analyze_target); - - if (!analyzer.analyze()) { - return true; - } - if (action.kills_condition <= analyzer.get_kills()) { - return true; - } - } - - return false; -} - bool asst::BattleProcessTask::battle_pause() { return ProcessTask(*this, { "BattlePause" }).run(); } +bool asst::BattleProcessTask::battle_speedup() +{ + return ProcessTask(*this, { "BattleSpeedUp" }).run(); +} + bool asst::BattleProcessTask::cancel_selection() { return ProcessTask(*this, { "BattleCancelSelection" }).run(); diff --git a/src/MeoAssistant/BattleProcessTask.h b/src/MeoAssistant/BattleProcessTask.h index e509eeb57b..3d6069da3d 100644 --- a/src/MeoAssistant/BattleProcessTask.h +++ b/src/MeoAssistant/BattleProcessTask.h @@ -21,6 +21,7 @@ namespace asst bool get_stage_info(); bool battle_pause(); + bool battle_speedup(); bool cancel_selection(); // 取消选择干员 bool analyze_opers_preview(); bool update_opers_info(); @@ -28,12 +29,15 @@ namespace asst bool do_action(const BattleAction& action); bool wait_condition(const BattleAction& action); + bool oper_deploy(const BattleAction& action); + std::string m_stage_name; std::unordered_map m_side_tile_info; std::unordered_map m_normal_tile_info; BattleActionsGroup m_actions; + /* 实时更新的数据 */ std::unordered_map m_opers_info; }; }