Files
MaaAssistantArknights/src/MaaCore/Task/Infrast/InfrastReceptionTask.cpp

313 lines
8.7 KiB
C++

#include "InfrastReceptionTask.h"
#include <ranges>
#include "Config/TaskData.h"
#include "Controller/Controller.h"
#include "Task/ProcessTask.h"
#include "Utils/Logger.hpp"
#include "Utils/StringMisc.hpp"
#include "Vision/Infrast/InfrastClueVacancyImageAnalyzer.h"
#include "Vision/Matcher.h"
#include "Vision/MultiMatcher.h"
#include "Vision/RegionOCRer.h"
bool asst::InfrastReceptionTask::_run()
{
m_all_available_opers.clear();
swipe_to_the_left_of_main_ui();
if (!enter_facility()) {
swipe_to_right_of_main_ui();
if (!enter_facility()) {
return false;
}
}
if (m_receive_message_board) {
receive_message_board();
}
click_bottom_left_tab();
close_end_of_clue_exchange();
get_friend_clue();
if (need_exit()) {
return false;
}
if (m_enable_clue_exchange) {
use_clue();
back_to_reception_main();
}
if (m_send_clue) {
send_clue();
}
if (need_exit()) {
return false;
}
// 赠送线索后的弹窗会挡住自己新线索的图标
sleep(500);
get_self_clue();
if (need_exit()) {
return false;
}
if (m_enable_clue_exchange) {
use_clue();
back_to_reception_main();
}
if (need_exit()) {
return false;
}
if (!m_skip_shift) {
return shift();
}
Log.info("skip shift in rotation mode");
return true;
}
bool asst::InfrastReceptionTask::receive_message_board()
{
return ProcessTask(*this, { "InfrastReceptionReceiveMessageBoard" }).run();
}
bool asst::InfrastReceptionTask::close_end_of_clue_exchange()
{
ProcessTask task_temp(*this, { "EndOfClueExchangeBegin" });
return task_temp.run();
}
bool asst::InfrastReceptionTask::get_friend_clue()
{
ProcessTask task_temp(*this, { "InfrastClueFriendNew", "ReceptionFlag" });
return task_temp.set_retry_times(ProcessTask::RetryTimesDefault).run();
}
bool asst::InfrastReceptionTask::get_self_clue()
{
constexpr int kRetryTimesDefault = ProcessTask::RetryTimesDefault;
auto run_with_retries = [&](const std::vector<std::string>& tasks) {
ProcessTask task(*this, tasks);
task.set_retry_times(kRetryTimesDefault);
return task.run();
};
run_with_retries({ "InfrastClueSelfNew", "InfrastClueSelfMaybeFull", "ReceptionFlag" });
if (!ProcessTask(*this, { "InfrastClueSelfFull" }).set_retry_times(0).run()) {
return run_with_retries({ "CloseCluePage", "ReceptionFlag" });
}
if (m_enable_clue_exchange) {
return run_with_retries({ "CloseCluePageThenSendClue" });
}
return run_with_retries({ "CloseCluePage", "ReceptionFlag" });
}
bool asst::InfrastReceptionTask::use_clue()
{
LogTraceFunction;
const static std::string clue_vacancy = "InfrastClueVacancy";
const static std::vector<std::string> clue_suffix = { "No1", "No2", "No3", "No4", "No5", "No6", "No7" };
proc_clue_vacancy();
sleep(1000);
if (m_enable_clue_exchange && unlock_clue_exchange()) {
proc_clue_vacancy();
}
cv::Mat image = ctrler()->get_image();
// 所有的空位分析一次,看看还缺哪些线索
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
vacancy_analyzer.set_to_be_analyzed(clue_suffix);
vacancy_analyzer.analyze();
const auto& vacancy = vacancy_analyzer.get_vacancy();
for (const auto& id : vacancy | std::views::keys) {
Log.trace("InfrastReceptionTask | Vacancy", id);
}
std::string product;
if (vacancy.size() == 1) {
product = vacancy.begin()->first;
}
else {
product = "General";
}
Log.trace("InfrastReceptionTask | product", product);
set_product(product);
return true;
}
bool asst::InfrastReceptionTask::proc_clue_vacancy()
{
LogTraceFunction;
const static std::string clue_vacancy = "InfrastClueVacancy";
const static std::vector<std::string> clue_suffix = { "No1", "No2", "No3", "No4", "No5", "No6", "No7" };
cv::Mat image = ctrler()->get_image();
// 优先检测官服新增的“快捷置入”按钮,如果存在则尝试根据数字与空位一致时批量置入
if (ProcessTask(*this, { "InfrastClueQuickInsert" }).set_retry_times(3).run()) {
InfrastClueVacancyImageAnalyzer vacancy_analyzer(image);
vacancy_analyzer.set_to_be_analyzed(clue_suffix);
vacancy_analyzer.analyze();
const int vacancy_cnt = static_cast<int>(vacancy_analyzer.get_vacancy().size());
const auto confirm_task = Task.get("InfrastClueQuickInsertConfirm");
if (vacancy_cnt > 0 && confirm_task != nullptr) {
RegionOCRer ocr_analyzer(image);
ocr_analyzer.set_task_info(confirm_task);
if (auto ocr_res = ocr_analyzer.analyze()) {
int available = 0;
if (utils::chars_to_number(ocr_res->text, available) && available == vacancy_cnt) {
Rect click_rect = confirm_task->roi.move(confirm_task->rect_move);
ctrler()->click(click_rect);
}
}
return true;
}
}
for (const std::string& clue : clue_suffix) {
if (need_exit()) {
return false;
}
// 先识别线索的空位
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 = Task.get(clue_vacancy + clue)->post_delay;
sleep(delay);
// 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的)
// swipe_to_the_bottom_of_clue_list_on_the_right();
image = ctrler()->get_image();
MultiMatcher clue_analyzer(image);
clue_analyzer.set_task_info("InfrastClue");
auto clue_result_opt = clue_analyzer.analyze();
if (!clue_result_opt) {
continue;
}
sort_by_horizontal_(*clue_result_opt);
ctrler()->click(clue_result_opt->back().rect);
delay = Task.get("InfrastClue")->post_delay;
sleep(delay);
}
return true;
}
bool asst::InfrastReceptionTask::unlock_clue_exchange()
{
ProcessTask task(*this, { "UnlockClues" });
task.set_retry_times(2);
return task.run();
}
bool asst::InfrastReceptionTask::back_to_reception_main()
{
ProcessTask(*this, { "EndOfClueExchange" }).set_retry_times(0).run();
return ProcessTask(*this, { "BackToReceptionMain" }).run();
}
bool asst::InfrastReceptionTask::send_clue()
{
// 优先检测是否存在“快捷传递重复线索”按钮(官服特性),若存在则点击一次
ProcessTask task(*this, { "SendClues" });
return task.set_retry_times(20).run();
}
bool asst::InfrastReceptionTask::shift()
{
LogTraceFunction;
if (m_is_custom && current_room_config().skip) {
Log.info("skip this room");
return true;
}
const auto image = ctrler()->get_image();
Matcher add_analyzer(image);
const auto raw_task_ptr = Task.get("InfrastAddOperator" + facility_name() + m_work_mode_name);
switch (raw_task_ptr->algorithm) {
case AlgorithmType::JustReturn:
if (raw_task_ptr->action == ProcessTaskAction::ClickRect) {
ctrler()->click(raw_task_ptr->specific_rect);
}
break;
case AlgorithmType::MatchTemplate: {
add_analyzer.set_task_info(raw_task_ptr);
if (!add_analyzer.analyze()) {
return true;
}
ctrler()->click(add_analyzer.get_result().rect);
} break;
default:
break;
}
sleep(raw_task_ptr->post_delay);
close_quick_formation_expand_role();
int retry_times;
for (retry_times = 0; retry_times <= OperSelectRetryTimes; ++retry_times) {
if (need_exit()) {
return false;
}
if (is_use_custom_opers()) {
if (swipe_and_select_custom_opers()) {
break;
}
swipe_to_the_left_of_operlist();
continue;
}
if (!opers_detect_with_swipe()) {
return false;
}
swipe_to_the_left_of_operlist();
optimal_calc();
// 清空按钮放到识别完之后,现在通过切换职业栏来回到界面最左侧,先清空会导致当前设施里的人排到最后面
click_clear_button();
bool ret = opers_choose();
if (!ret) {
m_all_available_opers.clear();
swipe_to_the_left_of_operlist();
continue;
}
break;
}
if (retry_times > OperSelectRetryTimes) {
return false;
}
click_confirm_button();
return true;
}