From 18cc0074b62dc22b7e92e3a73a6bbe0521127770 Mon Sep 17 00:00:00 2001 From: DuKong <649740559@qq.com> Date: Fri, 21 Apr 2023 00:04:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=82=89=E9=B8=BD?= =?UTF-8?q?=E7=BC=96=E9=98=9F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/roguelike/recruitment.json | 20 +--- resource/tasks.json | 20 ++++ .../RoguelikeFormationTaskPlugin.cpp | 93 ++++++++++++++++--- .../Roguelike/RoguelikeFormationTaskPlugin.h | 9 +- .../Roguelike/RoguelikeRecruitTaskPlugin.cpp | 3 +- .../Vision/OcrWithFlagTemplImageAnalyzer.cpp | 7 ++ .../Vision/OcrWithFlagTemplImageAnalyzer.h | 2 + .../RoguelikeFormationImageAnalyzer.cpp | 34 +++++-- .../RoguelikeFormationImageAnalyzer.h | 2 + 9 files changed, 151 insertions(+), 39 deletions(-) diff --git a/resource/roguelike/recruitment.json b/resource/roguelike/recruitment.json index bb19c58abe..4550a49890 100644 --- a/resource/roguelike/recruitment.json +++ b/resource/roguelike/recruitment.json @@ -27,7 +27,7 @@ ], "team_complete_condition": [ { - "groups": [ "百嘉", "玛恩纳", "号角", "地面C" ], + "groups": [ "百嘉", "号角", "玛恩纳", "地面C" ], "threshold": 2 }, { @@ -43,11 +43,11 @@ "threshold": 1 }, { - "groups": [ "重装" ], + "groups": [ "重装", "刺客" ], "threshold": 1 }, { - "groups": [ "投锋", "挡人先锋" ], + "groups": [ "挡人先锋", "投锋" ], "threshold": 1 } ], @@ -1189,11 +1189,6 @@ "promote_priority": 0, "is_key": true, "recruit_priority_offsets": [ - { - "groups": [ "重装" ], - "threshold": 1, - "offset": -150 - }, { "groups": [ "单奶", "群奶" ], "threshold": 0, @@ -1207,14 +1202,7 @@ "skill": 1, "recruit_priority": 624, "promote_priority": 0, - "is_key": true, - "recruit_priority_offsets": [ - { - "groups": [ "重装" ], - "threshold": 1, - "offset": -200 - } - ] + "is_key": true }, { "name": "米格鲁", diff --git a/resource/tasks.json b/resource/tasks.json index 5716303628..3952e9fed3 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -8296,6 +8296,7 @@ "Roguelike@StageEncounterLeaveConfirm", "Roguelike@StageEncounterOptionLeave", "Roguelike@StageEncounterOptionUnknown", + "Roguelike@NextLevel#next", "Roguelike@StageEncounterOptions#next" ] }, @@ -8307,6 +8308,7 @@ "Roguelike@StageEncounterLeaveConfirm", "Roguelike@StageEncounterOptionFree", "Roguelike@StageEncounterOptionUnknown", + "Roguelike@NextLevel#next", "Roguelike@StageEncounterOptions#next" ] }, @@ -8318,6 +8320,7 @@ "Roguelike@StageEncounterLeaveConfirm", "Roguelike@StageEncounterOptionFree", "Roguelike@StageEncounterOptionUnknown", + "Roguelike@NextLevel#next", "Roguelike@StageEncounterOptions#next" ] }, @@ -9502,6 +9505,23 @@ ], "templThreshold": 0.9 }, + "RoguelikeFormationOcr": { + "algorithm": "OcrDetect", + "text": [], + "fullMatch": true, + "roi": [ + 205, + 83, + 100, + 23 + ], + "specificRect": [ + 100, + 0, + 0, + 0 + ] + }, "RoguelikeQuickFormationClearAndReselect": { "action": "ClickSelf", "roi": [ diff --git a/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.cpp index 432ec6ba46..57c75f2dc9 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.cpp +++ b/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.cpp @@ -3,11 +3,11 @@ #include "Utils/Ranges.hpp" #include "Config/TaskData.h" +#include "Config/Roguelike/RoguelikeRecruitConfig.h" #include "Controller/Controller.h" #include "Status.h" #include "Task/ProcessTask.h" #include "Utils/Logger.hpp" -#include "Vision/Roguelike/RoguelikeFormationImageAnalyzer.h" bool asst::RoguelikeFormationTaskPlugin::verify(AsstMsg msg, const json::value& details) const { @@ -57,8 +57,10 @@ bool asst::RoguelikeFormationTaskPlugin::_run() // 1. 这一页选满 8 个了 // 2. select 有值,但是重新识别后发现总的选择数量并没有增加(说明上面的click都没生效) bool reselect = false; + first_page_full = false; if (pre_selected == MaxNumOfOperPerPage) { + first_page_full = true; reselect = true; } if (!reselect && select_count != 0) { @@ -89,30 +91,95 @@ void asst::RoguelikeFormationTaskPlugin::clear_and_reselect() { // 清空并退出游戏会自动按等级重新排序 ProcessTask(*this, { "RoguelikeQuickFormationClearAndReselect" }).run(); + + oper_list.clear(); + cur_page = 1; + analyze(); - size_t select_count = analyze_and_select(); - // 说明第二页还有 - if (select_count == MaxNumOfOperPerPage) { + if (first_page_full) { // 说明第二页还有 ProcessTask(*this, { "RoguelikeRecruitOperListSlowlySwipeToTheRight" }).run(); - analyze_and_select(); + cur_page = 2; + analyze(); // 应该不可能还有第三页吧,不管了 } + + std::vector sorted_oper_list; + std::string rogue_theme = status()->get_properties(Status::RoguelikeTheme).value(); + const auto& team_complete_condition = RoguelikeRecruit.get_team_complete_info(rogue_theme); + const auto& group_list = RoguelikeRecruit.get_group_info(rogue_theme); + for (const auto& condition : team_complete_condition) { // 优先选择阵容核心干员 + int count = 0; + int require = condition.threshold; + for (const std::string& group_name : condition.groups) { + for (const auto& oper : oper_list) { + int group_id = RoguelikeRecruit.get_group_id(rogue_theme, oper.name); + std::string oper_group = group_list[group_id]; + if (oper_group == group_name) { + sorted_oper_list.emplace_back(oper); + count++; + } + if (count == require) break; + } + if (count == require) break; + } + } + for (const auto& oper : oper_list) { // 然后按默认排序选择 + if (sorted_oper_list.size() >= 13) break; + bool already_exist = false; + for (const auto& existing_oper : sorted_oper_list) { + if (oper.name == existing_oper.name) already_exist = true; + } + if (!already_exist) { + sorted_oper_list.emplace_back(oper); + } + } + for (const auto& oper : sorted_oper_list) { + select(oper); + } } -size_t asst::RoguelikeFormationTaskPlugin::analyze_and_select() +bool asst::RoguelikeFormationTaskPlugin::analyze() { RoguelikeFormationImageAnalyzer formation_analyzer(ctrler()->get_image()); if (!formation_analyzer.analyze()) { return false; } - size_t select_count = 0; - for (const auto& oper : formation_analyzer.get_result()) { - if (oper.selected) { - continue; + auto oper_result = formation_analyzer.get_result(); + for (auto& oper : oper_result) { + bool already_exist = false; + for (const auto& existing_oper : oper_list) { + if (oper.name == existing_oper.name) already_exist = true; + } + if (!already_exist) { + oper.page = cur_page; + oper_list.emplace_back(oper); } - ctrler()->click(oper.rect); - ++select_count; } - return select_count; + return true; + + //for (const auto& oper : oper_list) { + // if (oper.selected) { + // continue; + // } + // ctrler()->click(oper.rect); + // ++select_count; + //} + //return select_count; +} + +bool asst::RoguelikeFormationTaskPlugin::select(asst::RoguelikeFormationImageAnalyzer::FormationOper oper) +{ + if (cur_page != oper.page) { + if (cur_page == 1) { + ProcessTask(*this, { "RoguelikeRecruitOperListSlowlySwipeToTheRight" }).run(); + cur_page = 2; + } + else { + ProcessTask(*this, { "RoguelikeRecruitOperListSlowlySwipeToTheLeft" }).run(); + cur_page = 1; + } + } + ctrler()->click(oper.rect); + return true; } diff --git a/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.h b/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.h index 43fbd3e8f3..546e14462f 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.h +++ b/src/MaaCore/Task/Roguelike/RoguelikeFormationTaskPlugin.h @@ -1,5 +1,6 @@ #pragma once #include "Task/AbstractTaskPlugin.h" +#include "Vision/Roguelike/RoguelikeFormationImageAnalyzer.h" namespace asst { @@ -19,6 +20,12 @@ namespace asst virtual bool _run() override; void clear_and_reselect(); - size_t analyze_and_select(); + bool analyze(); + bool select(asst::RoguelikeFormationImageAnalyzer::FormationOper oper); + + private: + int cur_page; + bool first_page_full; + std::vector oper_list; }; } diff --git a/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp b/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp index 650421cdfb..0fea845aab 100644 --- a/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp +++ b/src/MaaCore/Task/Roguelike/RoguelikeRecruitTaskPlugin.cpp @@ -92,9 +92,10 @@ bool asst::RoguelikeRecruitTaskPlugin::_run() // __________________will-be-removed-end__________________ std::unordered_map group_count; + const auto& group_list = RoguelikeRecruit.get_group_info(rogue_theme); for (const auto& oper : chars_map) { int group_id = RoguelikeRecruit.get_group_id(rogue_theme, oper.first); - std::string group_name = RoguelikeRecruit.get_group_info(rogue_theme)[group_id]; + std::string group_name = group_list[group_id]; group_count[group_name]++; } diff --git a/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.cpp b/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.cpp index 4b528b10d2..063696a3ef 100644 --- a/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.cpp +++ b/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.cpp @@ -21,11 +21,13 @@ void asst::OcrWithFlagTemplImageAnalyzer::set_roi(const Rect& roi) noexcept bool asst::OcrWithFlagTemplImageAnalyzer::analyze() { m_all_result.clear(); + m_flag_result.clear(); if (!m_multi_match_image_analyzer.analyze()) { return false; } for (const auto& templ_res : m_multi_match_image_analyzer.get_result()) { + m_flag_result.emplace_back(templ_res); Rect roi = templ_res.rect.move(m_flag_rect_move); if (roi.x + roi.width >= WindowWidthDefault) { continue; @@ -64,3 +66,8 @@ std::vector& asst::OcrWithFlagTemplImageAnalyzer::get_result() n { return m_all_result; } + +std::vector& asst::OcrWithFlagTemplImageAnalyzer::get_flag_result() noexcept +{ + return m_flag_result; +} diff --git a/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.h b/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.h index 83e4c0bcf4..88f79318c4 100644 --- a/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.h +++ b/src/MaaCore/Vision/OcrWithFlagTemplImageAnalyzer.h @@ -18,6 +18,7 @@ namespace asst virtual const std::vector& get_result() const noexcept override; virtual std::vector& get_result() noexcept override; + std::vector& get_flag_result() noexcept; void set_task_info(const std::string& templ_task_name, const std::string& ocr_task_name); void set_flag_rect_move(Rect flag_rect_move); @@ -28,5 +29,6 @@ namespace asst MultiMatchImageAnalyzer m_multi_match_image_analyzer; Rect m_flag_rect_move; std::vector m_all_result; + std::vector m_flag_result; }; } diff --git a/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.cpp b/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.cpp index f5f1da9f36..80c44aaf9a 100644 --- a/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.cpp +++ b/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.cpp @@ -5,23 +5,40 @@ #include "Config/TaskData.h" #include "Utils/Logger.hpp" #include "Vision/MultiMatchImageAnalyzer.h" +#include "Vision/OcrWithFlagTemplImageAnalyzer.h" bool asst::RoguelikeFormationImageAnalyzer::analyze() { m_result.clear(); - MultiMatchImageAnalyzer opers_analyzer(m_image); - opers_analyzer.set_task_info("RoguelikeFormationOper"); + OcrWithFlagTemplImageAnalyzer analyzer(m_image); + analyzer.set_task_info("RoguelikeFormationOper", "RoguelikeFormationOcr"); + analyzer.set_replace(Task.get("CharsNameOcrReplace")->replace_map, + Task.get("CharsNameOcrReplace")->replace_full); + analyzer.set_threshold(Task.get("RoguelikeFormationOcr")->specific_rect.x); - if (!opers_analyzer.analyze()) { + if (!analyzer.analyze()) { return false; } - opers_analyzer.sort_result_vertical(); - const auto& all_opers = opers_analyzer.get_result(); - for (const MatchRect& oper_mr : all_opers) { + + analyzer.sort_result_vertical(); + auto& rect_list = analyzer.get_flag_result(); + ranges::sort(rect_list, [](const Rect& lhs, const Rect& rhs) -> bool { + if (std::abs(lhs.x - rhs.x) < 5) { + return lhs.y < rhs.y; + } + else { + return lhs.x < rhs.x; + } + }); + int pos = 0; + for (const auto& [_, text_rect, name] : analyzer.get_result()) { + Rect rect = rect_list[pos]; FormationOper oper; - oper.rect = oper_mr.rect; - oper.selected = selected_analyze(oper_mr.rect); + oper.rect = rect; + oper.selected = selected_analyze(rect); + oper.name = name; + pos++; #ifdef ASST_DEBUG if (oper.selected) { @@ -55,6 +72,7 @@ bool asst::RoguelikeFormationImageAnalyzer::selected_analyze(const Rect& roi) int upper = cv::countNonZero(bin(cv::Rect(bin.cols / 4, 0, bin.cols / 2, bin.rows / 2))); int lower = cv::countNonZero(bin(cv::Rect(bin.cols / 4, bin.rows / 2, bin.cols / 2, bin.rows / 2))); Log.trace("selected_analyze |", upper, ':', lower); + cv::imwrite("C:/code/MaaAssistantArknights/x64/Release/debug/roguelike/test_b.png", bin); return upper > 250 && lower < 2; } diff --git a/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.h b/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.h index 0c7a7751e8..ef97ad7a65 100644 --- a/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.h +++ b/src/MaaCore/Vision/Roguelike/RoguelikeFormationImageAnalyzer.h @@ -10,6 +10,8 @@ namespace asst { Rect rect; bool selected = false; + std::string name; + int page; // TODO };