#include "InfrastAbstractTask.h" #include #include #include #include "Controller.h" #include "ImageAnalyzer/General/MatchImageAnalyzer.h" #include "ImageAnalyzer/General/OcrImageAnalyzer.h" #include "ImageAnalyzer/General/OcrWithPreprocessImageAnalyzer.h" #include "ImageAnalyzer/InfrastFacilityImageAnalyzer.h" #include "ImageAnalyzer/InfrastOperImageAnalyzer.h" #include "ProcessTask.h" #include "TaskData.h" #include "Utils/AsstMsg.h" #include "Utils/AsstRanges.hpp" #include "Utils/Logger.hpp" asst::InfrastAbstractTask::InfrastAbstractTask(AsstCallback callback, void* callback_arg, std::string task_chain) : AbstractTask(std::move(callback), callback_arg, std::move(task_chain)) { m_retry_times = TaskRetryTimes; } asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_mood_threshold(double mood_thres) noexcept { m_mood_threshold = mood_thres; return *this; } json::value asst::InfrastAbstractTask::basic_info() const { json::value info = AbstractTask::basic_info(); auto& details = info["details"]; details["facility"] = facility_name(); details["index"] = m_cur_facility_index; return info; } std::string asst::InfrastAbstractTask::facility_name() const { if (m_facility_name_cache.empty()) { std::string class_name = typeid(*this).name(); // typeid.name() 结果可能和编译器有关,所以这里使用正则尽可能保证结果正确。 // 但还是不能完全保证,如果不行的话建议 override std::regex regex("Infrast(.*)Task"); std::smatch match_obj; if (std::regex_search(class_name, match_obj, regex)) { m_facility_name_cache = match_obj[1].str(); } else { m_facility_name_cache = class_name; } } return m_facility_name_cache; } void asst::InfrastAbstractTask::set_custom_config(infrast::CustomFacilityConfig config) noexcept { m_custom_config = std::move(config); m_is_custom = true; } void asst::InfrastAbstractTask::clear_custom_config() noexcept { m_is_custom = false; m_custom_config.clear(); } asst::infrast::CustomRoomConfig& asst::InfrastAbstractTask::current_room_config() { static infrast::CustomRoomConfig empty; if (!m_is_custom) { Log.error(__FUNCTION__, "custom is not enabled"); return empty; } if (m_cur_facility_index < m_custom_config.size()) { return m_custom_config[m_cur_facility_index]; } else { Log.error(__FUNCTION__, "tab size is lager than config size", m_cur_facility_index, m_custom_config.size()); return empty; } } bool asst::InfrastAbstractTask::on_run_fails() { LogTraceFunction; ProcessTask return_task(*this, { "InfrastBegin" }); return return_task.run(); } bool asst::InfrastAbstractTask::enter_facility(int index) { LogTraceFunction; if (m_is_custom && index >= m_custom_config.size()) { Log.warn("index is lager than config size", index, m_custom_config.size()); return false; } InfrastFacilityImageAnalyzer analyzer(m_ctrler->get_image()); analyzer.set_to_be_analyzed({ facility_name() }); if (!analyzer.analyze()) { Log.info("result is empty"); return false; } Rect rect = analyzer.get_rect(facility_name(), index); if (rect.empty()) { Log.info("facility index is out of range"); return false; } m_ctrler->click(rect); m_cur_facility_index = index; callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("EnterFacility")); sleep(Task.get("InfrastEnterFacility")->rear_delay); return true; } bool asst::InfrastAbstractTask::enter_oper_list_page() { LogTraceFunction; ProcessTask task(*this, { "InfrastEnterOperList", "InfrastStationedInfo" }); return task.run(); } void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse) { LogTraceFunction; static Rect begin_rect = Task.get("InfrastOperListSwipeBegin")->specific_rect; static Rect end_rect = Task.get("InfrastOperListSwipeEnd")->specific_rect; static int duration = Task.get("InfrastOperListSwipeBegin")->pre_delay; if (!reverse) { m_last_swipe_id = m_ctrler->swipe(begin_rect, end_rect, duration, false, 0, true); } else { m_last_swipe_id = m_ctrler->swipe(end_rect, begin_rect, duration, false, 0, true); } } bool asst::InfrastAbstractTask::is_use_custom_opers() { return m_is_custom && (!current_room_config().names.empty() || !current_room_config().candidates.empty()); } void asst::InfrastAbstractTask::await_swipe() { LogTraceFunction; static int extra_delay = Task.get("InfrastOperListSwipeBegin")->rear_delay; m_ctrler->wait(m_last_swipe_id); sleep(extra_delay); } /// @brief 按技能排序->清空干员->选择定制干员->按指定顺序排序 bool asst::InfrastAbstractTask::swipe_and_select_custom_opers(bool is_dorm_order) { LogTraceFunction; auto& room_config = current_room_config(); { json::value cb_info = basic_info_with_what("CustomInfrastRoomOperators"); auto& details = cb_info["details"]; details["names"] = json::array(room_config.names); details["candidates"] = json::array(room_config.candidates); callback(AsstMsg::SubTaskExtraInfo, cb_info); } if (!is_dorm_order) { ProcessTask(*this, { "InfrastOperListTabSkillUnClicked", "Stop" }).run(); } if (max_num_of_opers() > 1) { click_clear_button(); // 先排序后清空,加速干员变化不大时的选择速度 } std::vector opers_order = room_config.names; opers_order.insert(opers_order.end(), room_config.candidates.cbegin(), room_config.candidates.cend()); std::vector pre_partial_result; bool retried = false; bool pre_result_no_changes = false; int swipe_times = 0; while (true) { if (need_exit()) { return false; } std::vector partial_result; if (!select_custom_opers(partial_result)) { return false; } if (room_config.selected >= max_num_of_opers() || (room_config.names.empty() && room_config.candidates.empty())) { break; } if (partial_result == pre_partial_result) { if (pre_result_no_changes) { Log.warn("partial result is not changed, reset the page"); if (retried) { Log.error("already retring"); break; } swipe_to_the_left_of_operlist(swipe_times / 5 + 1); swipe_times = 0; retried = true; } else { pre_result_no_changes = true; } } else { pre_result_no_changes = false; } pre_partial_result = partial_result; swipe_of_operlist(); ++swipe_times; } // 先按任意其他的tab排序,游戏会自动把已经选中的人放到最前面 // 因为后面autofill要按工作状态排序,所以直接按工作状态排序好了 // 然后滑动到最左边,清空一下,在走后面的识别+按序点击逻辑 if (is_dorm_order) { ProcessTask(*this, { "InfrastOperListTabMoodDoubleClick" }).run(); } else { ProcessTask(*this, { "InfrastOperListTabWorkStatusUnClicked" }).run(); } if (room_config.sort || room_config.autofill) { swipe_to_the_left_of_operlist(swipe_times / 5 + 1); swipe_times = 0; } // 如果只选了一个人没必要排序 if (room_config.sort && room_config.selected > 1) { click_clear_button(); order_opers_selection(opers_order); } return room_config.names.empty(); } bool asst::InfrastAbstractTask::select_custom_opers(std::vector& partial_result) { LogTraceFunction; auto& room_config = current_room_config(); if (room_config.names.empty() && room_config.candidates.empty()) { Log.warn("opers_name is empty"); return false; } const auto image = m_ctrler->get_image(); InfrastOperImageAnalyzer oper_analyzer(image); oper_analyzer.set_to_be_calced(InfrastOperImageAnalyzer::ToBeCalced::Selected); if (!oper_analyzer.analyze()) { Log.warn("No oper"); return false; } oper_analyzer.sort_by_loc(); partial_result.clear(); const auto& ocr_replace = Task.get("CharsNameOcrReplace")->replace_map; for (const auto& oper : oper_analyzer.get_result()) { OcrWithPreprocessImageAnalyzer name_analyzer; name_analyzer.set_replace(ocr_replace); name_analyzer.set_image(oper.name_img); name_analyzer.set_expansion(0); if (!name_analyzer.analyze()) { continue; } const std::string& name = name_analyzer.get_result().front().text; partial_result.emplace_back(name); if (auto iter = ranges::find(room_config.names, name); iter != room_config.names.end()) { room_config.names.erase(iter); } else if (max_num_of_opers() - room_config.selected > room_config.names.size()) { // names中的数量,比剩余的空位多,就可以选备选的 if (auto candd_iter = ranges::find(room_config.candidates, name); candd_iter != room_config.candidates.end()) { room_config.candidates.erase(candd_iter); } else { continue; } } else { continue; } if (!oper.selected) { m_ctrler->click(oper.rect); } if (++room_config.selected >= max_num_of_opers()) { break; } } return true; } void asst::InfrastAbstractTask::order_opers_selection(const std::vector& names) { LogTraceFunction; if (names.empty()) { Log.warn("names is empty"); return; } const auto image = m_ctrler->get_image(); InfrastOperImageAnalyzer oper_analyzer(image); oper_analyzer.set_to_be_calced(InfrastOperImageAnalyzer::ToBeCalced::Selected); if (!oper_analyzer.analyze()) { Log.warn("No oper"); return; } oper_analyzer.sort_by_loc(); const auto& ocr_replace = Task.get("CharsNameOcrReplace")->replace_map; std::vector page_result; for (const auto& oper : oper_analyzer.get_result()) { OcrWithPreprocessImageAnalyzer name_analyzer; name_analyzer.set_replace(ocr_replace); name_analyzer.set_image(oper.name_img); name_analyzer.set_expansion(0); if (!name_analyzer.analyze()) { continue; } TextRect tr = name_analyzer.get_result().front(); tr.rect = oper.rect; page_result.emplace_back(std::move(tr)); } for (const std::string& name : names) { auto iter = ranges::find_if(page_result, [&name](const TextRect& tr) { return tr.text == name; }); if (iter != page_result.cend()) { m_ctrler->click(iter->rect); } else { Log.error("name not in this page", name); } } } void asst::InfrastAbstractTask::click_return_button() { LogTraceFunction; ProcessTask(*this, { "Infrast@ReturnTo" }).run(); } bool asst::InfrastAbstractTask::click_bottom_left_tab() { LogTraceFunction; ProcessTask task(*this, { "InfrastBottomLeftTab" }); return task.run(); } bool asst::InfrastAbstractTask::click_clear_button() { LogTraceFunction; for (int i = 0; i != 5; ++i) { if (need_exit()) { return false; } bool ret = ProcessTask(*this, { "InfrastClearButton" }).run(); // 有可能点快了,清空按钮刚刚出来,实际点上去还不生效,就点了 // 所以多识别一次,如果没清掉就再清一下 if (ret) { InfrastOperImageAnalyzer analyzer(m_ctrler->get_image()); analyzer.set_to_be_calced(InfrastOperImageAnalyzer::ToBeCalced::Selected); if (!analyzer.analyze()) { return false; } size_t selected_count = ranges::count_if(analyzer.get_result(), [](const infrast::Oper& info) { return info.selected; }); Log.info(__FUNCTION__, "after clear, selected_count = ", selected_count); if (selected_count == 0) { break; } } else { Log.error(__FUNCTION__, "clear failed"); return false; } } return true; } bool asst::InfrastAbstractTask::click_sort_by_trust_button() { LogTraceFunction; ProcessTask task(*this, { "InfrastSortByTrustButton" }); return task.run(); } bool asst::InfrastAbstractTask::click_filter_menu_not_stationed_button() { LogTraceFunction; ProcessTask task(*this, { "InfrastFilterMenuNotStationed" }); return task.run(); } bool asst::InfrastAbstractTask::click_filter_menu_cancel_not_stationed_button() { LogTraceFunction; ProcessTask task(*this, { "InfrastFilterMenuCancelNotStationed" }); return task.run(); } bool asst::InfrastAbstractTask::click_confirm_button() { LogTraceFunction; ProcessTask task(*this, { "InfrastDormConfirmButton" }); return task.run(); } void asst::InfrastAbstractTask::swipe_of_operlist(bool reverse) { async_swipe_of_operlist(reverse); await_swipe(); } void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist(int loop_times) { LogTraceFunction; static Rect begin_rect = Task.get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = Task.get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = Task.get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; static int extra_delay = Task.get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; static int cfg_loop_times = Task.get("InfrastOperListSwipeToTheLeftBegin")->max_times; for (int i = 0; i != cfg_loop_times * loop_times; ++i) { if (need_exit()) { return; } m_ctrler->swipe(end_rect, begin_rect, duration, true, 0, false); } sleep(extra_delay); } void asst::InfrastAbstractTask::swipe_to_the_left_of_main_ui() { LogTraceFunction; static Rect begin_rect = Task.get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = Task.get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = Task.get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; static int extra_delay = Task.get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; m_ctrler->swipe(end_rect, begin_rect, duration, true, extra_delay, false); } void asst::InfrastAbstractTask::swipe_to_the_right_of_main_ui() { LogTraceFunction; static Rect begin_rect = Task.get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; static Rect end_rect = Task.get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; static int duration = Task.get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; static int extra_delay = Task.get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; m_ctrler->swipe(begin_rect, end_rect, duration, true, extra_delay, false); }