diff --git a/resource/battle/WD-EX8-raid.json b/resource/battle/WD-EX8-raid.json index 24567bd6fb..992d2ee700 100644 --- a/resource/battle/WD-EX8-raid.json +++ b/resource/battle/WD-EX8-raid.json @@ -74,7 +74,7 @@ "direction": "右" }, { - "pre_delay": 5000, + "pre_delay": 8000, "name": "艾雅法拉", "type": "技能" }, @@ -96,15 +96,7 @@ "name": "推进之王" }, { - "kills": 36, - "name": "澄闪", - "location": [ - 2, - 6 - ], - "direction": "右" - }, - { + "pre_delay": 2000, "name": "史尔特尔", "location": [ 8, @@ -129,7 +121,7 @@ "type": "技能" }, { - "pre_delay": 7000, + "pre_delay": 2000, "name": "史尔特尔", "type": "技能" }, @@ -138,8 +130,13 @@ "type": "技能" }, { - "name": "澄闪", - "type": "技能" + "pre_delay": 10000, + "name": "推进之王", + "location": [ + 3, + 4 + ], + "direction": "下" }, { "name": "塞雷娅", @@ -173,13 +170,13 @@ { "name": "推进之王", "location": [ - 7, + 8, 3 ], - "direction": "上" + "direction": "左" }, { - "pre_delay": 6000, + "pre_delay": 3000, "name": "艾雅法拉", "type": "技能" }, diff --git a/resource/tasks.json b/resource/tasks.json index 77ce8aed86..cd9c2ba1f6 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -3552,6 +3552,21 @@ 10 ] }, + "BattleOperCooling": { + "template": "empty.png", + "specialThreshold": 6000, + "specialThreshold_Doc": "这个任务中作为符合range的像素点数量阈值", + "rectMove": [ + -45, + 6, + 75, + 120 + ], + "maskRange": [ + 0, + 10 + ] + }, "BattleOperRoleRange": { "algorithm": "JustReturn", "rectMove": [ diff --git a/src/MeoAssistant/AbstractTask.cpp b/src/MeoAssistant/AbstractTask.cpp index 4eec1afb33..0ab5fc2cba 100644 --- a/src/MeoAssistant/AbstractTask.cpp +++ b/src/MeoAssistant/AbstractTask.cpp @@ -133,6 +133,7 @@ bool AbstractTask::sleep(unsigned millisecond) return false; } if (millisecond == 0) { + std::this_thread::yield(); return true; } auto start = std::chrono::steady_clock::now(); diff --git a/src/MeoAssistant/AsstBattleDef.h b/src/MeoAssistant/AsstBattleDef.h index 0484f49243..3efc5bc5f0 100644 --- a/src/MeoAssistant/AsstBattleDef.h +++ b/src/MeoAssistant/AsstBattleDef.h @@ -92,5 +92,6 @@ namespace asst cv::Mat avatar; std::string name; size_t index; + bool cooling = false; }; } diff --git a/src/MeoAssistant/BattleImageAnalyzer.cpp b/src/MeoAssistant/BattleImageAnalyzer.cpp index b3f926bd87..7b217477f0 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.cpp +++ b/src/MeoAssistant/BattleImageAnalyzer.cpp @@ -99,6 +99,7 @@ bool asst::BattleImageAnalyzer::opers_analyze() const auto role_move = Task.get("BattleOperRoleRange")->rect_move; const auto cost_move = Task.get("BattleOperCostRange")->rect_move; const auto avlb_move = Task.get("BattleOperAvailable")->rect_move; + const auto cooling_move = Task.get("BattleOperCooling")->rect_move; size_t index = 0; for (const MatchRect& flag_mrect : flags_analyzer.get_result()) { @@ -118,10 +119,17 @@ bool asst::BattleImageAnalyzer::opers_analyze() } #endif + Rect cooling_rect = flag_mrect.rect.move(cooling_move); + oper.cooling = oper_cooling_analyze(cooling_rect); + if (oper.cooling && oper.available) { + Log.error("oper is available, but with cooling"); + } + Rect role_rect = flag_mrect.rect.move(role_move); oper.role = oper_role_analyze(role_rect); Rect cost_rect = flag_mrect.rect.move(cost_move); + // 费用识别的不太准,暂时也没用上,先注释掉,TODO:优化费用识别 //oper.cost = oper_cost_analyze(cost_rect); oper.index = index++; @@ -177,6 +185,32 @@ asst::BattleRole asst::BattleImageAnalyzer::oper_role_analyze(const Rect& roi) return result; } +bool asst::BattleImageAnalyzer::oper_cooling_analyze(const Rect& roi) +{ + const auto cooling_task_ptr = std::dynamic_pointer_cast( + Task.get("BattleOperCooling")); + + cv::Mat hsv; + cv::cvtColor(m_image(utils::make_rect(roi)), hsv, cv::COLOR_BGR2HSV); + std::vector channels; + cv::split(hsv, channels); + int mask_lowb = cooling_task_ptr->mask_range.first; + int mask_uppb = cooling_task_ptr->mask_range.second; + + int count = 0; + auto& h_channel = channels.at(0); + for (int i = 0; i != h_channel.rows; ++i) { + for (int j = 0; j != h_channel.cols; ++j) { + cv::uint8_t value = h_channel.at(i, j); + if (mask_lowb < value && value < mask_uppb) { + ++count; + } + } + } + Log.trace("oper_cooling_analyze |", count); + return count >= cooling_task_ptr->special_threshold; +} + int asst::BattleImageAnalyzer::oper_cost_analyze(const Rect& roi) { static const std::array NumName = { diff --git a/src/MeoAssistant/BattleImageAnalyzer.h b/src/MeoAssistant/BattleImageAnalyzer.h index 17219ddb0a..a8274d13e5 100644 --- a/src/MeoAssistant/BattleImageAnalyzer.h +++ b/src/MeoAssistant/BattleImageAnalyzer.h @@ -38,6 +38,7 @@ namespace asst protected: bool opers_analyze(); // 识别干员 BattleRole oper_role_analyze(const Rect& roi); + bool oper_cooling_analyze(const Rect& roi); int oper_cost_analyze(const Rect& roi); bool oper_available_analyze(const Rect& roi); diff --git a/src/MeoAssistant/BattleProcessTask.cpp b/src/MeoAssistant/BattleProcessTask.cpp index 365249b052..b729e9dae0 100644 --- a/src/MeoAssistant/BattleProcessTask.cpp +++ b/src/MeoAssistant/BattleProcessTask.cpp @@ -1,5 +1,8 @@ #include "BattleProcessTask.h" +#include +#include + #include "Controller.h" #include "Resource.h" #include "TaskData.h" @@ -25,7 +28,7 @@ bool asst::BattleProcessTask::_run() } while (!analyze_opers_preview()) { - ; + std::this_thread::yield(); } for (const auto& action : m_actions_group.actions) { @@ -140,24 +143,24 @@ bool asst::BattleProcessTask::update_opers_info(const cv::Mat& image) return false; } const auto& cur_opers_info = analyzer.get_opers(); + // 除非主动使用,不然可用干员数任何情况下都不会减少 + // 主动使用会 earse, 所以少了就是识别错了 + if (cur_opers_info.size() < m_cur_opers_info.size()) { + Log.error(__FUNCTION__, "Decrease in staff, Just return"); + return false; + } + decltype(m_cur_opers_info) pre_opers_info; m_cur_opers_info.swap(pre_opers_info); const int size_change = static_cast(cur_opers_info.size()) - static_cast(pre_opers_info.size()); - const bool is_increased = size_change > 0; for (const auto& cur_oper : cur_opers_info) { - int left_index = 0; - int right_index = 0; - // 干员数变多了,之前的干员可能位于 [当前位置 - |size_change|, 当前位置 ] 之间 - if (is_increased) { - left_index = std::max(0, static_cast(cur_oper.index) - size_change); - right_index = static_cast(cur_oper.index); - } - // 干员数减少了,之前的干员可能位于 [当前位置 , 当前位置 + |size_change|] 之间 - else { - left_index = static_cast(cur_oper.index); - right_index = static_cast(cur_oper.index) - size_change + 1; // size_change 是 负值 + if (cur_oper.cooling) { + continue; } + // 该干员在上一帧中可能的位置。需要考虑召唤者退场&可用召唤物消失的情况,所以 lhs-1 rhs+1 + int left_index = std::max(0, static_cast(cur_oper.index) - size_change - 1); + int right_index = static_cast(cur_oper.index + 1); std::vector ranged_iters; // 找出该干员可能对应的之前的谁 @@ -168,24 +171,11 @@ bool asst::BattleProcessTask::update_opers_info(const cv::Mat& image) } } - if (is_increased && !cur_oper.available) { - continue; - } - // 为空说明上一帧画面里没有这个干员,可能是撤退下来的,把所有已使用的都拿出来比较下 - if (ranged_iters.empty()) { - // 已使用的(可能是撤退下来的) = 所有干员 - 当前可用的干员 - // 求差集 - - //std::set_difference(m_all_opers_info.cbegin(), m_all_opers_info.cend(), - // pre_opers_info.cbegin(), pre_opers_info.cend(), - // std::back_inserter(ranged_iters) - //); - // set_difference 返回的是元素,但我这里需要迭代器,所以只能手写了 - for (auto iter = m_all_opers_info.cbegin(); iter != m_all_opers_info.cend(); ++iter) { - const std::string& key = iter->first; - if (pre_opers_info.find(key) == pre_opers_info.cend()) { - ranged_iters.emplace_back(iter); - } + // 干员也可能是撤退下来的,把所有已使用的都拿出来比较下 + for (auto iter = m_all_opers_info.cbegin(); iter != m_all_opers_info.cend(); ++iter) { + const std::string& key = iter->first; + if (pre_opers_info.find(key) == pre_opers_info.cend()) { + ranged_iters.emplace_back(iter); } } @@ -229,9 +219,7 @@ bool asst::BattleProcessTask::update_opers_info(const cv::Mat& image) } else { oper_name = matched_iter->first; - if (is_increased) { - m_used_opers.erase(oper_name); - } + m_used_opers.erase(oper_name); } auto temp_oper = cur_oper; @@ -248,7 +236,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) if (!wait_condition(action)) { return false; } - sleep(action.pre_delay); + sleep_with_possible_skill(action.pre_delay); bool ret = false; switch (action.type) { @@ -274,7 +262,7 @@ bool asst::BattleProcessTask::do_action(const BattleAction& action) return true; } break; } - sleep(action.rear_delay); + sleep_with_possible_skill(action.rear_delay); return ret; } @@ -291,6 +279,25 @@ bool asst::BattleProcessTask::wait_condition(const BattleAction& action) } try_possible_skill(image); + std::this_thread::yield(); + } + + // 部署干员还有额外等待费用够或 CD 转好 + if (action.type == BattleActionType::Deploy) { + const std::string& name = m_group_to_oper_mapping[action.group_name].name; + + while (true) { + const auto& image = m_ctrler->get_image(); + update_opers_info(image); + + if (auto iter = m_cur_opers_info.find(name); + iter != m_cur_opers_info.cend() && iter->second.available) { + break; + } + + try_possible_skill(image); + std::this_thread::yield(); + }; } return true; @@ -301,22 +308,9 @@ 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"); - decltype(m_cur_opers_info)::iterator iter; - BattleDeployOper oper_info; - do { - const auto& image = m_ctrler->get_image(); + const auto& oper_info = m_group_to_oper_mapping[action.group_name]; - update_opers_info(image); - - oper_info = m_group_to_oper_mapping[action.group_name]; - const std::string& name = oper_info.name; - iter = m_cur_opers_info.find(name); - if (iter == m_cur_opers_info.cend()) { - Log.info("battle opers group", name, "not found"); - } - - try_possible_skill(image); - } while (iter == m_cur_opers_info.cend() || !iter->second.available); + auto iter = m_cur_opers_info.find(oper_info.name); // 点击干员 Rect oper_rect = iter->second.rect; @@ -361,7 +355,7 @@ bool asst::BattleProcessTask::oper_deploy(const BattleAction& action) m_used_opers[iter->first] = BattleDeployInfo{ action.location, m_normal_tile_info[action.location].pos, - std::move(oper_info) }; + oper_info }; m_cur_opers_info.erase(iter); @@ -428,6 +422,28 @@ void asst::BattleProcessTask::try_possible_skill(const cv::Mat& image) } } +void asst::BattleProcessTask::sleep_with_possible_skill(unsigned millisecond) +{ + if (need_exit()) { + return; + } + if (millisecond == 0) { + return; + } + auto start = std::chrono::steady_clock::now(); + long long duration = 0; + + Log.trace("ready to sleep_with_possible_skill", millisecond); + + while (!need_exit() && duration < millisecond) { + duration = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start).count(); + try_possible_skill(m_ctrler->get_image()); + std::this_thread::yield(); + } + Log.trace("end of sleep_with_possible_skill", millisecond); +} + bool asst::BattleProcessTask::battle_pause() { return ProcessTask(*this, { "BattlePause" }).run(); diff --git a/src/MeoAssistant/BattleProcessTask.h b/src/MeoAssistant/BattleProcessTask.h index dee3544839..e75edafb6f 100644 --- a/src/MeoAssistant/BattleProcessTask.h +++ b/src/MeoAssistant/BattleProcessTask.h @@ -34,6 +34,7 @@ namespace asst bool use_skill(const BattleAction& action); void try_possible_skill(const cv::Mat& image); + void sleep_with_possible_skill(unsigned millisecond); std::string m_stage_name;