diff --git a/README.md b/README.md index 248ff2d91b..fd5ddb3b74 100644 --- a/README.md +++ b/README.md @@ -89,11 +89,11 @@ A game assistance for Arknights #### MuMu模拟器 -支持 +完美支持 #### 雷电模拟器 -勉强支持,偶尔能用偶尔用不了,莫名其妙的,有空再修,暂时不建议用_(:з」∠)_ +支持 #### 逍遥模拟器 diff --git a/resource/config.json b/resource/config.json index ca85628bd2..d21ac331e1 100644 --- a/resource/config.json +++ b/resource/config.json @@ -72,14 +72,14 @@ "screencap": "[Adb] -s 127.0.0.1:7555 exec-out screencap -p" } }, - "LDPlayer": { + "LDPlayerLocal": { "handle": { "class": "LDPlayerMainFrame", - "window": "雷电模拟器" + "window": "" }, "adb": { "path": "[EmulatorPath]adb.exe", - "connect": "[Adb] connect 127.0.0.1:5555", + "connect": "[Adb] devices", "click": "[Adb] -s emulator-5554 shell input tap [x] [y]", "swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]", "display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '", @@ -87,19 +87,19 @@ "screencap": "[Adb] -s emulator-5554 shell screencap -p" } }, - "LDPlayer64": { + "LDPlayerRemote": { "handle": { "class": "LDPlayerMainFrame", - "window": "雷电模拟器(64)" + "window": "" }, "adb": { "path": "[EmulatorPath]adb.exe", "connect": "[Adb] connect 127.0.0.1:5555", - "click": "[Adb] -s emulator-5554 shell input tap [x] [y]", - "swipe": "[Adb] -s emulator-5554 shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] -s emulator-5554 shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "click": "[Adb] -s 127.0.0.1:5555 shell input tap [x] [y]", + "swipe": "[Adb] -s 127.0.0.1:5555 shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s 127.0.0.1:5555 shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayRegex": "cur=%dx%d", - "screencap": "[Adb] -s emulator-5554 shell screencap -p" + "screencap": "[Adb] -s 127.0.0.1:5555 shell screencap -p" } }, "Nox": { @@ -117,6 +117,21 @@ "screencap": "[Adb] -s 127.0.0.1:62001 exec-out screencap -p" } }, + "NoxPort2": { + "handle": { + "class": "Qt5QWindowIcon", + "window": "夜神模拟器" + }, + "adb": { + "path": "[EmulatorPath]nox_adb.exe", + "connect": "[Adb] connect 127.0.0.1:59865", + "click": "[Adb] -s 127.0.0.1:59865 shell input tap [x] [y]", + "swipe": "[Adb] -s 127.0.0.1:59865 shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -s 127.0.0.1:59865 shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "displayRegex": "cur=%dx%d", + "screencap": "[Adb] -s 127.0.0.1:59865 exec-out screencap -p" + } + }, "XYAZ": { "handle": { "class": "Qt5QWindowIcon", @@ -158,11 +173,11 @@ "Doc": "USB连接暂时不兼容多设备的情况", "path": "[ExecDir]platform-tools\\adb.exe", "connect": "[Adb] devices", - "click": "[Adb] shell input tap [x] [y]", - "swipe": "[Adb] shell input swipe [x1] [y1] [x2] [y2] [duration]", - "display": "[Adb] shell dumpsys window displays | grep init= | awk ' { print $3 } '", + "click": "[Adb] -d shell input tap [x] [y]", + "swipe": "[Adb] -d shell input swipe [x1] [y1] [x2] [y2] [duration]", + "display": "[Adb] -d shell dumpsys window displays | grep init= | awk ' { print $3 } '", "displayRegex": "cur=%dx%d", - "screencap": "[Adb] exec-out screencap -p" + "screencap": "[Adb] -d exec-out screencap -p" } } } diff --git a/resource/tasks.json b/resource/tasks.json index 2a0a95f818..efd2b30481 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -1250,18 +1250,19 @@ "rearDelay": 1000 }, "InfrastOperSelected": { - "template": "OperSelected.png", + "template": "empty.png", "cache": false, - "templThreshold": 0.35, + "templThreshold": 8000, + "templThreshold_Doc": "这个任务中作为符合range的像素点数量阈值", "maskRange": [ - 1, - 255 + 95, + 105 ], "rectMove": [ 0, - -252, + -230, 74, - 320 + 283 ] }, "InfrastOperOnShift": { @@ -1278,63 +1279,84 @@ "InfrastClueVacancyNo1": { "template": "ClueVacancyNo1.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 0, + 100, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo2": { "template": "ClueVacancyNo2.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 200, + 200, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo3": { "template": "ClueVacancyNo3.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 400, + 100, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo4": { "template": "ClueVacancyNo4.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 650, + 150, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo5": { "template": "ClueVacancyNo5.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 300, + 420, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo6": { "template": "ClueVacancyNo6.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 500, + 350, + 500, + 250 ], "rearDelay": 1000 }, "InfrastClueVacancyNo7": { "template": "ClueVacancyNo7.png", "cache": false, - "maskRange": [ - 180, - 255 + "templThreshold": 0.75, + "roi": [ + 50, + 350, + 500, + 250 ], "rearDelay": 1000 }, @@ -1344,6 +1366,12 @@ "maskRange": [ 1, 255 + ], + "roi": [ + 800, + 0, + 480, + 720 ] }, "InfrastClueOnTheRightSwipeBegin": { @@ -1355,8 +1383,8 @@ 300, 100 ], - "preDelay": 1000, - "rearDelay": 1000, + "preDelay": 500, + "rearDelay": 500, "maxTimes": 1, "Doc": "这里的preDelay作为滑动duration使用,rearDelay作为滑动额外延时使用,maxTimes作为重复次数使用" }, @@ -1423,6 +1451,6 @@ "template": "UnlockClues.png", "action": "clickSelf", "cache": false, - "rearDelay": 3000 + "rearDelay": 5000 } } \ No newline at end of file diff --git a/src/MeoAssistance/AsstMsg.h b/src/MeoAssistance/AsstMsg.h index 33e652b441..5fb6f4170f 100644 --- a/src/MeoAssistance/AsstMsg.h +++ b/src/MeoAssistance/AsstMsg.h @@ -42,7 +42,7 @@ namespace asst { OpersDetected = 4000, // 识别到了干员s OpersIdtfResult, // 干员识别结果(总的) InfrastComb, // 当前房间的最优干员组合 - EnterStation, // 进入某个房间 + EnterFacility, // 进入某个房间 StationInfo, // 当前房间信息 ReadyToShift, // 准备换班 ShiftCompleted, // 换班完成(单个房间) @@ -80,7 +80,7 @@ namespace asst { {AsstMsg::OpersDetected, "OpersDetected"}, {AsstMsg::OpersIdtfResult, "OpersIdtfResult"}, {AsstMsg::InfrastComb, "InfrastComb"}, - {AsstMsg::EnterStation, "EnterStation"}, + {AsstMsg::EnterFacility, "EnterFacility"}, {AsstMsg::StationInfo, "StationInfo"}, {AsstMsg::ReadyToShift, "ReadyToShift"}, {AsstMsg::ShiftCompleted, "ShiftCompleted"}, diff --git a/src/MeoAssistance/Controller.cpp b/src/MeoAssistance/Controller.cpp index c461397c49..17dab3c5d0 100644 --- a/src/MeoAssistance/Controller.cpp +++ b/src/MeoAssistance/Controller.cpp @@ -121,7 +121,7 @@ void asst::Controller::pipe_working_proc() bool Controller::try_capture(const EmulatorInfo& info, bool without_handle) { - LogTraceFunction; + LogTraceScope("try_capture | " + info.name); const HandleInfo& handle_info = info.handle; std::string adb_dir; @@ -554,9 +554,15 @@ int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int du cv::Mat asst::Controller::get_image(bool raw) { + static bool has_successful = false; if (!screencap()) { - return cv::Mat(); + // 如果之前成功截图过,这次没成功可能是一次意外,重试一次 + // 点名批评雷电模拟器! + if (!(has_successful && screencap())) { + return cv::Mat(); + } } + has_successful = true; //std::shared_lock image_lock(m_image_mutex); if (raw) { return m_cache_image; diff --git a/src/MeoAssistance/InfrastAbstractTask.cpp b/src/MeoAssistance/InfrastAbstractTask.cpp index 625431cf70..2c1e79041b 100644 --- a/src/MeoAssistance/InfrastAbstractTask.cpp +++ b/src/MeoAssistance/InfrastAbstractTask.cpp @@ -5,18 +5,29 @@ #include "InfrastFacilityImageAnalyzer.h" #include "Controller.h" #include "Resource.h" +#include "Logger.hpp" +#include "AsstMsg.h" bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int index) { + LogTraceFunction; + json::value enter_json = json::object{ + { "facility", facility }, + { "index", index} + }; + m_callback(AsstMsg::EnterFacility, enter_json, m_callback_arg); + const auto& image = ctrler.get_image(); InfrastFacilityImageAnalyzer analyzer(image); analyzer.set_to_be_analyzed({ facility }); if (!analyzer.analyze()) { + log.trace("result is empty"); return false; } Rect rect = analyzer.get_rect(facility, index); if (rect.empty()) { + log.trace("facility index is out of range"); return false; } @@ -30,6 +41,8 @@ bool asst::InfrastAbstractTask::enter_facility(const std::string& facility, int bool asst::InfrastAbstractTask::enter_oper_list_page() { + LogTraceFunction; + auto image = ctrler.get_image(); // 识别右边的“进驻”按钮 @@ -40,25 +53,30 @@ bool asst::InfrastAbstractTask::enter_oper_list_page() // 如果没找到,说明“进驻信息”这个按钮没有被点开,那就点开它 if (!enter_analyzer.analyze()) { + log.trace("ready to analyze the stationed info button"); OcrImageAnalyzer station_analyzer(image); const auto stationedinfo_task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastStationedInfo")); station_analyzer.set_task_info(*stationedinfo_task_ptr); if (station_analyzer.analyze()) { + log.trace("the stationed info button found"); ctrler.click(station_analyzer.get_result().front().rect); sleep(stationedinfo_task_ptr->rear_delay); // 点开了按钮之后,再识别一次右边的 image = ctrler.get_image(); enter_analyzer.set_image(image); if (!enter_analyzer.analyze()) { + log.error("no enterlist button"); return false; } } else { + log.error("no stationed info button"); return false; } } + log.trace("ready to click the enterlist button"); ctrler.click(enter_analyzer.get_result().front().rect); sleep(enter_task_ptr->rear_delay); @@ -67,6 +85,7 @@ bool asst::InfrastAbstractTask::enter_oper_list_page() void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse) { + LogTraceFunction; static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeBegin")->specific_rect; static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeEnd")->specific_rect; static int duration = resource.task().task_ptr("InfrastOperListSwipeBegin")->pre_delay; @@ -81,6 +100,7 @@ void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse) void asst::InfrastAbstractTask::await_swipe() { + LogTraceFunction; static int extra_delay = resource.task().task_ptr("InfrastOperListSwipeBegin")->rear_delay; ctrler.wait(m_last_swipe_id); @@ -89,6 +109,7 @@ void asst::InfrastAbstractTask::await_swipe() bool asst::InfrastAbstractTask::click_bottomleft_tab() { + LogTraceFunction; const auto task_ptr = resource.task().task_ptr("InfrastBottomLeftTab"); ctrler.click(task_ptr->specific_rect); sleep(task_ptr->rear_delay); @@ -97,6 +118,7 @@ bool asst::InfrastAbstractTask::click_bottomleft_tab() bool asst::InfrastAbstractTask::click_clear_button() { + LogTraceFunction; const auto task_ptr = resource.task().task_ptr("InfrastClearButton"); ctrler.click(task_ptr->specific_rect); sleep(task_ptr->rear_delay); @@ -105,6 +127,7 @@ bool asst::InfrastAbstractTask::click_clear_button() bool asst::InfrastAbstractTask::click_confirm_button() { + LogTraceFunction; const auto task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastConfirmButton")); ctrler.click(task_ptr->specific_rect); @@ -114,6 +137,9 @@ bool asst::InfrastAbstractTask::click_confirm_button() OcrImageAnalyzer analyzer; analyzer.set_task_info(*task_ptr); for (int i = 0; i != m_retry_times; ++i) { + if (need_exit()) { + return false; + } const auto& image = ctrler.get_image(); analyzer.set_image(image); if (!analyzer.analyze()) { @@ -133,6 +159,7 @@ void asst::InfrastAbstractTask::sync_swipe_of_operlist(bool reverse) void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist() { + LogTraceFunction; static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay; @@ -140,6 +167,9 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist() static int loop_times = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->max_times; for (int i = 0; i != loop_times; ++i) { + if (need_exit()) { + return; + } ctrler.swipe(end_rect, begin_rect, duration, true, 0, false); } sleep(extra_delay); @@ -147,6 +177,7 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist() void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui() { + LogTraceFunction; static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay; @@ -157,6 +188,7 @@ void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui() void asst::InfrastAbstractTask::swipe_to_the_right_of_main_ui() { + LogTraceFunction; static Rect begin_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = resource.task().task_ptr("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = resource.task().task_ptr("InfrastOperListSwipeToTheLeftBegin")->pre_delay; diff --git a/src/MeoAssistance/InfrastClueVacancyImageAnalyzer.cpp b/src/MeoAssistance/InfrastClueVacancyImageAnalyzer.cpp index b16331d9e8..9d4eb2cace 100644 --- a/src/MeoAssistance/InfrastClueVacancyImageAnalyzer.cpp +++ b/src/MeoAssistance/InfrastClueVacancyImageAnalyzer.cpp @@ -3,6 +3,7 @@ #include "MatchImageAnalyzer.h" #include "Resource.h" #include "AsstUtils.hpp" +#include "Logger.hpp" bool asst::InfrastClueVacancyImageAnalyzer::analyze() { @@ -14,9 +15,11 @@ bool asst::InfrastClueVacancyImageAnalyzer::analyze() resource.task().task_ptr(clue_vacancy + suffix)); analyzer.set_task_info(*task_ptr); if (!analyzer.analyze()) { + log.trace("no", clue_vacancy, suffix); continue; } Rect rect = analyzer.get_result().rect; + log.trace("has", clue_vacancy, suffix); #ifdef LOG_TRACE cv::rectangle(m_image_draw, utils::make_rect(rect), cv::Scalar(0, 0, 255), 2); cv::putText(m_image_draw, suffix, cv::Point(rect.x, rect.y + 1), 0, 1, cv::Scalar(0, 0, 255), 2); diff --git a/src/MeoAssistance/InfrastDormTask.cpp b/src/MeoAssistance/InfrastDormTask.cpp index 5e40ca2965..219eb69f7b 100644 --- a/src/MeoAssistance/InfrastDormTask.cpp +++ b/src/MeoAssistance/InfrastDormTask.cpp @@ -5,6 +5,7 @@ #include "InfrastMoodImageAnalyzer.h" #include "Resource.h" #include "Controller.h" +#include "Logger.hpp" const std::string asst::InfrastDormTask::FacilityName = "Dorm"; @@ -17,6 +18,9 @@ bool asst::InfrastDormTask::run() m_callback(AsstMsg::TaskStart, task_start_json, m_callback_arg); for (; m_cur_dorm_index < m_max_num_of_dorm; ++m_cur_dorm_index) { + if (need_exit()) { + return false; + } enter_facility(FacilityName, m_cur_dorm_index); if (!enter_oper_list_page()) { return false; @@ -26,9 +30,13 @@ bool asst::InfrastDormTask::run() int quantity_selected = 0; while (quantity_selected < MaxNumOfOpers) { + if (need_exit()) { + return false; + } const auto& image = ctrler.get_image(); InfrastMoodImageAnalyzer mood_analyzer(image); if (!mood_analyzer.analyze()) { + log.error("mood analyze faild!"); return false; } mood_analyzer.sort_result(); @@ -36,7 +44,11 @@ bool asst::InfrastDormTask::run() int quantity_resting = 0; for (const auto& mood_info : mood_result) { + if (need_exit()) { + return false; + } if (quantity_selected >= MaxNumOfOpers) { + log.trace("quantity_selected:", quantity_selected, ", just break"); break; } switch (mood_info.smiley.type) @@ -44,6 +56,7 @@ bool asst::InfrastDormTask::run() case InfrastSmileyType::Rest: // 如果当前页面休息完成的人数超过5个,说明已经已经把所有心情不满的滑过一遍、没有更多的了,直接退出即可 if (++quantity_resting > MaxNumOfOpers) { + log.trace("quantity_resting:", quantity_resting, ", confirm"); click_confirm_button(); return true; } @@ -57,6 +70,7 @@ bool asst::InfrastDormTask::run() { ctrler.click(mood_info.rect); if (++quantity_selected >= MaxNumOfOpers) { + log.trace("quantity_selected:", quantity_selected, ", just break"); break; } } @@ -66,6 +80,7 @@ bool asst::InfrastDormTask::run() } } if (quantity_selected >= MaxNumOfOpers) { + log.trace("quantity_selected:", quantity_selected, ", just break"); break; } sync_swipe_of_operlist(); @@ -78,6 +93,8 @@ bool asst::InfrastDormTask::run() bool asst::InfrastDormTask::click_confirm_button() { + LogTraceFunction; + const auto task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastConfirmButton")); ctrler.click(task_ptr->specific_rect); @@ -98,6 +115,9 @@ bool asst::InfrastDormTask::click_confirm_button() OcrImageAnalyzer analyzer; analyzer.set_task_info(*task_ptr); for (int i = 0; i != m_retry_times; ++i) { + if (need_exit()) { + return false; + } const auto& image = ctrler.get_image(); analyzer.set_image(image); if (!analyzer.analyze()) { diff --git a/src/MeoAssistance/InfrastFacilityImageAnalyzer.cpp b/src/MeoAssistance/InfrastFacilityImageAnalyzer.cpp index 9b39812793..a16e6d4a22 100644 --- a/src/MeoAssistance/InfrastFacilityImageAnalyzer.cpp +++ b/src/MeoAssistance/InfrastFacilityImageAnalyzer.cpp @@ -3,6 +3,7 @@ #include "Resource.h" #include "MultiMatchImageAnalyzer.h" #include "AsstUtils.hpp" +#include "Logger.hpp" bool asst::InfrastFacilityImageAnalyzer::analyze() { @@ -32,13 +33,13 @@ bool asst::InfrastFacilityImageAnalyzer::analyze() int cor_suffix_index = -1; - for (const auto& [key, task_name] : facility_task_name) { - if (!m_to_be_analyzed.empty()) { // 若为空,则分析所有设施 - if (std::find(m_to_be_analyzed.cbegin(), m_to_be_analyzed.cend(), key) - == m_to_be_analyzed.cend()) { - continue; - } + for (const auto& key : m_to_be_analyzed) { + auto find_iter = facility_task_name.find(key); + if (find_iter == facility_task_name.cend()) { + log.error("facility name error", key); + continue; } + std::string task_name = find_iter->second; std::vector cur_facility_result; // 已知基建缩放状态的时候,只识别这个缩放状态下的就行了 // 否则识别所有状态,直到找出正确的当前缩放状态 diff --git a/src/MeoAssistance/InfrastFacilityImageAnalyzer.h b/src/MeoAssistance/InfrastFacilityImageAnalyzer.h index 7e14d4efc1..5c90a86c2e 100644 --- a/src/MeoAssistance/InfrastFacilityImageAnalyzer.h +++ b/src/MeoAssistance/InfrastFacilityImageAnalyzer.h @@ -11,7 +11,6 @@ namespace asst { virtual bool analyze() override; - // 若为空,则分析所有设施 void set_to_be_analyzed(std::vector facilities) noexcept { m_to_be_analyzed = std::move(facilities); } @@ -53,7 +52,7 @@ namespace asst { // key:设施名,value:所有这种设施的当前Rect(例如所有制造站的位置) std::unordered_map> m_result; - // 需要识别的设施名,若为空,则分析所有设施 + // 需要识别的设施名 std::vector m_to_be_analyzed; }; } diff --git a/src/MeoAssistance/InfrastInfoTask.cpp b/src/MeoAssistance/InfrastInfoTask.cpp index f886eb6c8d..781089288d 100644 --- a/src/MeoAssistance/InfrastInfoTask.cpp +++ b/src/MeoAssistance/InfrastInfoTask.cpp @@ -17,10 +17,7 @@ bool asst::InfrastInfoTask::run() const auto& image = ctrler.get_image(); InfrastFacilityImageAnalyzer analyzer(image); - std::vector all_facilities; - for (auto&& [key, _value] : resource.infrast().get_facility_info()) { - all_facilities.emplace_back(key); - } + analyzer.set_to_be_analyzed({ "Mfg", "Trade", "Power" }); if (!analyzer.analyze()) { return false; } diff --git a/src/MeoAssistance/InfrastMoodImageAnalyzer.cpp b/src/MeoAssistance/InfrastMoodImageAnalyzer.cpp index 7b8a4b582d..739773f517 100644 --- a/src/MeoAssistance/InfrastMoodImageAnalyzer.cpp +++ b/src/MeoAssistance/InfrastMoodImageAnalyzer.cpp @@ -4,15 +4,24 @@ #include "InfrastSmileyImageAnalyzer.h" #include "MatchImageAnalyzer.h" #include "AsstUtils.hpp" +#include "Logger.hpp" bool asst::InfrastMoodImageAnalyzer::analyze() { + LogTraceFunction; + mood_detect(); mood_analyze(); hash_calc(); selected_analyze(); working_analyze(); + for (auto&& info : m_result) { + log.trace(info.hash, info.rect.to_string(), + "smiley:", (int)info.smiley.type, "mood:", info.percentage, + "selected:", info.selected, "working:", info.working); + } + return true; } @@ -186,20 +195,32 @@ bool asst::InfrastMoodImageAnalyzer::selected_analyze() resource.task().task_ptr("InfrastOperSelected")); Rect rect_move = selected_task_ptr->rect_move; - MatchImageAnalyzer selected_analyzer(m_image); - selected_analyzer.set_task_info(*selected_task_ptr); - for (auto&& info : m_result) { - Rect selected_rect = rect_move; + cv::Rect selected_rect = utils::make_rect(rect_move); selected_rect.x += info.smiley.rect.x; selected_rect.y += info.smiley.rect.y; - selected_analyzer.set_roi(selected_rect); - if (selected_analyzer.analyze()) { - info.selected = true; -#ifdef LOG_TRACE - cv::putText(m_image_draw, "SELECTED", cv::Point(selected_rect.x, selected_rect.y + 50), 1, 1, cv::Scalar(0, 0, 255), 2); -#endif + + cv::Mat roi = m_image(selected_rect); + cv::Mat hsv, bin; + cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV); + std::vector channels; + cv::split(hsv, channels); + int mask_lowb = selected_task_ptr->mask_range.first; + int mask_uppb = selected_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("selected_analyze |", count); + info.selected = count >= selected_task_ptr->templ_threshold; } return true; } diff --git a/src/MeoAssistance/InfrastOfficeTask.cpp b/src/MeoAssistance/InfrastOfficeTask.cpp index 8c6b21ca9e..69d4f4b66b 100644 --- a/src/MeoAssistance/InfrastOfficeTask.cpp +++ b/src/MeoAssistance/InfrastOfficeTask.cpp @@ -22,6 +22,9 @@ bool asst::InfrastOfficeTask::run() constexpr int retry_times = 1; for (int i = 0; i <= retry_times; ++i) { + if (need_exit()) { + return false; + } swipe_to_the_left_of_operlist(); opers_detect_with_swipe(); swipe_to_the_left_of_operlist(); diff --git a/src/MeoAssistance/InfrastPowerTask.cpp b/src/MeoAssistance/InfrastPowerTask.cpp index fe85c5295e..c76b3d13c2 100644 --- a/src/MeoAssistance/InfrastPowerTask.cpp +++ b/src/MeoAssistance/InfrastPowerTask.cpp @@ -17,6 +17,9 @@ bool asst::InfrastPowerTask::run() set_product("Drone"); for (int i = 0; i != MaxNumOfFacility; ++i) { + if (need_exit()) { + return false; + } swipe_to_the_left_of_main_ui(); enter_facility(FacilityName, i); diff --git a/src/MeoAssistance/InfrastProductionTask.cpp b/src/MeoAssistance/InfrastProductionTask.cpp index 1b432f2539..76cf038eb1 100644 --- a/src/MeoAssistance/InfrastProductionTask.cpp +++ b/src/MeoAssistance/InfrastProductionTask.cpp @@ -35,8 +35,11 @@ bool asst::InfrastProductionTask::shift_facility_list() { + LogTraceFunction; facility_list_detect(); - + if (need_exit()) { + return false; + } const auto tab_task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastFacilityListTab" + m_facility)); MatchImageAnalyzer add_analyzer; @@ -45,6 +48,9 @@ bool asst::InfrastProductionTask::shift_facility_list() add_analyzer.set_task_info(*add_task_ptr); for (const Rect& tab : m_facility_list_tabs) { + if (need_exit()) { + return false; + } ctrler.click(tab); sleep(tab_task_ptr->rear_delay); @@ -52,6 +58,7 @@ bool asst::InfrastProductionTask::shift_facility_list() const auto& image = ctrler.get_image(); add_analyzer.set_image(image); if (!add_analyzer.analyze()) { + log.info("no add button, just continue"); continue; } Rect add_button = add_analyzer.get_result().rect; @@ -71,12 +78,16 @@ bool asst::InfrastProductionTask::shift_facility_list() } } set_product(cur_product); + log.info("cur product", cur_product); /* 进入干员选择页面 */ ctrler.click(add_button); sleep(add_task_ptr->rear_delay); constexpr int retry_times = 1; for (int i = 0; i <= retry_times; ++i) { + if (need_exit()) { + return false; + } click_clear_button(); swipe_to_the_left_of_operlist(); @@ -102,11 +113,16 @@ bool asst::InfrastProductionTask::shift_facility_list() bool asst::InfrastProductionTask::opers_detect_with_swipe() { + LogTraceFunction; m_all_available_opers.clear(); int first_number = 0; while (true) { + if (need_exit()) { + return false; + } size_t num = opers_detect(); + log.trace("opers_detect return", num); // 这里本来是判断不相等就可以退出循环。 // 但是有时候滑动会把一个干员挡住一半,一个页面完整的干员真的只有10个,所以加个2的差值 @@ -126,6 +142,7 @@ bool asst::InfrastProductionTask::opers_detect_with_swipe() size_t asst::InfrastProductionTask::opers_detect() { + LogTraceFunction; const auto& image = ctrler.get_image(); InfrastSkillsImageAnalyzer skills_analyzer(image); @@ -176,6 +193,7 @@ size_t asst::InfrastProductionTask::opers_detect() bool asst::InfrastProductionTask::optimal_calc() { + LogTraceFunction; auto& facility_info = resource.infrast().get_facility_info(m_facility); int max_num_of_opers = facility_info.max_num_of_opers; @@ -322,8 +340,12 @@ bool asst::InfrastProductionTask::optimal_calc() bool asst::InfrastProductionTask::opers_choose() { + LogTraceFunction; bool has_error = false; while (true) { + if (need_exit()) { + return false; + } const auto& image = ctrler.get_image(); InfrastSkillsImageAnalyzer skills_analyzer(image); @@ -389,6 +411,7 @@ bool asst::InfrastProductionTask::opers_choose() bool asst::InfrastProductionTask::facility_list_detect() { + LogTraceFunction; m_facility_list_tabs.clear(); const auto& image = ctrler.get_image(); diff --git a/src/MeoAssistance/InfrastReceptionTask.cpp b/src/MeoAssistance/InfrastReceptionTask.cpp index c4ea58a56b..33d596e1e6 100644 --- a/src/MeoAssistance/InfrastReceptionTask.cpp +++ b/src/MeoAssistance/InfrastReceptionTask.cpp @@ -38,8 +38,12 @@ bool asst::InfrastReceptionTask::run() bool asst::InfrastReceptionTask::harvest_clue() { + LogTraceFunction; std::vector tasks_vec = { "InfrastClueNew" }; while (!tasks_vec.empty()) { + if (need_exit()) { + return false; + } ProcessTaskImageAnalyzer analyzer(ctrler.get_image(), tasks_vec); if (!analyzer.analyze()) { break; @@ -71,37 +75,15 @@ bool asst::InfrastReceptionTask::harvest_clue() bool asst::InfrastReceptionTask::proc_clue() { + LogTraceFunction; const static std::string clue_vacancy = "InfrastClueVacancy"; const static std::vector clue_suffix = { "No1", "No2", "No3", "No4", "No5", "No6", "No7" }; - cv::Mat image; - for (const std::string& clue : clue_suffix) { - // 先识别线索的空位 - image = ctrler.get_image(); - InfrastClueVacancyImageAnalyzer vacancy_analyzer(image); - vacancy_analyzer.set_to_be_analyzed({ clue }); - if (!vacancy_analyzer.analyze()) { - continue; - } - // 点开线索的空位 - Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second; - ctrler.click(vacancy); - int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay; - sleep(delay); + proc_vacancy(); - // 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的) - swipe_to_the_bottom_of_clue_list_on_the_right(); - image = ctrler.get_image(); - InfrastClueImageAnalyzer clue_analyzer(image); - if (!clue_analyzer.analyze()) { - continue; - } - ctrler.click(clue_analyzer.get_result().back().first); - sleep(delay); - } // 开启线索交流,“解锁线索” - image = ctrler.get_image(); + cv::Mat image = ctrler.get_image(); MatchImageAnalyzer unlock_analyzer(image); const auto unlock_task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("UnlockClues")); @@ -110,6 +92,7 @@ bool asst::InfrastReceptionTask::proc_clue() ctrler.click(unlock_analyzer.get_result().rect); sleep(unlock_task_ptr->rear_delay); click_bottomleft_tab(); + proc_vacancy(); image = ctrler.get_image(); } @@ -136,8 +119,46 @@ bool asst::InfrastReceptionTask::proc_clue() return true; } +bool asst::InfrastReceptionTask::proc_vacancy() +{ + LogTraceFunction; + const static std::string clue_vacancy = "InfrastClueVacancy"; + const static std::vector clue_suffix = { + "No1", "No2", "No3", "No4", "No5", "No6", "No7" }; + + for (const std::string& clue : clue_suffix) { + if (need_exit()) { + return false; + } + // 先识别线索的空位 + cv::Mat image = ctrler.get_image(); + InfrastClueVacancyImageAnalyzer vacancy_analyzer(image); + vacancy_analyzer.set_to_be_analyzed({ clue }); + if (!vacancy_analyzer.analyze()) { + continue; + } + // 点开线索的空位 + Rect vacancy = vacancy_analyzer.get_vacancy().cbegin()->second; + ctrler.click(vacancy); + int delay = resource.task().task_ptr(clue_vacancy + clue)->rear_delay; + sleep(delay); + + // 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的) + swipe_to_the_bottom_of_clue_list_on_the_right(); + image = ctrler.get_image(); + InfrastClueImageAnalyzer clue_analyzer(image); + if (!clue_analyzer.analyze()) { + continue; + } + ctrler.click(clue_analyzer.get_result().back().first); + sleep(delay); + } + return true; +} + bool asst::InfrastReceptionTask::shift() { + LogTraceFunction; const auto& image = ctrler.get_image(); MatchImageAnalyzer add_analyzer(image); @@ -153,6 +174,9 @@ bool asst::InfrastReceptionTask::shift() constexpr int retry_times = 1; for (int i = 0; i <= retry_times; ++i) { + if (need_exit()) { + return false; + } swipe_to_the_left_of_operlist(); click_clear_button(); @@ -173,6 +197,7 @@ bool asst::InfrastReceptionTask::shift() bool asst::InfrastReceptionTask::swipe_to_the_bottom_of_clue_list_on_the_right() { + LogTraceFunction; static Rect begin_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->specific_rect; static Rect end_rect = resource.task().task_ptr("InfrastClueOnTheRightSwipeEnd")->specific_rect; static int duration = resource.task().task_ptr("InfrastClueOnTheRightSwipeBegin")->pre_delay; diff --git a/src/MeoAssistance/InfrastReceptionTask.h b/src/MeoAssistance/InfrastReceptionTask.h index cc1c647735..94eec97a4a 100644 --- a/src/MeoAssistance/InfrastReceptionTask.h +++ b/src/MeoAssistance/InfrastReceptionTask.h @@ -13,6 +13,7 @@ namespace asst { private: bool harvest_clue(); bool proc_clue(); + bool proc_vacancy(); bool shift(); bool swipe_to_the_bottom_of_clue_list_on_the_right(); }; diff --git a/src/MeoAssistance/InfrastSkillsImageAnalyzer.cpp b/src/MeoAssistance/InfrastSkillsImageAnalyzer.cpp index 8f89daa37b..4d07867f7b 100644 --- a/src/MeoAssistance/InfrastSkillsImageAnalyzer.cpp +++ b/src/MeoAssistance/InfrastSkillsImageAnalyzer.cpp @@ -8,6 +8,8 @@ bool asst::InfrastSkillsImageAnalyzer::analyze() { + LogTraceFunction; + m_skills_detected.clear(); m_skills_splited.clear(); m_result.clear(); @@ -18,11 +20,20 @@ bool asst::InfrastSkillsImageAnalyzer::analyze() skill_analyze(); + for (auto&& info : m_result) { + std::string skill_str; + for (auto&& skill : info.skills_comb.skills) { + skill_str += skill.names.front() + " "; + } + log.trace(info.hash, info.rect.to_string(), skill_str); + } + return true; } void asst::InfrastSkillsImageAnalyzer::sort_result() { + LogTraceFunction; // 按位置排个序 std::sort(m_result.begin(), m_result.end(), [](const auto& lhs, const auto& rhs) -> bool { @@ -38,6 +49,7 @@ void asst::InfrastSkillsImageAnalyzer::sort_result() bool asst::InfrastSkillsImageAnalyzer::skills_detect() { + LogTraceFunction; const auto upper_task_ptr = resource.task().task_ptr("InfrastSkillsUpper"); const auto lower_task_ptr = resource.task().task_ptr("InfrastSkillsLower"); const auto hash_task_ptr = resource.task().task_ptr("InfrastSkillsHash"); @@ -98,6 +110,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_detect() bool asst::InfrastSkillsImageAnalyzer::skills_split() { + LogTraceFunction; const auto task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastSkills")); const auto thres = task_ptr->hist_threshold; @@ -144,6 +157,7 @@ bool asst::InfrastSkillsImageAnalyzer::skills_split() bool asst::InfrastSkillsImageAnalyzer::skill_analyze() { + LogTraceFunction; const auto task_ptr = std::dynamic_pointer_cast( resource.task().task_ptr("InfrastSkills")); @@ -151,11 +165,6 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze() skill_analyzer.set_mask_range(task_ptr->mask_range); skill_analyzer.set_threshold(task_ptr->templ_threshold); - const auto selected_task_ptr = std::dynamic_pointer_cast( - resource.task().task_ptr("InfrastOperSelected")); - MatchImageAnalyzer selected_analyzer(m_image); - selected_analyzer.set_task_info(*selected_task_ptr); - for (const auto& [hash, skills_rect_vec] : m_skills_splited) { std::unordered_set skills_set; // 单个干员的全部技能 std::string log_str = "[ "; @@ -233,16 +242,7 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze() // 识别该干员是否被选中。数据结构设计的不太合理,先这么凑合用了 int smiley_x = skills_rect_vec.front().x - task_ptr->rect_move.x; int smiley_y = skills_rect_vec.front().y - task_ptr->rect_move.y; - Rect selected_rect = selected_task_ptr->rect_move; - selected_rect.x += smiley_x; - selected_rect.y += smiley_y; - selected_analyzer.set_roi(selected_rect); - if (selected_analyzer.analyze()) { - info.selected = true; - } - else { - info.selected = false; - } + info.selected = selected_analyze(smiley_x, smiley_y); m_result.emplace_back(std::move(info)); } @@ -250,4 +250,40 @@ bool asst::InfrastSkillsImageAnalyzer::skill_analyze() return true; } return false; +} + +bool asst::InfrastSkillsImageAnalyzer::selected_analyze(int smiley_x, int smiley_y) +{ + LogTraceFunction; + const auto selected_task_ptr = std::dynamic_pointer_cast( + resource.task().task_ptr("InfrastOperSelected")); + cv::Rect selected_rect = utils::make_rect(selected_task_ptr->rect_move); + selected_rect.x += smiley_x; + selected_rect.y += smiley_y; + +#ifdef LOG_TRACE + cv::Mat draw = m_image_draw.clone(); + cv::rectangle(draw, selected_rect, cv::Scalar(0, 0, 255), 2); +#endif // LOG_TRACE + cv::Mat roi = m_image(selected_rect); + cv::Mat hsv, bin; + cv::cvtColor(roi, hsv, cv::COLOR_BGR2HSV); + std::vector channels; + cv::split(hsv, channels); + int mask_lowb = selected_task_ptr->mask_range.first; + int mask_uppb = selected_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("selected_analyze |", count); + return count >= selected_task_ptr->templ_threshold; } \ No newline at end of file diff --git a/src/MeoAssistance/InfrastSkillsImageAnalyzer.h b/src/MeoAssistance/InfrastSkillsImageAnalyzer.h index 5a16c7f8f5..2bf6de963a 100644 --- a/src/MeoAssistance/InfrastSkillsImageAnalyzer.h +++ b/src/MeoAssistance/InfrastSkillsImageAnalyzer.h @@ -37,6 +37,7 @@ namespace asst { bool skills_detect(); // 检测出所有技能区域 bool skills_split(); // 拆分成一个一个的区域 bool skill_analyze(); // 识别每个技能区域是啥 + bool selected_analyze(int smiley_x, int smiley_y); // 识别干员是否被选中 std::string m_facility; // skills_detect()的结果,key是hash,value是单个干员的全部技能区域 diff --git a/src/MeoAssistance/Version.h b/src/MeoAssistance/Version.h index 1bb26519d6..4554282372 100644 --- a/src/MeoAssistance/Version.h +++ b/src/MeoAssistance/Version.h @@ -1,5 +1,5 @@ #pragma once namespace asst { - constexpr static const char* Version = "v1.3-alpha.6"; + constexpr static const char* Version = "v1.3-alpha.7"; } \ No newline at end of file diff --git a/tools/build_release.bat b/tools/build_release.bat new file mode 100644 index 0000000000..4d2e240ffa --- /dev/null +++ b/tools/build_release.bat @@ -0,0 +1,2 @@ +MSBuild.exe ..\MeoAssistance.sln /p:Configuration=Release /p:Platform=x64 /t:Rebuild +sh zip_release.sh \ No newline at end of file diff --git a/tools/zip_release.sh b/tools/zip_release.sh new file mode 100644 index 0000000000..b4927a5560 --- /dev/null +++ b/tools/zip_release.sh @@ -0,0 +1,6 @@ +TargetDir="../x64/Release" +LatestTag=$(git describe --tags $(git rev-list --tags --max-count=1)) +Package="../x64/MeoAssistance_"$LatestTag".zip" + +cp ./*.url $TargetDir +7z.exe a $Package $TargetDir/resource $TargetDir/*.dll $TargetDir/MeoAsstGui.exe $TargetDir/*.url \ No newline at end of file diff --git a/tools/使用说明.url b/tools/使用说明.url new file mode 100644 index 0000000000..a467b530af --- /dev/null +++ b/tools/使用说明.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://github.com/MistEO/MeoAssistance-Arknights/blob/master/README.md diff --git a/tools/问题反馈.url b/tools/问题反馈.url new file mode 100644 index 0000000000..4cf932e0c1 --- /dev/null +++ b/tools/问题反馈.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://github.com/MistEO/MeoAssistance-Arknights/issues