From 029fafc8021bbfd2d2a8559e3a583100e515231b Mon Sep 17 00:00:00 2001 From: MistEO Date: Mon, 7 Mar 2022 22:09:06 +0800 Subject: [PATCH] =?UTF-8?q?style.=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MeoAssistant/AsstInfrastDef.h | 216 ++-- src/MeoAssistant/AsstUtils.hpp | 2 +- src/MeoAssistant/AutoRecruitTask.h | 54 +- src/MeoAssistant/Controller.cpp | 1088 +++++++++-------- src/MeoAssistant/Controller.h | 190 +-- src/MeoAssistant/GeneralConfiger.h | 156 +-- src/MeoAssistant/InfrastAbstractTask.cpp | 224 ++-- src/MeoAssistant/InfrastAbstractTask.h | 64 +- src/MeoAssistant/InfrastOperImageAnalyzer.cpp | 658 +++++----- src/MeoAssistant/InfrastReceptionTask.cpp | 264 ++-- src/MeoAssistant/Logger.hpp | 4 +- src/MeoAssistant/OcrImageAnalyzer.cpp | 206 ++-- src/MeoAssistant/OcrPack.h | 24 +- src/MeoAssistant/ProcessTask.cpp | 336 ++--- src/MeoAssistant/ProcessTask.h | 44 +- src/MeoAssistant/ProcessTaskImageAnalyzer.cpp | 194 +-- src/MeoAssistant/ProcessTaskImageAnalyzer.h | 90 +- src/MeoAssistant/RecruitConfiger.h | 78 +- src/MeoAssistant/RecruitImageAnalyzer.h | 84 +- src/MeoAssistant/RecruitTask.cpp | 4 +- src/MeoAssistant/RoguelikeBattleTaskPlugin.h | 60 +- src/MeoAssistant/TilePack.h | 94 +- 22 files changed, 2068 insertions(+), 2066 deletions(-) diff --git a/src/MeoAssistant/AsstInfrastDef.h b/src/MeoAssistant/AsstInfrastDef.h index 9ec00ab65c..b5dbf0ba47 100644 --- a/src/MeoAssistant/AsstInfrastDef.h +++ b/src/MeoAssistant/AsstInfrastDef.h @@ -4,127 +4,127 @@ namespace asst { - namespace infrast - { - struct Facility - { - ::std::string id; - ::std::vector<::std::string> products; - int max_num_of_opers = 0; - }; + namespace infrast + { + struct Facility + { + ::std::string id; + ::std::vector<::std::string> products; + int max_num_of_opers = 0; + }; - enum class SmileyType - { - Invalid = -1, - Rest, // 休息完成,绿色笑脸 - Work, // 工作中,黄色笑脸 - Distract // 注意力涣散,红色哭脸 - }; - struct Smiley - { - SmileyType type = SmileyType::Invalid; - Rect rect; - }; - enum class Doing // 正在做什么 - { - Invalid = -1, - Nothing, // 什么都不在做,也不在休息也不在工作 - Resting, // 休息中 - Working // 工作中 - }; + enum class SmileyType + { + Invalid = -1, + Rest, // 休息完成,绿色笑脸 + Work, // 工作中,黄色笑脸 + Distract // 注意力涣散,红色哭脸 + }; + struct Smiley + { + SmileyType type = SmileyType::Invalid; + Rect rect; + }; + enum class Doing // 正在做什么 + { + Invalid = -1, + Nothing, // 什么都不在做,也不在休息也不在工作 + Resting, // 休息中 + Working // 工作中 + }; - struct Skill - { - ::std::string id; - ::std::string templ_name; - ::std::vector<::std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字 - ::std::string desc; - ::std::unordered_map<::std::string, double> efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值 - ::std::unordered_map<::std::string, ::std::string> efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值 - int max_num = INT_MAX; // 最多选几个该技能 + struct Skill + { + ::std::string id; + ::std::string templ_name; + ::std::vector<::std::string> names; // 很多基建技能是一样的,就是名字不同。所以一个技能id可能对应多个名字 + ::std::string desc; + ::std::unordered_map<::std::string, double> efficient; // 技能效率,key:产品名(赤金、经验书等), value: 效率数值 + ::std::unordered_map<::std::string, ::std::string> efficient_regex; // 技能效率正则,key:产品名(赤金、经验书等), value: 效率正则。如不为空,会先对正则进行计算,再加上efficient里面的值 + int max_num = INT_MAX; // 最多选几个该技能 - bool operator==(const Skill& skill) const noexcept - { - return id == skill.id; - } - }; - } + bool operator==(const Skill& skill) const noexcept + { + return id == skill.id; + } + }; + } } namespace std { - template <> - class hash - { - public: - size_t operator()(const asst::infrast::Skill& skill) const - { - return ::std::hash()(skill.id); - } - }; + template <> + class hash + { + public: + size_t operator()(const asst::infrast::Skill& skill) const + { + return ::std::hash()(skill.id); + } + }; } namespace asst { - namespace infrast - { - struct Oper - { - ::std::string face_hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员 - ::std::string name_hash; // 预留 - Smiley smiley; - double mood_ratio = 0; // 心情进度条的百分比 - Doing doing = Doing::Invalid; - bool selected = false; // 干员是否已被选择(蓝色的选择框) - ::std::unordered_set skills; - Rect rect; - }; + namespace infrast + { + struct Oper + { + ::std::string face_hash; // 有些干员的技能是完全一样的,做个hash区分一下不同干员 + ::std::string name_hash; // 预留 + Smiley smiley; + double mood_ratio = 0; // 心情进度条的百分比 + Doing doing = Doing::Invalid; + bool selected = false; // 干员是否已被选择(蓝色的选择框) + ::std::unordered_set skills; + Rect rect; + }; - struct SkillsComb - { - SkillsComb() = default; - SkillsComb(std::unordered_set skill_vec) - { - skills = ::std::move(skill_vec); - for (const auto& s : skills) { - for (const auto& [key, value] : s.efficient) { - efficient[key] += value; - } - for (const auto& [key, reg] : s.efficient_regex) { - efficient_regex[key] += "+(" + reg + ")"; - } - } - } - bool operator==(const SkillsComb& rhs) const - { - return skills == rhs.skills; - } + struct SkillsComb + { + SkillsComb() = default; + SkillsComb(std::unordered_set skill_vec) + { + skills = ::std::move(skill_vec); + for (const auto& s : skills) { + for (const auto& [key, value] : s.efficient) { + efficient[key] += value; + } + for (const auto& [key, reg] : s.efficient_regex) { + efficient_regex[key] += "+(" + reg + ")"; + } + } + } + bool operator==(const SkillsComb& rhs) const + { + return skills == rhs.skills; + } - ::std::string desc; - ::std::unordered_set skills; - ::std::unordered_map efficient; - ::std::unordered_map efficient_regex; + ::std::string desc; + ::std::unordered_set skills; + ::std::unordered_map efficient; + ::std::unordered_map efficient_regex; - ::std::string name_hash; - bool hash_filter = false; - ::std::unordered_map possible_hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同,即使技能匹配了也不可用。hashs若为空,则不生效 - }; - // 基建技能组 - struct SkillsGroup - { - ::std::string desc; // 文字介绍,实际不起作用 - ::std::unordered_map conditions; // 技能组合可用条件,例如:key 发电站数量,value 3 - ::std::vector necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用 - ::std::vector optional; // 可选技能。 - bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员 - }; + ::std::string name_hash; + bool hash_filter = false; + ::std::unordered_map possible_hashs; // 限定只允许某些hash匹配的某些干员。若hash不相同,即使技能匹配了也不可用。hashs若为空,则不生效 + }; + // 基建技能组 + struct SkillsGroup + { + ::std::string desc; // 文字介绍,实际不起作用 + ::std::unordered_map conditions; // 技能组合可用条件,例如:key 发电站数量,value 3 + ::std::vector necessary; // 必选技能。这里面的缺少任一,则该技能组合不可用 + ::std::vector optional; // 可选技能。 + bool allow_external = false; // 当干员数没满3个的时候,是否允许补充外部干员 + }; - enum class WorkMode - { - Invalid = -1, - Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班 - Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员 - Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作 - }; - } + enum class WorkMode + { + Invalid = -1, + Gentle, // 温和换班模式:会对干员人数不满的设施进行换班,计算单设施内最优解,尽量不破坏原有的干员组合;即若设施内干员是满的,则不对该设施进行换班 + Aggressive, // 激进换班模式:会对每一个设施进行换班,计算单设施内最优解,但不会将其他设施中的干员替换过来;即按工作状态排序,仅选择前面的干员 + Extreme // 偏激换班模式:会对每一个设施进行换班,计算全局的单设施内最优解,为追求更高效率,会将其他设施内的干员也替换过来;即按技能排序,计算所有拥有该设施技能的干员效率,无论他在不在其他地方工作 + }; + } } diff --git a/src/MeoAssistant/AsstUtils.hpp b/src/MeoAssistant/AsstUtils.hpp index 565ab4475a..2d3ce27916 100644 --- a/src/MeoAssistant/AsstUtils.hpp +++ b/src/MeoAssistant/AsstUtils.hpp @@ -34,7 +34,7 @@ namespace asst inline std::string string_replace_all_batch(const std::string& src, const std::vector>& replace_pairs) { std::string str = src; - for (auto &[old_value, new_value] : replace_pairs) { + for (auto& [old_value, new_value] : replace_pairs) { str = string_replace_all(str, old_value, new_value); } return str; diff --git a/src/MeoAssistant/AutoRecruitTask.h b/src/MeoAssistant/AutoRecruitTask.h index 2e8b87bd3c..bb9c0be659 100644 --- a/src/MeoAssistant/AutoRecruitTask.h +++ b/src/MeoAssistant/AutoRecruitTask.h @@ -5,36 +5,36 @@ namespace asst { - class AutoRecruitTask final : public AbstractTask - { - public: - using AbstractTask::AbstractTask; - virtual ~AutoRecruitTask() = default; + class AutoRecruitTask final : public AbstractTask + { + public: + using AbstractTask::AbstractTask; + virtual ~AutoRecruitTask() = default; - AutoRecruitTask& set_select_level(std::vector select_level) noexcept; - AutoRecruitTask& set_confirm_level(std::vector confirm_level) noexcept; - AutoRecruitTask& set_need_refresh(bool need_refresh) noexcept; - AutoRecruitTask& set_max_times(int max_times) noexcept; - AutoRecruitTask& set_use_expedited(bool use_or_not) noexcept; + AutoRecruitTask& set_select_level(std::vector select_level) noexcept; + AutoRecruitTask& set_confirm_level(std::vector confirm_level) noexcept; + AutoRecruitTask& set_need_refresh(bool need_refresh) noexcept; + AutoRecruitTask& set_max_times(int max_times) noexcept; + AutoRecruitTask& set_use_expedited(bool use_or_not) noexcept; - protected: - virtual bool _run() override; + protected: + virtual bool _run() override; - bool analyze_start_buttons(); - bool recruit_index(size_t index); - bool calc_and_recruit(); - bool check_recruit_home_page(); - bool recruit_now(); - bool confirm(); - bool refresh(); + bool analyze_start_buttons(); + bool recruit_index(size_t index); + bool calc_and_recruit(); + bool check_recruit_home_page(); + bool recruit_now(); + bool confirm(); + bool refresh(); - std::vector m_select_level; - std::vector m_confirm_level; - bool m_need_refresh = false; - bool m_use_expedited = false; - int m_max_times = 0; + std::vector m_select_level; + std::vector m_confirm_level; + bool m_need_refresh = false; + bool m_use_expedited = false; + int m_max_times = 0; - std::vector m_start_buttons; - int m_cur_times = 0; - }; + std::vector m_start_buttons; + int m_cur_times = 0; + }; } diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index fc9dec2af0..de948241e6 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -23,85 +23,85 @@ #include "Resource.h" asst::Controller::Controller(AsstCallback callback, void* callback_arg) - : m_callback(callback), - m_callback_arg(callback_arg), - m_rand_engine(static_cast(time(nullptr))) + : m_callback(callback), + m_callback_arg(callback_arg), + m_rand_engine(static_cast(time(nullptr))) { - LogTraceFunction; + LogTraceFunction; #ifdef _WIN32 - // 安全属性描述符 - m_pipe_sec_attr.nLength = sizeof SECURITY_ATTRIBUTES; - m_pipe_sec_attr.lpSecurityDescriptor = nullptr; - m_pipe_sec_attr.bInheritHandle = TRUE; + // 安全属性描述符 + m_pipe_sec_attr.nLength = sizeof SECURITY_ATTRIBUTES; + m_pipe_sec_attr.lpSecurityDescriptor = nullptr; + m_pipe_sec_attr.bInheritHandle = TRUE; - // 创建管道,本进程读-子进程写 - BOOL pipe_read_ret = ::CreatePipe(&m_pipe_read, &m_pipe_child_write, &m_pipe_sec_attr, PipeBuffSize); - // 创建管道,本进程写-子进程读 - BOOL pipe_write_ret = ::CreatePipe(&m_pipe_write, &m_pipe_child_read, &m_pipe_sec_attr, PipeBuffSize); + // 创建管道,本进程读-子进程写 + BOOL pipe_read_ret = ::CreatePipe(&m_pipe_read, &m_pipe_child_write, &m_pipe_sec_attr, PipeBuffSize); + // 创建管道,本进程写-子进程读 + BOOL pipe_write_ret = ::CreatePipe(&m_pipe_write, &m_pipe_child_read, &m_pipe_sec_attr, PipeBuffSize); - if (!pipe_read_ret || !pipe_write_ret) { - throw "controller pipe created failed"; - } + if (!pipe_read_ret || !pipe_write_ret) { + throw "controller pipe created failed"; + } - m_child_startup_info.cb = sizeof(STARTUPINFO); - m_child_startup_info.dwFlags = STARTF_USESTDHANDLES; - m_child_startup_info.wShowWindow = SW_HIDE; - // 重定向子进程的读写 - m_child_startup_info.hStdInput = m_pipe_child_read; - m_child_startup_info.hStdOutput = m_pipe_child_write; - m_child_startup_info.hStdError = m_pipe_child_write; + m_child_startup_info.cb = sizeof(STARTUPINFO); + m_child_startup_info.dwFlags = STARTF_USESTDHANDLES; + m_child_startup_info.wShowWindow = SW_HIDE; + // 重定向子进程的读写 + m_child_startup_info.hStdInput = m_pipe_child_read; + m_child_startup_info.hStdOutput = m_pipe_child_write; + m_child_startup_info.hStdError = m_pipe_child_write; #else - int pipe_in_ret = pipe(m_pipe_in); - int pipe_out_ret = pipe(m_pipe_out); - fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); + int pipe_in_ret = pipe(m_pipe_in); + int pipe_out_ret = pipe(m_pipe_out); + fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); - if (pipe_in_ret < 0 || pipe_out_ret < 0) { - throw "controller pipe created failed"; - } + if (pipe_in_ret < 0 || pipe_out_ret < 0) { + throw "controller pipe created failed"; + } #endif - m_pipe_buffer = std::make_unique(PipeBuffSize); - if (!m_pipe_buffer) { - throw "controller pipe buffer allocated failed"; - } + m_pipe_buffer = std::make_unique(PipeBuffSize); + if (!m_pipe_buffer) { + throw "controller pipe buffer allocated failed"; + } - m_cmd_thread = std::thread(&Controller::pipe_working_proc, this); + m_cmd_thread = std::thread(&Controller::pipe_working_proc, this); } asst::Controller::~Controller() { - LogTraceFunction; + LogTraceFunction; - m_thread_exit = true; - //m_thread_idle = true; - m_cmd_condvar.notify_all(); - m_completed_id = UINT_MAX; // make all WinMacor::wait to exit + m_thread_exit = true; + //m_thread_idle = true; + m_cmd_condvar.notify_all(); + m_completed_id = UINT_MAX; // make all WinMacor::wait to exit - if (m_cmd_thread.joinable()) { - m_cmd_thread.join(); - } + if (m_cmd_thread.joinable()) { + m_cmd_thread.join(); + } #ifndef _WIN32 - if (m_child) { + if (m_child) { #else - if (true) { + if (true) { #endif - call_command(m_adb.release); - } + call_command(m_adb.release); + } #ifdef _WIN32 - ::CloseHandle(m_pipe_read); - ::CloseHandle(m_pipe_write); - ::CloseHandle(m_pipe_child_read); - ::CloseHandle(m_pipe_child_write); + ::CloseHandle(m_pipe_read); + ::CloseHandle(m_pipe_write); + ::CloseHandle(m_pipe_child_read); + ::CloseHandle(m_pipe_child_write); #else - close(m_pipe_in[PIPE_READ]); - close(m_pipe_in[PIPE_WRITE]); - close(m_pipe_out[PIPE_READ]); - close(m_pipe_out[PIPE_WRITE]); + close(m_pipe_in[PIPE_READ]); + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); #endif - } +} //asst::Rect asst::Controller::shaped_correct(const Rect & rect) const //{ @@ -158,644 +158,646 @@ asst::Controller::~Controller() std::pair asst::Controller::get_scale_size() const noexcept { - return m_scale_size; + return m_scale_size; } void asst::Controller::pipe_working_proc() { - LogTraceFunction; + LogTraceFunction; - while (!m_thread_exit) { - std::unique_lock cmd_queue_lock(m_cmd_queue_mutex); + while (!m_thread_exit) { + std::unique_lock cmd_queue_lock(m_cmd_queue_mutex); - if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务 - std::string cmd = m_cmd_queue.front(); - m_cmd_queue.pop(); - cmd_queue_lock.unlock(); - // todo 判断命令是否执行成功 - call_command(cmd); - ++m_completed_id; - } - //else if (!m_thread_idle) { // 队列中没有任务,又不是闲置的时候,就去截图 - // cmd_queue_lock.unlock(); - // auto start_time = std::chrono::steady_clock::now(); - // screencap(); - // cmd_queue_lock.lock(); - // if (!m_cmd_queue.empty()) { - // continue; - // } - // m_cmd_condvar.wait_until( - // cmd_queue_lock, - // start_time + std::chrono::milliseconds(1000)); // todo 时间写到配置文件里 - //} - else { - //m_cmd_condvar.wait(cmd_queue_lock, [&]() -> bool {return !m_thread_idle; }); - m_cmd_condvar.wait(cmd_queue_lock); - } - } + if (!m_cmd_queue.empty()) { // 队列中有任务就执行任务 + std::string cmd = m_cmd_queue.front(); + m_cmd_queue.pop(); + cmd_queue_lock.unlock(); + // todo 判断命令是否执行成功 + call_command(cmd); + ++m_completed_id; + } + //else if (!m_thread_idle) { // 队列中没有任务,又不是闲置的时候,就去截图 + // cmd_queue_lock.unlock(); + // auto start_time = std::chrono::steady_clock::now(); + // screencap(); + // cmd_queue_lock.lock(); + // if (!m_cmd_queue.empty()) { + // continue; + // } + // m_cmd_condvar.wait_until( + // cmd_queue_lock, + // start_time + std::chrono::milliseconds(1000)); // todo 时间写到配置文件里 + //} + else { + //m_cmd_condvar.wait(cmd_queue_lock, [&]() -> bool {return !m_thread_idle; }); + m_cmd_condvar.wait(cmd_queue_lock); + } + } } std::optional> asst::Controller::call_command(const std::string & cmd) { - LogTraceFunction; + LogTraceFunction; - static std::mutex pipe_mutex; - std::unique_lock pipe_lock(pipe_mutex); + static std::mutex pipe_mutex; + std::unique_lock pipe_lock(pipe_mutex); - std::vector pipe_data; + std::vector pipe_data; #ifdef _WIN32 - PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体 - BOOL create_ret = ::CreateProcessA(nullptr, const_cast(cmd.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &m_child_startup_info, &process_info); - if (!create_ret) { - return std::nullopt; - } - DWORD peek_num = 0; - DWORD read_num = 0; - do { - //DWORD write_num = 0; - //WriteFile(parent_write, cmd.c_str(), cmd.size(), &write_num, nullptr); - while (::PeekNamedPipe(m_pipe_read, nullptr, 0, nullptr, &peek_num, nullptr) && peek_num > 0) { - if (::ReadFile(m_pipe_read, m_pipe_buffer.get(), PipeBuffSize, &read_num, nullptr)) { - pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); - } - } - } while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT); + PROCESS_INFORMATION process_info = { 0 }; // 进程信息结构体 + BOOL create_ret = ::CreateProcessA(nullptr, const_cast(cmd.c_str()), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, nullptr, &m_child_startup_info, &process_info); + if (!create_ret) { + return std::nullopt; + } + DWORD peek_num = 0; + DWORD read_num = 0; + do { + //DWORD write_num = 0; + //WriteFile(parent_write, cmd.c_str(), cmd.size(), &write_num, nullptr); + while (::PeekNamedPipe(m_pipe_read, nullptr, 0, nullptr, &peek_num, nullptr) && peek_num > 0) { + if (::ReadFile(m_pipe_read, m_pipe_buffer.get(), PipeBuffSize, &read_num, nullptr)) { + pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); + } + } + } while (::WaitForSingleObject(process_info.hProcess, 0) == WAIT_TIMEOUT); - DWORD exit_ret = 255; - ::GetExitCodeProcess(process_info.hProcess, &exit_ret); + DWORD exit_ret = 255; + ::GetExitCodeProcess(process_info.hProcess, &exit_ret); - ::CloseHandle(process_info.hProcess); - ::CloseHandle(process_info.hThread); + ::CloseHandle(process_info.hProcess); + ::CloseHandle(process_info.hThread); #else - int exit_ret = 0; - m_child = fork(); - if (m_child == 0) { - // child process - LogTraceScope("Child process: " + cmd); - std::cout << ("Child process: " + cmd) << std::endl; + int exit_ret = 0; + m_child = fork(); + if (m_child == 0) { + // child process + LogTraceScope("Child process: " + cmd); + std::cout << ("Child process: " + cmd) << std::endl; - dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); - dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); - dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); + dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); - // all these are for use by parent only - // close(m_pipe_in[PIPE_READ]); - // close(m_pipe_in[PIPE_WRITE]); - // close(m_pipe_out[PIPE_READ]); - // close(m_pipe_out[PIPE_WRITE]); + // all these are for use by parent only + // close(m_pipe_in[PIPE_READ]); + // close(m_pipe_in[PIPE_WRITE]); + // close(m_pipe_out[PIPE_READ]); + // close(m_pipe_out[PIPE_WRITE]); - exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr); - exit(exit_ret); - } - else if (m_child > 0) { - // parent process - // LogTraceScope("Parent process: " + cmd); - do { - ssize_t read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); + exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), nullptr); + exit(exit_ret); + } + else if (m_child > 0) { + // parent process + // LogTraceScope("Parent process: " + cmd); + do { + ssize_t read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); - while (read_num > 0) { - pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); - read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); - }; - } while (::waitpid(m_child, &exit_ret, WNOHANG) == 0); - } - else { - // failed to create child process - return std::nullopt; - } + while (read_num > 0) { + pipe_data.insert(pipe_data.end(), m_pipe_buffer.get(), m_pipe_buffer.get() + read_num); + read_num = read(m_pipe_out[PIPE_READ], m_pipe_buffer.get(), PipeBuffSize); + }; + } while (::waitpid(m_child, &exit_ret, WNOHANG) == 0); + } + else { + // failed to create child process + return std::nullopt; + } #endif - Log.trace("Call `", cmd, "` ret", exit_ret, ", output size:", pipe_data.size()); - if (!pipe_data.empty() && pipe_data.size() < 4096) { - Log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); - } + Log.trace("Call `", cmd, "` ret", exit_ret, ", output size:", pipe_data.size()); + if (!pipe_data.empty() && pipe_data.size() < 4096) { + Log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); + } - if (!exit_ret) { - return pipe_data; - } - else { - return std::nullopt; - } + if (!exit_ret) { + return pipe_data; + } + else { + return std::nullopt; + } } void asst::Controller::convert_lf(std::vector&data) { - LogTraceFunction; + LogTraceFunction; - if (data.empty() || data.size() < 2) { - return; - } - using Iter = std::vector::iterator; - auto pred = [](const Iter& cur) -> bool { - return *cur == '\r' && *(cur + 1) == '\n'; - }; - // find the first of "\r\n" - Iter first_iter = data.end(); - for (Iter iter = data.begin(); iter != data.end() - 1; ++iter) { - if (pred(iter)) { - first_iter = iter; - break; - } - } - if (first_iter == data.end()) { - return; - } - // move forward all non-crlf elements - Iter end_r1_iter = data.end() - 1; - Iter next_iter = first_iter; - while (++first_iter != end_r1_iter) { - if (!pred(first_iter)) { - *next_iter = std::move(*first_iter); - ++next_iter; - } - } - *next_iter = std::move(*end_r1_iter); - ++next_iter; - data.erase(next_iter, data.end()); + if (data.empty() || data.size() < 2) { + return; + } + using Iter = std::vector::iterator; + auto pred = [](const Iter& cur) -> bool { + return *cur == '\r' && *(cur + 1) == '\n'; + }; + // find the first of "\r\n" + Iter first_iter = data.end(); + for (Iter iter = data.begin(); iter != data.end() - 1; ++iter) { + if (pred(iter)) { + first_iter = iter; + break; + } + } + if (first_iter == data.end()) { + return; + } + // move forward all non-crlf elements + Iter end_r1_iter = data.end() - 1; + Iter next_iter = first_iter; + while (++first_iter != end_r1_iter) { + if (!pred(first_iter)) { + *next_iter = std::move(*first_iter); + ++next_iter; + } + } + *next_iter = std::move(*end_r1_iter); + ++next_iter; + data.erase(next_iter, data.end()); } asst::Point asst::Controller::rand_point_in_rect(const Rect & rect) { - int x = 0, y = 0; - if (rect.width == 0) { - x = rect.x; - } - else { - int x_rand = std::poisson_distribution(rect.width / 2)(m_rand_engine); + int x = 0, y = 0; + if (rect.width == 0) { + x = rect.x; + } + else { + int x_rand = std::poisson_distribution(rect.width / 2)(m_rand_engine); - x = x_rand + rect.x; - } + x = x_rand + rect.x; + } - if (rect.height == 0) { - y = rect.y; - } - else { - int y_rand = std::poisson_distribution(rect.height / 2)(m_rand_engine); - y = y_rand + rect.y; - } + if (rect.height == 0) { + y = rect.y; + } + else { + int y_rand = std::poisson_distribution(rect.height / 2)(m_rand_engine); + y = y_rand + rect.y; + } - return Point(x, y); + return Point(x, y); } void asst::Controller::random_delay() const { - auto& opt = Resrc.cfg().get_options(); - if (opt.control_delay_upper != 0) { - LogTraceFunction; - static std::default_random_engine rand_engine(static_cast(time(nullptr))); - static std::uniform_int_distribution rand_uni( - opt.control_delay_lower, - opt.control_delay_upper); + auto& opt = Resrc.cfg().get_options(); + if (opt.control_delay_upper != 0) { + LogTraceFunction; + static std::default_random_engine rand_engine(static_cast(time(nullptr))); + static std::uniform_int_distribution rand_uni( + opt.control_delay_lower, + opt.control_delay_upper); - unsigned rand_delay = rand_uni(rand_engine); + unsigned rand_delay = rand_uni(rand_engine); - Log.trace("random_delay |", rand_delay, "ms"); - std::this_thread::sleep_for(std::chrono::milliseconds(rand_delay)); - } + Log.trace("random_delay |", rand_delay, "ms"); + std::this_thread::sleep_for(std::chrono::milliseconds(rand_delay)); + } } int asst::Controller::push_cmd(const std::string & cmd) { - random_delay(); + random_delay(); - std::unique_lock lock(m_cmd_queue_mutex); - m_cmd_queue.emplace(cmd); - m_cmd_condvar.notify_one(); - return ++m_push_id; + std::unique_lock lock(m_cmd_queue_mutex); + m_cmd_queue.emplace(cmd); + m_cmd_condvar.notify_one(); + return ++m_push_id; } void asst::Controller::wait(unsigned id) const noexcept { - while (id > m_completed_id) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - std::this_thread::yield(); - } + while (id > m_completed_id) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::this_thread::yield(); + } } bool asst::Controller::screencap() { - LogTraceFunction; + LogTraceFunction; - DecodeFunc decode_raw_with_gzip = [&](const std::vector& data) -> bool { - auto unzip_data = gzip::decompress(data.data(), data.size()); - Log.trace("unzip data size:", unzip_data.size()); - if (unzip_data.empty()) { - return false; - } - size_t std_size = m_width * m_height * 4; - if (unzip_data.size() < std_size) { - return false; - } - size_t header_size = unzip_data.size() - std_size; - Log.trace("header size:", header_size); + DecodeFunc decode_raw_with_gzip = [&](const std::vector& data) -> bool { + auto unzip_data = gzip::decompress(data.data(), data.size()); + Log.trace("unzip data size:", unzip_data.size()); + if (unzip_data.empty()) { + return false; + } + size_t std_size = m_width * m_height * 4; + if (unzip_data.size() < std_size) { + return false; + } + size_t header_size = unzip_data.size() - std_size; + Log.trace("header size:", header_size); - bool is_all_zero = std::all_of(unzip_data.data() + header_size, unzip_data.data() + std_size, - [](uchar uch) -> bool { - return uch == 0; - }); - if (is_all_zero) { - return false; - } - cv::Mat temp(m_height, m_width, CV_8UC4, unzip_data.data() + header_size); - if (temp.empty()) { - return false; - } - cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); - std::unique_lock image_lock(m_image_mutex); - m_cache_image = temp; - return true; - }; + bool is_all_zero = std::all_of(unzip_data.data() + header_size, unzip_data.data() + std_size, + [](uchar uch) -> bool { + return uch == 0; + }); + if (is_all_zero) { + return false; + } + cv::Mat temp(m_height, m_width, CV_8UC4, unzip_data.data() + header_size); + if (temp.empty()) { + return false; + } + cv::cvtColor(temp, temp, cv::COLOR_RGB2BGR); + std::unique_lock image_lock(m_image_mutex); + m_cache_image = temp; + return true; + }; - DecodeFunc decode_encode = [&](const std::vector& data) -> bool { - cv::Mat temp = cv::imdecode(data, cv::IMREAD_COLOR); - if (temp.empty()) { - return false; - } - std::unique_lock image_lock(m_image_mutex); - m_cache_image = temp; - return true; - }; + DecodeFunc decode_encode = [&](const std::vector& data) -> bool { + cv::Mat temp = cv::imdecode(data, cv::IMREAD_COLOR); + if (temp.empty()) { + return false; + } + std::unique_lock image_lock(m_image_mutex); + m_cache_image = temp; + return true; + }; - switch (m_adb.screencap_method) { - case AdbProperty::ScreencapMethod::UnknownYet: - { - if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { - m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip; - return true; - } - else if (screencap(m_adb.screencap_encode, decode_encode)) { - m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode; - return true; - } - else { - return false; - } - } - case AdbProperty::ScreencapMethod::RawWithGzip: - { - return screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip); - } - break; - case AdbProperty::ScreencapMethod::Encode: - { - return screencap(m_adb.screencap_encode, decode_encode); - } - break; - } + switch (m_adb.screencap_method) { + case AdbProperty::ScreencapMethod::UnknownYet: + { + if (screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip)) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::RawWithGzip; + return true; + } + else if (screencap(m_adb.screencap_encode, decode_encode)) { + m_adb.screencap_method = AdbProperty::ScreencapMethod::Encode; + return true; + } + else { + return false; + } + } + case AdbProperty::ScreencapMethod::RawWithGzip: + { + return screencap(m_adb.screencap_raw_with_gzip, decode_raw_with_gzip); + } + break; + case AdbProperty::ScreencapMethod::Encode: + { + return screencap(m_adb.screencap_encode, decode_encode); + } + break; + } - return false; + return false; } bool asst::Controller::screencap(const std::string & cmd, DecodeFunc decode_func) { - auto ret = call_command(cmd); + auto ret = call_command(cmd); - if (!ret || ret.value().empty()) { - Log.error("data is empty!"); - return false; - } - auto data = std::move(ret).value(); + if (!ret || ret.value().empty()) { + Log.error("data is empty!"); + return false; + } + auto data = std::move(ret).value(); - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::CRLF) { - convert_lf(data); - } + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::CRLF) { + convert_lf(data); + } - if (decode_func(data)) { - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { - m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::LF; - } - return true; - } - else { - Log.info("data is not empty, but image is empty"); + if (decode_func(data)) { + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { + m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::LF; + } + return true; + } + else { + Log.info("data is not empty, but image is empty"); - if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { - Log.info("try to cvt lf"); - convert_lf(data); + if (m_adb.screencap_end_of_line == AdbProperty::ScreencapEndOfLine::UnknownYet) { + Log.info("try to cvt lf"); + convert_lf(data); - if (decode_func(data)) { - m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::CRLF; - return true; - } - else { - Log.error("convert lf and retry decode falied!"); - } - } - return false; - } + if (decode_func(data)) { + m_adb.screencap_end_of_line = AdbProperty::ScreencapEndOfLine::CRLF; + return true; + } + else { + Log.error("convert lf and retry decode falied!"); + } + } + return false; + } } cv::Mat asst::Controller::get_resized_image() const { - const static cv::Size dsize(m_scale_size.first, m_scale_size.second); + const static cv::Size dsize(m_scale_size.first, m_scale_size.second); - std::shared_lock image_lock(m_image_mutex); - if (m_cache_image.empty()) { - Log.error("image is empty"); - return cv::Mat(dsize, CV_8UC3); - } - cv::Mat resized_mat; - cv::resize(m_cache_image, resized_mat, dsize); - return resized_mat; + std::shared_lock image_lock(m_image_mutex); + if (m_cache_image.empty()) { + Log.error("image is empty"); + return cv::Mat(dsize, CV_8UC3); + } + cv::Mat resized_mat; + cv::resize(m_cache_image, resized_mat, dsize); + return resized_mat; } int asst::Controller::click(const Point & p, bool block) { - int x = static_cast(p.x * m_control_scale); - int y = static_cast(p.y * m_control_scale); - //log.trace("Click, raw:", p.x, p.y, "corr:", x, y); + int x = static_cast(p.x * m_control_scale); + int y = static_cast(p.y * m_control_scale); + //log.trace("Click, raw:", p.x, p.y, "corr:", x, y); - return click_without_scale(Point(x, y), block); + return click_without_scale(Point(x, y), block); } int asst::Controller::click(const Rect & rect, bool block) { - return click(rand_point_in_rect(rect), block); + return click(rand_point_in_rect(rect), block); } int asst::Controller::click_without_scale(const Point & p, bool block) { - if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height) { - Log.error("click point out of range"); - } - std::string cur_cmd = utils::string_replace_all(m_adb.click, "[x]", std::to_string(p.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y]", std::to_string(p.y)); - int id = push_cmd(cur_cmd); - if (block) { - wait(id); - } - return id; + if (p.x < 0 || p.x >= m_width || p.y < 0 || p.y >= m_height) { + Log.error("click point out of range"); + } + std::string cur_cmd = utils::string_replace_all(m_adb.click, "[x]", std::to_string(p.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y]", std::to_string(p.y)); + int id = push_cmd(cur_cmd); + if (block) { + wait(id); + } + return id; } int asst::Controller::click_without_scale(const Rect & rect, bool block) { - return click_without_scale(rand_point_in_rect(rect), block); + return click_without_scale(rand_point_in_rect(rect), block); } int asst::Controller::swipe(const Point & p1, const Point & p2, int duration, bool block, int extra_delay, bool extra_swipe) { - int x1 = static_cast(p1.x * m_control_scale); - int y1 = static_cast(p1.y * m_control_scale); - int x2 = static_cast(p2.x * m_control_scale); - int y2 = static_cast(p2.y * m_control_scale); - //log.trace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); + int x1 = static_cast(p1.x * m_control_scale); + int y1 = static_cast(p1.y * m_control_scale); + int x2 = static_cast(p2.x * m_control_scale); + int y2 = static_cast(p2.y * m_control_scale); + //log.trace("Swipe, raw:", p1.x, p1.y, p2.x, p2.y, "corr:", x1, y1, x2, y2); - return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay, extra_swipe); + return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay, extra_swipe); } int asst::Controller::swipe(const Rect & r1, const Rect & r2, int duration, bool block, int extra_delay, bool extra_swipe) { - return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); + return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } int asst::Controller::swipe_without_scale(const Point & p1, const Point & p2, int duration, bool block, int extra_delay, bool extra_swipe) { - if (p1.x < 0 || p1.x >= m_width || p1.y < 0 || p1.y >= m_height || p2.x < 0 || p2.x >= m_width || p2.y < 0 || p2.y >= m_height) { - Log.error("swipe point out of range"); - } - std::string cur_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p1.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y1]", std::to_string(p1.y)); - cur_cmd = utils::string_replace_all(cur_cmd, "[x2]", std::to_string(p2.x)); - cur_cmd = utils::string_replace_all(cur_cmd, "[y2]", std::to_string(p2.y)); - if (duration <= 0) { - cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", ""); - } - else { - cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", std::to_string(duration)); - } + if (p1.x < 0 || p1.x >= m_width || p1.y < 0 || p1.y >= m_height || p2.x < 0 || p2.x >= m_width || p2.y < 0 || p2.y >= m_height) { + Log.error("swipe point out of range"); + } + std::string cur_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p1.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y1]", std::to_string(p1.y)); + cur_cmd = utils::string_replace_all(cur_cmd, "[x2]", std::to_string(p2.x)); + cur_cmd = utils::string_replace_all(cur_cmd, "[y2]", std::to_string(p2.y)); + if (duration <= 0) { + cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", ""); + } + else { + cur_cmd = utils::string_replace_all(cur_cmd, "[duration]", std::to_string(duration)); + } - int id = 0; + int id = 0; - int extra_swipe_dist = Resrc.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/; - int extra_swipe_duration = Resrc.cfg().get_options().adb_extra_swipe_duration; + int extra_swipe_dist = Resrc.cfg().get_options().adb_extra_swipe_dist /* * m_control_scale*/; + int extra_swipe_duration = Resrc.cfg().get_options().adb_extra_swipe_duration; - // 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 - if (extra_swipe && extra_swipe_duration >= 0) { - std::string extra_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p2.x)); - extra_cmd = utils::string_replace_all(extra_cmd, "[y1]", std::to_string(p2.y)); - int end_x = 0, end_y = 0; - if (p2.x != p1.x) { - double k = (double)(p2.y - p1.y) / (p2.x - p1.x); - double temp = extra_swipe_dist / std::sqrt(1 + k * k); - end_x = p2.x + static_cast((p2.x > p1.x ? -1.0 : 1.0) * temp); - end_y = p2.y + static_cast((p2.y > p1.y ? -1.0 : 1.0) * std::fabs(k) * temp); - } - else { - end_x = p2.x; - end_y = p2.y + (p2.y > p1.y ? -1 : 1) * extra_swipe_dist; - } - extra_cmd = utils::string_replace_all(extra_cmd, "[x2]", std::to_string(end_x)); - extra_cmd = utils::string_replace_all(extra_cmd, "[y2]", std::to_string(end_y)); - extra_cmd = utils::string_replace_all(extra_cmd, "[duration]", std::to_string(extra_swipe_duration)); + // 额外的滑动:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 + if (extra_swipe && extra_swipe_duration >= 0) { + std::string extra_cmd = utils::string_replace_all(m_adb.swipe, "[x1]", std::to_string(p2.x)); + extra_cmd = utils::string_replace_all(extra_cmd, "[y1]", std::to_string(p2.y)); + int end_x = 0, end_y = 0; + if (p2.x != p1.x) { + double k = (double)(p2.y - p1.y) / (p2.x - p1.x); + double temp = extra_swipe_dist / std::sqrt(1 + k * k); + end_x = p2.x + static_cast((p2.x > p1.x ? -1.0 : 1.0) * temp); + end_y = p2.y + static_cast((p2.y > p1.y ? -1.0 : 1.0) * std::fabs(k) * temp); + } + else { + end_x = p2.x; + end_y = p2.y + (p2.y > p1.y ? -1 : 1) * extra_swipe_dist; + } + extra_cmd = utils::string_replace_all(extra_cmd, "[x2]", std::to_string(end_x)); + extra_cmd = utils::string_replace_all(extra_cmd, "[y2]", std::to_string(end_y)); + extra_cmd = utils::string_replace_all(extra_cmd, "[duration]", std::to_string(extra_swipe_duration)); - push_cmd(cur_cmd); - id = push_cmd(extra_cmd); - } - else { - id = push_cmd(cur_cmd); - } + push_cmd(cur_cmd); + id = push_cmd(extra_cmd); + } + else { + id = push_cmd(cur_cmd); + } - if (block) { - wait(id); - std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay)); - } - return id; + if (block) { + wait(id); + std::this_thread::sleep_for(std::chrono::milliseconds(extra_delay)); + } + return id; } int asst::Controller::swipe_without_scale(const Rect & r1, const Rect & r2, int duration, bool block, int extra_delay, bool extra_swipe) { - return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); + return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } bool asst::Controller::connect(const std::string & adb_path, const std::string & address, const std::string & config) { - LogTraceFunction; + LogTraceFunction; - auto adb_ret = Resrc.cfg().get_adb_cfg(config); - if (!adb_ret) { - json::value info = json::object{ - {"what", "ConnectFailed"}, - {"why", "ConfigNotFound"}, - {"details", json::object{}} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + auto adb_ret = Resrc.cfg().get_adb_cfg(config); + if (!adb_ret) { + json::value info = json::object{ + {"what", "ConnectFailed"}, + {"why", "ConfigNotFound"}, + {"details", json::object{}} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - const auto adb_cfg = std::move(adb_ret.value()); - std::string display_id; + const auto adb_cfg = std::move(adb_ret.value()); + std::string display_id; - auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string { - const std::unordered_map replacements = { - {"[Adb]", adb_path}, - {"[Address]", address}, - {"[DisplayId]", display_id} - }; - std::string formatted = cfg_cmd; - for (const auto& [key, value] : replacements) { - formatted = utils::string_replace_all(formatted, key, value); - } - return formatted; - }; + auto cmd_replace = [&](const std::string& cfg_cmd) -> std::string { + const std::unordered_map replacements = { + {"[Adb]", adb_path}, + {"[Address]", address}, + {"[DisplayId]", display_id} + }; + std::string formatted = cfg_cmd; + for (const auto& [key, value] : replacements) { + formatted = utils::string_replace_all(formatted, key, value); + } + return formatted; + }; - /* connect */ - { - auto connect_ret = call_command(cmd_replace(adb_cfg.connect)); - // 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断 - if (!connect_ret) { - json::value info = json::object{ - {"what", "ConnectFailed"}, - {"why", "Connection command failed to exec"}, - {"details", json::object{}} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } - else { - json::value info = json::object{ - {"what", "Connected"}, - {"why", ""}, - {"details", json::object{}} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } - } + /* connect */ + { + auto connect_ret = call_command(cmd_replace(adb_cfg.connect)); + // 端口即使错误,命令仍然会返回0,TODO 对connect_result进行判断 + if (!connect_ret) { + json::value info = json::object{ + {"what", "ConnectFailed"}, + {"why", "Connection command failed to exec"}, + {"details", json::object{}} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } + else { + json::value info = json::object{ + {"what", "Connected"}, + {"why", ""}, + {"details", json::object{}} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } + } - // 按需获取display ID 信息 - if (!adb_cfg.display_id.empty()) { - auto display_id_ret = call_command(cmd_replace(adb_cfg.display_id)); - if (!display_id_ret) { - return false; - } + // 按需获取display ID 信息 + if (!adb_cfg.display_id.empty()) { + auto display_id_ret = call_command(cmd_replace(adb_cfg.display_id)); + if (!display_id_ret) { + return false; + } - auto& display_id_result = display_id_ret.value(); - convert_lf(display_id_result); - std::string display_id_pipe_str( - std::make_move_iterator(display_id_result.begin()), - std::make_move_iterator(display_id_result.end())); - auto last = display_id_pipe_str.rfind(':'); - if (last == std::string::npos) { - return false; - } + auto& display_id_result = display_id_ret.value(); + convert_lf(display_id_result); + std::string display_id_pipe_str( + std::make_move_iterator(display_id_result.begin()), + std::make_move_iterator(display_id_result.end())); + auto last = display_id_pipe_str.rfind(':'); + if (last == std::string::npos) { + return false; + } - display_id = display_id_pipe_str.substr(last + 1); - // 去掉换行 - display_id.pop_back(); - } + display_id = display_id_pipe_str.substr(last + 1); + // 去掉换行 + display_id.pop_back(); + } - /* display */ - { - auto display_ret = call_command(cmd_replace(adb_cfg.display)); - if (!display_ret) { - json::value info = json::object{ - {"what", "ConnectFailed"}, - {"why", "Display command failed to exec"}, - {"details", json::object{}} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + /* display */ + { + auto display_ret = call_command(cmd_replace(adb_cfg.display)); + if (!display_ret) { + json::value info = json::object{ + {"what", "ConnectFailed"}, + {"why", "Display command failed to exec"}, + {"details", json::object{}} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - auto& display_result = display_ret.value(); - std::string display_pipe_str( - std::make_move_iterator(display_result.begin()), - std::make_move_iterator(display_result.end())); - int size_value1 = 0; - int size_value2 = 0; + auto& display_result = display_ret.value(); + std::string display_pipe_str( + std::make_move_iterator(display_result.begin()), + std::make_move_iterator(display_result.end())); + int size_value1 = 0; + int size_value2 = 0; #ifdef _MSC_VER - sscanf_s(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); + sscanf_s(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); #else - sscanf(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); + sscanf(display_pipe_str.c_str(), adb_cfg.display_format.c_str(), &size_value1, &size_value2); #endif - // 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高 - // 总不能有人竖屏玩明日方舟吧(? - m_width = (std::max)(size_value1, size_value2); - m_height = (std::min)(size_value1, size_value2); - } + // 为了防止抓取句柄的时候手机是竖屏的(还没进游戏),这里取大的值为宽,小的为高 + // 总不能有人竖屏玩明日方舟吧(? + m_width = (std::max)(size_value1, size_value2); + m_height = (std::min)(size_value1, size_value2); + } - /* calc ratio */ - { - constexpr double DefaultRatio = - static_cast(WindowWidthDefault) / static_cast(WindowHeightDefault); - double cur_ratio = static_cast(m_width) / static_cast(m_height); + /* calc ratio */ + { + constexpr double DefaultRatio = + static_cast(WindowWidthDefault) / static_cast(WindowHeightDefault); + double cur_ratio = static_cast(m_width) / static_cast(m_height); - if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 - || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { - int scale_width = static_cast(cur_ratio * WindowHeightDefault); - m_scale_size = std::make_pair(scale_width, WindowHeightDefault); - m_control_scale = static_cast(m_height) / static_cast(WindowHeightDefault); - } - else { // 否则可能是偏正方形的屏幕,按宽度计算 - int scale_height = static_cast(WindowWidthDefault / cur_ratio); - m_scale_size = std::make_pair(WindowWidthDefault, scale_height); - m_control_scale = static_cast(m_width) / static_cast(WindowWidthDefault); - } - } + if (cur_ratio >= DefaultRatio // 说明是宽屏或默认16:9,按照高度计算缩放 + || std::fabs(cur_ratio - DefaultRatio) < DoubleDiff) { + int scale_width = static_cast(cur_ratio * WindowHeightDefault); + m_scale_size = std::make_pair(scale_width, WindowHeightDefault); + m_control_scale = static_cast(m_height) / static_cast(WindowHeightDefault); + } + else { // 否则可能是偏正方形的屏幕,按宽度计算 + int scale_height = static_cast(WindowWidthDefault / cur_ratio); + m_scale_size = std::make_pair(WindowWidthDefault, scale_height); + m_control_scale = static_cast(m_width) / static_cast(WindowWidthDefault); + } + } - /* get uuid (imei) */ - { - auto uuid_ret = call_command(cmd_replace(adb_cfg.uuid)); - if (!uuid_ret) { - json::value info = json::object{ - {"what", "ConnectFailed"}, - {"why", "Uuid command failed to exec"}, - {"details", json::object{}} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - return false; - } + /* get uuid (imei) */ + { + auto uuid_ret = call_command(cmd_replace(adb_cfg.uuid)); + if (!uuid_ret) { + json::value info = json::object{ + {"what", "ConnectFailed"}, + {"why", "Uuid command failed to exec"}, + {"details", json::object{}} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + return false; + } - auto& uuid_result = uuid_ret.value(); - std::string uuid_str( - std::make_move_iterator(uuid_result.begin()), - std::make_move_iterator(uuid_result.end())); - uuid_str.erase(std::remove(uuid_str.begin(), uuid_str.end(), ' '), uuid_str.end()); - m_uuid = std::move(uuid_str); + auto& uuid_result = uuid_ret.value(); + std::string uuid_str( + std::make_move_iterator(uuid_result.begin()), + std::make_move_iterator(uuid_result.end())); + uuid_str.erase(std::remove(uuid_str.begin(), uuid_str.end(), ' '), uuid_str.end()); + m_uuid = std::move(uuid_str); - json::value info = json::object{ - {"what", "UuidGetted"}, - {"why", ""}, - {"details", json::object { - { "uuid", m_uuid}, - {"address",address}, - }} - }; - m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); - } + json::value info = json::object{ + {"what", "UuidGetted"}, + {"why", ""}, + {"details", json::object { + { "uuid", m_uuid }, + { "adb", adb_path }, + { "address", address }, + { "config", config } + }} + }; + m_callback(AsstMsg::ConnectionInfo, info, m_callback_arg); + } - m_adb.click = cmd_replace(adb_cfg.click); - m_adb.swipe = cmd_replace(adb_cfg.swipe); - m_adb.screencap_raw_with_gzip = cmd_replace(adb_cfg.screencap_raw_with_gzip); - m_adb.screencap_encode = cmd_replace(adb_cfg.screencap_encode); - m_adb.release = cmd_replace(adb_cfg.release); + m_adb.click = cmd_replace(adb_cfg.click); + m_adb.swipe = cmd_replace(adb_cfg.swipe); + m_adb.screencap_raw_with_gzip = cmd_replace(adb_cfg.screencap_raw_with_gzip); + m_adb.screencap_encode = cmd_replace(adb_cfg.screencap_encode); + m_adb.release = cmd_replace(adb_cfg.release); - return true; + return true; } const std::string& asst::Controller::get_uuid() const { - return m_uuid; + return m_uuid; } cv::Mat asst::Controller::get_image() { - // 有些模拟器adb偶尔会莫名其妙截图失败,多试几次 - for (int i = 0; i != 20; ++i) { - if (screencap()) { - break; - } - } + // 有些模拟器adb偶尔会莫名其妙截图失败,多试几次 + for (int i = 0; i != 20; ++i) { + if (screencap()) { + break; + } + } - return get_resized_image(); + return get_resized_image(); } std::vector asst::Controller::get_image_encode() const { - cv::Mat img = get_resized_image(); - std::vector buf; - cv::imencode(".png", img, buf); + cv::Mat img = get_resized_image(); + std::vector buf; + cv::imencode(".png", img, buf); - return buf; + return buf; } diff --git a/src/MeoAssistant/Controller.h b/src/MeoAssistant/Controller.h index 4a72689b8e..dcdcd7f228 100644 --- a/src/MeoAssistant/Controller.h +++ b/src/MeoAssistant/Controller.h @@ -22,124 +22,124 @@ namespace asst { - class Controller - { - public: - Controller(AsstCallback callback, void* callback_arg); - Controller(const Controller&) = delete; - Controller(Controller&&) = delete; - ~Controller(); + class Controller + { + public: + Controller(AsstCallback callback, void* callback_arg); + Controller(const Controller&) = delete; + Controller(Controller&&) = delete; + ~Controller(); - bool connect(const std::string& adb_path, const std::string& address, const std::string& config); + bool connect(const std::string& adb_path, const std::string& address, const std::string& config); - const std::string& get_uuid() const; - cv::Mat get_image(); - std::vector get_image_encode() const; + const std::string& get_uuid() const; + cv::Mat get_image(); + std::vector get_image_encode() const; - // 点击和滑动都是异步执行,返回该任务的id - int click(const Point& p, bool block = true); - int click(const Rect& rect, bool block = true); - int click_without_scale(const Point& p, bool block = true); - int click_without_scale(const Rect& rect, bool block = true); + // 点击和滑动都是异步执行,返回该任务的id + int click(const Point& p, bool block = true); + int click(const Rect& rect, bool block = true); + int click_without_scale(const Point& p, bool block = true); + int click_without_scale(const Rect& rect, bool block = true); - constexpr static int SwipeExtraDelayDefault = 1000; - int swipe(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); - int swipe(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); - int swipe_without_scale(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); - int swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); + constexpr static int SwipeExtraDelayDefault = 1000; + int swipe(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); + int swipe(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); + int swipe_without_scale(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); + int swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); - void wait(unsigned id) const noexcept; + void wait(unsigned id) const noexcept; - // 异形屏矫正 - //Rect shaped_correct(const Rect& rect) const; - std::pair get_scale_size() const noexcept; + // 异形屏矫正 + //Rect shaped_correct(const Rect& rect) const; + std::pair get_scale_size() const noexcept; - Controller& operator=(const Controller&) = delete; - Controller& operator=(Controller&&) = delete; + Controller& operator=(const Controller&) = delete; + Controller& operator=(Controller&&) = delete; - private: - void pipe_working_proc(); - std::optional> call_command(const std::string& cmd); - int push_cmd(const std::string& cmd); + private: + void pipe_working_proc(); + std::optional> call_command(const std::string& cmd); + int push_cmd(const std::string& cmd); - using DecodeFunc = std::function&)>; - bool screencap(); - bool screencap(const std::string& cmd, DecodeFunc decode_func); - cv::Mat get_resized_image() const; + using DecodeFunc = std::function&)>; + bool screencap(); + bool screencap(const std::string& cmd, DecodeFunc decode_func); + cv::Mat get_resized_image() const; - Point rand_point_in_rect(const Rect& rect); + Point rand_point_in_rect(const Rect& rect); - void random_delay() const; + void random_delay() const; - // 转换data中所有的crlf为lf:有些模拟器自带的adb,exec-out输出的\n,会被替换成\r\n,导致解码错误,所以这里转一下回来(点名批评mumu) - static void convert_lf(std::vector& data); + // 转换data中所有的crlf为lf:有些模拟器自带的adb,exec-out输出的\n,会被替换成\r\n,导致解码错误,所以这里转一下回来(点名批评mumu) + static void convert_lf(std::vector& data); - AsstCallback m_callback; - void* m_callback_arg = nullptr; + AsstCallback m_callback; + void* m_callback_arg = nullptr; - std::minstd_rand m_rand_engine; + std::minstd_rand m_rand_engine; - constexpr static int PipeBuffSize = 4 * 1024 * 1024; // 管道缓冲区大小 - std::unique_ptr m_pipe_buffer = nullptr; + constexpr static int PipeBuffSize = 4 * 1024 * 1024; // 管道缓冲区大小 + std::unique_ptr m_pipe_buffer = nullptr; #ifdef _WIN32 - HANDLE m_pipe_read = nullptr; // 读管道句柄 - HANDLE m_pipe_write = nullptr; // 写管道句柄 - HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄 - HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄 - SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符 - STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息 + HANDLE m_pipe_read = nullptr; // 读管道句柄 + HANDLE m_pipe_write = nullptr; // 写管道句柄 + HANDLE m_pipe_child_read = nullptr; // 子进程的读管道句柄 + HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄 + SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符 + STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息 #else - constexpr static int PIPE_READ = 0; - constexpr static int PIPE_WRITE = 1; - int m_pipe_in[2] = { 0 }; - int m_pipe_out[2] = { 0 }; - int m_child = 0; + constexpr static int PIPE_READ = 0; + constexpr static int PIPE_WRITE = 1; + int m_pipe_in[2] = { 0 }; + int m_pipe_out[2] = { 0 }; + int m_child = 0; #endif - struct AdbProperty - { - /* command */ - std::string click; - std::string swipe; + struct AdbProperty + { + /* command */ + std::string click; + std::string swipe; - std::string screencap_raw_with_gzip; - std::string screencap_encode; - std::string release; + std::string screencap_raw_with_gzip; + std::string screencap_encode; + std::string release; - /* propertities */ - enum class ScreencapEndOfLine - { - UnknownYet, - CRLF, - LF, - CR - } screencap_end_of_line = ScreencapEndOfLine::UnknownYet; + /* propertities */ + enum class ScreencapEndOfLine + { + UnknownYet, + CRLF, + LF, + CR + } screencap_end_of_line = ScreencapEndOfLine::UnknownYet; - enum class ScreencapMethod - { - UnknownYet, - Default, - RawWithGzip, - Encode - } screencap_method = ScreencapMethod::UnknownYet; - } m_adb; + enum class ScreencapMethod + { + UnknownYet, + Default, + RawWithGzip, + Encode + } screencap_method = ScreencapMethod::UnknownYet; + } m_adb; - std::string m_uuid; - std::pair m_scale_size; - double m_control_scale = 1.0; - int m_width = 0; - int m_height = 0; + std::string m_uuid; + std::pair m_scale_size; + double m_control_scale = 1.0; + int m_width = 0; + int m_height = 0; - mutable std::shared_mutex m_image_mutex; - cv::Mat m_cache_image; + mutable std::shared_mutex m_image_mutex; + cv::Mat m_cache_image; - bool m_thread_exit = false; - //bool m_thread_idle = true; - std::mutex m_cmd_queue_mutex; - std::condition_variable m_cmd_condvar; - std::queue m_cmd_queue; - std::atomic m_completed_id = 0; - unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子 - std::thread m_cmd_thread; - }; + bool m_thread_exit = false; + //bool m_thread_idle = true; + std::mutex m_cmd_queue_mutex; + std::condition_variable m_cmd_condvar; + std::queue m_cmd_queue; + std::atomic m_completed_id = 0; + unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子 + std::thread m_cmd_thread; + }; } diff --git a/src/MeoAssistant/GeneralConfiger.h b/src/MeoAssistant/GeneralConfiger.h index df7f799d32..e658ba0d7b 100644 --- a/src/MeoAssistant/GeneralConfiger.h +++ b/src/MeoAssistant/GeneralConfiger.h @@ -11,91 +11,91 @@ namespace asst { - struct AipOcrCfg // 百度 OCR API 的配置 - { - bool enable = false; - bool accurate = false; // 是否使用高精度识别 - std::string client_id; - std::string client_secret; - }; + struct AipOcrCfg // 百度 OCR API 的配置 + { + bool enable = false; + bool accurate = false; // 是否使用高精度识别 + std::string client_id; + std::string client_secret; + }; - struct PenguinReportCfg // 企鹅物流数据汇报 的配置 - { - bool enable = false; - std::string cmd_format; // 命令格式 - std::string extra_param; // 额外参数 - std::string server; // 上传参数中"server"字段,"CN", "US", "JP" and "KR". - }; + struct PenguinReportCfg // 企鹅物流数据汇报 的配置 + { + bool enable = false; + std::string cmd_format; // 命令格式 + std::string extra_param; // 额外参数 + std::string server; // 上传参数中"server"字段,"CN", "US", "JP" and "KR". + }; - struct Options - { - int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗 - int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时 - int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时 - //bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下 - int adb_extra_swipe_dist = 0; // 额外的滑动距离:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 - int adb_extra_swipe_duration = -1; // 额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能 - PenguinReportCfg penguin_report; // 企鹅数据汇报:每次到结算界面,汇报掉落数据至企鹅数据 https://penguin-stats.cn/ - AipOcrCfg aip_ocr; // 百度 OCR API 的配置 - }; + struct Options + { + int task_delay = 0; // 任务间延时:越快操作越快,但会增加CPU消耗 + int control_delay_lower = 0; // 点击随机延时下限:每次点击操作会进行随机延时 + int control_delay_upper = 0; // 点击随机延时上限:每次点击操作会进行随机延时 + //bool print_window = false; // 截图功能:开启后每次结算界面会截图到screenshot目录下 + int adb_extra_swipe_dist = 0; // 额外的滑动距离:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来 + int adb_extra_swipe_duration = -1; // 额外的滑动持续时间:adb有bug,同样的参数,偶尔会划得非常远。额外做一个短程滑动,把之前的停下来。若小于0,则关闭额外滑动功能 + PenguinReportCfg penguin_report; // 企鹅数据汇报:每次到结算界面,汇报掉落数据至企鹅数据 https://penguin-stats.cn/ + AipOcrCfg aip_ocr; // 百度 OCR API 的配置 + }; - struct AdbCfg - { - /* format */ - std::string address_regex; - std::string display_format; + struct AdbCfg + { + /* format */ + std::string address_regex; + std::string display_format; - /* command */ - std::string devices; - std::string connect; - std::string display_id; - std::string uuid; - std::string click; - std::string swipe; - std::string display; - std::string screencap_raw_with_gzip; - std::string screencap_encode; - std::string release; - }; + /* command */ + std::string devices; + std::string connect; + std::string display_id; + std::string uuid; + std::string click; + std::string swipe; + std::string display; + std::string screencap_raw_with_gzip; + std::string screencap_encode; + std::string release; + }; - class GeneralConfiger : public AbstractConfiger - { - public: - virtual ~GeneralConfiger() = default; + class GeneralConfiger : public AbstractConfiger + { + public: + virtual ~GeneralConfiger() = default; - const std::string& get_version() const noexcept - { - return m_version; - } - const Options& get_options() const noexcept - { - return m_options; - } - Options& get_options() - { - return m_options; - } - std::optional get_adb_cfg(const std::string& name) const - { - if (auto iter = m_adb_cfg.find(name); - iter != m_adb_cfg.cend()) { - return iter->second; - } - else { - return std::nullopt; - } - } + const std::string& get_version() const noexcept + { + return m_version; + } + const Options& get_options() const noexcept + { + return m_options; + } + Options& get_options() + { + return m_options; + } + std::optional get_adb_cfg(const std::string& name) const + { + if (auto iter = m_adb_cfg.find(name); + iter != m_adb_cfg.cend()) { + return iter->second; + } + else { + return std::nullopt; + } + } - void set_options(Options opt) noexcept - { - m_options = std::move(opt); - } + void set_options(Options opt) noexcept + { + m_options = std::move(opt); + } - protected: - virtual bool parse(const json::value& json) override; + protected: + virtual bool parse(const json::value& json) override; - std::string m_version; - Options m_options; - std::unordered_map m_adb_cfg; - }; + std::string m_version; + Options m_options; + std::unordered_map m_adb_cfg; + }; } diff --git a/src/MeoAssistant/InfrastAbstractTask.cpp b/src/MeoAssistant/InfrastAbstractTask.cpp index 0d112fa6da..6cfa5c9020 100644 --- a/src/MeoAssistant/InfrastAbstractTask.cpp +++ b/src/MeoAssistant/InfrastAbstractTask.cpp @@ -13,190 +13,190 @@ asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_work_mode(infrast::WorkMode work_mode) noexcept { - m_work_mode = work_mode; - switch (work_mode) { - case infrast::WorkMode::Gentle: - m_work_mode_name = "Gentle"; - break; - case infrast::WorkMode::Aggressive: - m_work_mode_name = "Aggressive"; - break; - case infrast::WorkMode::Extreme: - m_work_mode_name = "Extreme"; - break; - default: - m_work_mode_name.clear(); - break; - } - return *this; + m_work_mode = work_mode; + switch (work_mode) { + case infrast::WorkMode::Gentle: + m_work_mode_name = "Gentle"; + break; + case infrast::WorkMode::Aggressive: + m_work_mode_name = "Aggressive"; + break; + case infrast::WorkMode::Extreme: + m_work_mode_name = "Extreme"; + break; + default: + m_work_mode_name.clear(); + break; + } + return *this; } asst::InfrastAbstractTask& asst::InfrastAbstractTask::set_mood_threshold(double mood_thres) noexcept { - m_mood_threshold = mood_thres; - return *this; + 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; + 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; + 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; } bool asst::InfrastAbstractTask::on_run_fails() { - LogTraceFunction; + LogTraceFunction; - ProcessTask return_task(*this, { "InfrastBegin" }); - return return_task.run(); + ProcessTask return_task(*this, { "InfrastBegin" }); + return return_task.run(); } bool asst::InfrastAbstractTask::enter_facility(int index) { - const auto image = m_ctrler->get_image(); + const auto image = m_ctrler->get_image(); - InfrastFacilityImageAnalyzer analyzer(image); - analyzer.set_to_be_analyzed({ facility_name() }); - if (!analyzer.analyze()) { - Log.trace("result is empty"); - return false; - } - Rect rect = analyzer.get_rect(facility_name(), index); - if (rect.empty()) { - Log.trace("facility index is out of range"); - return false; - } + InfrastFacilityImageAnalyzer analyzer(image); + analyzer.set_to_be_analyzed({ facility_name() }); + if (!analyzer.analyze()) { + Log.trace("result is empty"); + return false; + } + Rect rect = analyzer.get_rect(facility_name(), index); + if (rect.empty()) { + Log.trace("facility index is out of range"); + return false; + } - m_cur_facility_index = index; - callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("EnterFacility")); + m_cur_facility_index = index; + callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("EnterFacility")); - m_ctrler->click(rect); + m_ctrler->click(rect); - const auto enter_task_ptr = m_task_data->get("InfrastEnterFacility"); - sleep(enter_task_ptr->rear_delay); + const auto enter_task_ptr = m_task_data->get("InfrastEnterFacility"); + sleep(enter_task_ptr->rear_delay); - return true; + return true; } bool asst::InfrastAbstractTask::enter_oper_list_page() { - LogTraceFunction; + LogTraceFunction; - ProcessTask task(*this, { "InfrastEnterOperList", "InfrastStationedInfo" }); - return task.run(); + ProcessTask task(*this, { "InfrastEnterOperList", "InfrastStationedInfo" }); + return task.run(); } void asst::InfrastAbstractTask::async_swipe_of_operlist(bool reverse) { - LogTraceFunction; - static Rect begin_rect = m_task_data->get("InfrastOperListSwipeBegin")->specific_rect; - static Rect end_rect = m_task_data->get("InfrastOperListSwipeEnd")->specific_rect; - static int duration = m_task_data->get("InfrastOperListSwipeBegin")->pre_delay; + LogTraceFunction; + static Rect begin_rect = m_task_data->get("InfrastOperListSwipeBegin")->specific_rect; + static Rect end_rect = m_task_data->get("InfrastOperListSwipeEnd")->specific_rect; + static int duration = m_task_data->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); - } + 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); + } } void asst::InfrastAbstractTask::await_swipe() { - LogTraceFunction; - static int extra_delay = m_task_data->get("InfrastOperListSwipeBegin")->rear_delay; + LogTraceFunction; + static int extra_delay = m_task_data->get("InfrastOperListSwipeBegin")->rear_delay; - m_ctrler->wait(m_last_swipe_id); - sleep(extra_delay); + m_ctrler->wait(m_last_swipe_id); + sleep(extra_delay); } bool asst::InfrastAbstractTask::click_bottomleft_tab() { - LogTraceFunction; + LogTraceFunction; - ProcessTask task(*this, { "InfrastBottomLeftTab" }); - return task.run(); + ProcessTask task(*this, { "InfrastBottomLeftTab" }); + return task.run(); } bool asst::InfrastAbstractTask::click_clear_button() { - LogTraceFunction; + LogTraceFunction; - ProcessTask task(*this, { "InfrastClearButton" }); - return task.run(); + ProcessTask task(*this, { "InfrastClearButton" }); + return task.run(); } bool asst::InfrastAbstractTask::click_confirm_button() { - LogTraceFunction; + LogTraceFunction; - ProcessTask task(*this, { "InfrastDormConfirmButton" }); - return task.run(); + ProcessTask task(*this, { "InfrastDormConfirmButton" }); + return task.run(); } void asst::InfrastAbstractTask::swipe_of_operlist(bool reverse) { - async_swipe_of_operlist(reverse); - await_swipe(); + async_swipe_of_operlist(reverse); + await_swipe(); } void asst::InfrastAbstractTask::swipe_to_the_left_of_operlist(int loop_times) { - LogTraceFunction; - static Rect begin_rect = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; - static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; - static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; - static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; - static int cfg_loop_times = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->max_times; + LogTraceFunction; + static Rect begin_rect = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; + static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; + static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; + static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; + static int cfg_loop_times = m_task_data->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); + 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 = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; - static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; - static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; - static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; + LogTraceFunction; + static Rect begin_rect = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; + static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; + static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; + static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; - m_ctrler->swipe(end_rect, begin_rect, duration, true, extra_delay, false); + 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 = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; - static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; - static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; - static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; + LogTraceFunction; + static Rect begin_rect = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->specific_rect; + static Rect end_rect = m_task_data->get("InfrastOperListSwipeToTheLeftEnd")->specific_rect; + static int duration = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->pre_delay; + static int extra_delay = m_task_data->get("InfrastOperListSwipeToTheLeftBegin")->rear_delay; - m_ctrler->swipe(begin_rect, end_rect, duration, true, extra_delay, false); -} \ No newline at end of file + m_ctrler->swipe(begin_rect, end_rect, duration, true, extra_delay, false); +} diff --git a/src/MeoAssistant/InfrastAbstractTask.h b/src/MeoAssistant/InfrastAbstractTask.h index 69920779a1..b4cf1bbffd 100644 --- a/src/MeoAssistant/InfrastAbstractTask.h +++ b/src/MeoAssistant/InfrastAbstractTask.h @@ -5,43 +5,43 @@ namespace asst { - class InfrastAbstractTask : public AbstractTask - { - public: - using AbstractTask::AbstractTask; + class InfrastAbstractTask : public AbstractTask + { + public: + using AbstractTask::AbstractTask; - virtual ~InfrastAbstractTask() = default; - InfrastAbstractTask& set_work_mode(infrast::WorkMode work_mode) noexcept; - InfrastAbstractTask& set_mood_threshold(double mood_thres) noexcept; + virtual ~InfrastAbstractTask() = default; + InfrastAbstractTask& set_work_mode(infrast::WorkMode work_mode) noexcept; + InfrastAbstractTask& set_mood_threshold(double mood_thres) noexcept; - virtual json::value basic_info() const override; - virtual std::string facility_name() const; - virtual size_t max_num_of_facilities() const noexcept { return 1ULL; } - virtual size_t max_num_of_opers() const noexcept { return 1ULL; } + virtual json::value basic_info() const override; + virtual std::string facility_name() const; + virtual size_t max_num_of_facilities() const noexcept { return 1ULL; } + virtual size_t max_num_of_opers() const noexcept { return 1ULL; } - constexpr static int OperSelectRetryTimes = 3; - protected: - virtual bool on_run_fails() override; + constexpr static int OperSelectRetryTimes = 3; + protected: + virtual bool on_run_fails() override; - bool enter_facility(int index = 0); - bool enter_oper_list_page(); // 从刚点进设施的界面,到干员列表页 + bool enter_facility(int index = 0); + bool enter_oper_list_page(); // 从刚点进设施的界面,到干员列表页 - void swipe_to_the_left_of_operlist(int loop_times = 1); // 滑动到干员列表的最左侧 - void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧 - void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧 - void swipe_of_operlist(bool reverse = false); - void async_swipe_of_operlist(bool reverse = false); - void await_swipe(); + void swipe_to_the_left_of_operlist(int loop_times = 1); // 滑动到干员列表的最左侧 + void swipe_to_the_left_of_main_ui(); // 滑动基建的主界面到最左侧 + void swipe_to_the_right_of_main_ui(); // 滑动基建的主界面到最右侧 + void swipe_of_operlist(bool reverse = false); + void async_swipe_of_operlist(bool reverse = false); + void await_swipe(); - virtual bool click_bottomleft_tab(); // 点击进入设施后,左下角的tab(我也不知道这玩意该叫啥) - virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮 - virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮 + virtual bool click_bottomleft_tab(); // 点击进入设施后,左下角的tab(我也不知道这玩意该叫啥) + virtual bool click_clear_button(); // 点击干员选择页面的“清空选择”按钮 + virtual bool click_confirm_button(); // 点击干员选择页面的“确认”按钮 - int m_last_swipe_id = 0; - infrast::WorkMode m_work_mode = infrast::WorkMode::Gentle; - std::string m_work_mode_name = "Gentle"; - double m_mood_threshold = 0; - mutable std::string m_facility_name_cache; - int m_cur_facility_index = 0; - }; + int m_last_swipe_id = 0; + infrast::WorkMode m_work_mode = infrast::WorkMode::Gentle; + std::string m_work_mode_name = "Gentle"; + double m_mood_threshold = 0; + mutable std::string m_facility_name_cache; + int m_cur_facility_index = 0; + }; } diff --git a/src/MeoAssistant/InfrastOperImageAnalyzer.cpp b/src/MeoAssistant/InfrastOperImageAnalyzer.cpp index 8bf0b5bb20..e003f2da0d 100644 --- a/src/MeoAssistant/InfrastOperImageAnalyzer.cpp +++ b/src/MeoAssistant/InfrastOperImageAnalyzer.cpp @@ -8,417 +8,417 @@ bool asst::InfrastOperImageAnalyzer::analyze() { - m_result.clear(); - m_num_of_opers_with_skills = 0; + m_result.clear(); + m_num_of_opers_with_skills = 0; - if (m_to_be_calced & None) { - return true; - } + if (m_to_be_calced & None) { + return true; + } - if (m_to_be_calced & Smiley) { - oper_detect(); - } - if (m_to_be_calced & Mood) { - mood_analyze(); - } - if (m_to_be_calced & NameHash) { - name_hash_analyze(); - } - if (m_to_be_calced & FaceHash) { - face_hash_analyze(); - } - if (m_to_be_calced & Skill) { - skill_analyze(); - } - if (m_to_be_calced & Selected) { - selected_analyze(); - } - if (m_to_be_calced & Doing) { - doing_analyze(); - } + if (m_to_be_calced & Smiley) { + oper_detect(); + } + if (m_to_be_calced & Mood) { + mood_analyze(); + } + if (m_to_be_calced & NameHash) { + name_hash_analyze(); + } + if (m_to_be_calced & FaceHash) { + face_hash_analyze(); + } + if (m_to_be_calced & Skill) { + skill_analyze(); + } + if (m_to_be_calced & Selected) { + selected_analyze(); + } + if (m_to_be_calced & Doing) { + doing_analyze(); + } - return !m_result.empty(); + return !m_result.empty(); } void asst::InfrastOperImageAnalyzer::sort_by_loc() { - LogTraceFunction; + LogTraceFunction; - std::sort( - m_result.begin(), m_result.end(), - [](const infrast::Oper& lhs, const infrast::Oper& rhs) -> bool { - if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的,按y排序 - return lhs.rect.y < rhs.rect.y; - } - else { - return lhs.rect.x < rhs.rect.x; - } - }); + std::sort( + m_result.begin(), m_result.end(), + [](const infrast::Oper& lhs, const infrast::Oper& rhs) -> bool { + if (std::abs(lhs.rect.x - rhs.rect.x) < 5) { // x差距较小则理解为是同一排的,按y排序 + return lhs.rect.y < rhs.rect.y; + } + else { + return lhs.rect.x < rhs.rect.x; + } + }); } void asst::InfrastOperImageAnalyzer::sort_by_mood() { - LogTraceFunction; + LogTraceFunction; - std::sort( - m_result.begin(), m_result.end(), - [](const infrast::Oper& lhs, const infrast::Oper& rhs) -> bool { - // 先按心情排序,心情低的放前面 - if (std::fabs(lhs.mood_ratio - rhs.mood_ratio) > DoubleDiff) { - return lhs.mood_ratio < rhs.mood_ratio; - } - // 心情一样的就按位置排序,左边的放前面 - if (std::abs(lhs.rect.x - rhs.rect.x) > 5) { - return lhs.rect.x < rhs.rect.x; - } - else { - return lhs.rect.y < rhs.rect.y; - } - }); + std::sort( + m_result.begin(), m_result.end(), + [](const infrast::Oper& lhs, const infrast::Oper& rhs) -> bool { + // 先按心情排序,心情低的放前面 + if (std::fabs(lhs.mood_ratio - rhs.mood_ratio) > DoubleDiff) { + return lhs.mood_ratio < rhs.mood_ratio; + } + // 心情一样的就按位置排序,左边的放前面 + if (std::abs(lhs.rect.x - rhs.rect.x) > 5) { + return lhs.rect.x < rhs.rect.x; + } + else { + return lhs.rect.y < rhs.rect.y; + } + }); } void asst::InfrastOperImageAnalyzer::oper_detect() { - LogTraceFunction; + LogTraceFunction; - const Rect upper_roi = m_task_data->get("InfrastSkillsUpper")->roi; - const Rect lower_roi = m_task_data->get("InfrastSkillsLower")->roi; - const std::vector all_roi = { upper_roi, lower_roi }; + const Rect upper_roi = m_task_data->get("InfrastSkillsUpper")->roi; + const Rect lower_roi = m_task_data->get("InfrastSkillsLower")->roi; + const std::vector all_roi = { upper_roi, lower_roi }; - const Rect skill_rect_move = m_task_data->get("InfrastSkills")->rect_move; - const Rect hash_rect_move = m_task_data->get("InfrastOperNameHash")->rect_move; - const Rect prg_rect_move = m_task_data->get("InfrastOperMoodProgressBar")->roi; - const std::vector all_rect_move = { skill_rect_move, hash_rect_move, prg_rect_move }; + const Rect skill_rect_move = m_task_data->get("InfrastSkills")->rect_move; + const Rect hash_rect_move = m_task_data->get("InfrastOperNameHash")->rect_move; + const Rect prg_rect_move = m_task_data->get("InfrastOperMoodProgressBar")->roi; + const std::vector all_rect_move = { skill_rect_move, hash_rect_move, prg_rect_move }; - InfrastSmileyImageAnalyzer smiley_analyzer(m_image); + InfrastSmileyImageAnalyzer smiley_analyzer(m_image); - for (auto&& roi : all_roi) { - smiley_analyzer.set_roi(roi); - if (!smiley_analyzer.analyze()) { - continue; - } - for (const auto& smiley : smiley_analyzer.get_result()) { - auto&& [_type, smiley_rect] = smiley; - bool available = true; + for (auto&& roi : all_roi) { + smiley_analyzer.set_roi(roi); + if (!smiley_analyzer.analyze()) { + continue; + } + for (const auto& smiley : smiley_analyzer.get_result()) { + auto&& [_type, smiley_rect] = smiley; + bool available = true; - for (const Rect& rect_move : all_rect_move) { - Rect cor_rect = rect_move; - cor_rect.x += smiley_rect.x; - cor_rect.y += smiley_rect.y; - // 超过ROI边界了 - if (cor_rect.x + cor_rect.width > roi.x + roi.width || cor_rect.x < roi.x) { - available = false; - break; - } - } - if (!available) { - continue; - } + for (const Rect& rect_move : all_rect_move) { + Rect cor_rect = rect_move; + cor_rect.x += smiley_rect.x; + cor_rect.y += smiley_rect.y; + // 超过ROI边界了 + if (cor_rect.x + cor_rect.width > roi.x + roi.width || cor_rect.x < roi.x) { + available = false; + break; + } + } + if (!available) { + continue; + } #ifdef ASST_DEBUG - cv::rectangle(m_image_draw, utils::make_rect(smiley_rect), cv::Scalar(0, 0, 255), 2); + cv::rectangle(m_image_draw, utils::make_rect(smiley_rect), cv::Scalar(0, 0, 255), 2); #endif // ASST_DEBUG - infrast::Oper oper; - oper.smiley = smiley; - m_result.emplace_back(std::move(oper)); - } - } + infrast::Oper oper; + oper.smiley = smiley; + m_result.emplace_back(std::move(oper)); + } + } } void asst::InfrastOperImageAnalyzer::mood_analyze() { - LogTraceFunction; + LogTraceFunction; - const auto prg_task_ptr = std::dynamic_pointer_cast( - m_task_data->get("InfrastOperMoodProgressBar")); - uint8_t prg_lower_limit = static_cast(prg_task_ptr->templ_threshold); - int prg_diff_thres = static_cast(prg_task_ptr->special_threshold); - Rect rect_move = prg_task_ptr->rect_move; + const auto prg_task_ptr = std::dynamic_pointer_cast( + m_task_data->get("InfrastOperMoodProgressBar")); + uint8_t prg_lower_limit = static_cast(prg_task_ptr->templ_threshold); + int prg_diff_thres = static_cast(prg_task_ptr->special_threshold); + Rect rect_move = prg_task_ptr->rect_move; - for (auto&& oper : m_result) { - bool not_analyze = false; - switch (oper.smiley.type) { - case infrast::SmileyType::Distract: - oper.mood_ratio = 0; - not_analyze = true; - break; - case infrast::SmileyType::Rest: - oper.mood_ratio = 1.0; - not_analyze = true; - break; - case infrast::SmileyType::Work: - not_analyze = false; - break; - default: - // TODO 报错 - break; - } + for (auto&& oper : m_result) { + bool not_analyze = false; + switch (oper.smiley.type) { + case infrast::SmileyType::Distract: + oper.mood_ratio = 0; + not_analyze = true; + break; + case infrast::SmileyType::Rest: + oper.mood_ratio = 1.0; + not_analyze = true; + break; + case infrast::SmileyType::Work: + not_analyze = false; + break; + default: + // TODO 报错 + break; + } - if (not_analyze) { - continue; - } + if (not_analyze) { + continue; + } - Rect roi = rect_move; - roi.x += oper.smiley.rect.x; - roi.y += oper.smiley.rect.y; - cv::Mat prg_image = m_image(utils::make_rect(roi)); - cv::Mat prg_gray; - cv::cvtColor(prg_image, prg_gray, cv::COLOR_BGR2GRAY); + Rect roi = rect_move; + roi.x += oper.smiley.rect.x; + roi.y += oper.smiley.rect.y; + cv::Mat prg_image = m_image(utils::make_rect(roi)); + cv::Mat prg_gray; + cv::cvtColor(prg_image, prg_gray, cv::COLOR_BGR2GRAY); - int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度 - for (int i = 0; i != prg_gray.rows; ++i) { - int cur_white_length = 0; - cv::uint8_t left_value = prg_lower_limit; - for (int j = 0; j != prg_gray.cols; ++j) { - auto value = prg_gray.at(i, j); - // 当前点的颜色,需要大于最低阈值;且与相邻点的差值不能过大,否则就认为当前点不是进度条 - if (value >= prg_lower_limit && left_value < value + prg_diff_thres) { - left_value = value; - ++cur_white_length; - if (max_white_length < cur_white_length) { - max_white_length = cur_white_length; - } - } - else { - if (max_white_length < cur_white_length) { - max_white_length = cur_white_length; - } - left_value = prg_lower_limit; - cur_white_length = 0; - break; - } - } - } + int max_white_length = 0; // 最长横扫的白色长度,即作为进度条长度 + for (int i = 0; i != prg_gray.rows; ++i) { + int cur_white_length = 0; + cv::uint8_t left_value = prg_lower_limit; + for (int j = 0; j != prg_gray.cols; ++j) { + auto value = prg_gray.at(i, j); + // 当前点的颜色,需要大于最低阈值;且与相邻点的差值不能过大,否则就认为当前点不是进度条 + if (value >= prg_lower_limit && left_value < value + prg_diff_thres) { + left_value = value; + ++cur_white_length; + if (max_white_length < cur_white_length) { + max_white_length = cur_white_length; + } + } + else { + if (max_white_length < cur_white_length) { + max_white_length = cur_white_length; + } + left_value = prg_lower_limit; + cur_white_length = 0; + break; + } + } + } - // TODO:这里的进度条长度算的并不是特别准,属于能跑就行。有空再优化下 - double ratio = static_cast(max_white_length) / roi.width; - oper.mood_ratio = ratio; + // TODO:这里的进度条长度算的并不是特别准,属于能跑就行。有空再优化下 + double ratio = static_cast(max_white_length) / roi.width; + oper.mood_ratio = ratio; #ifdef ASST_DEBUG - cv::Point p1(roi.x, roi.y); - cv::Point p2(roi.x + max_white_length, roi.y); - cv::line(m_image_draw, p1, p2, cv::Scalar(0, 255, 0), 1); - cv::putText(m_image_draw, std::to_string(ratio), p1, 1, 1.0, cv::Scalar(0, 255, 0)); + cv::Point p1(roi.x, roi.y); + cv::Point p2(roi.x + max_white_length, roi.y); + cv::line(m_image_draw, p1, p2, cv::Scalar(0, 255, 0), 1); + cv::putText(m_image_draw, std::to_string(ratio), p1, 1, 1.0, cv::Scalar(0, 255, 0)); #endif // ASST_DEBUG - } + } } void asst::InfrastOperImageAnalyzer::face_hash_analyze() { - LogTraceFunction; + LogTraceFunction; - const Rect hash_rect_move = m_task_data->get("InfrastOperFaceHash")->rect_move; + const Rect hash_rect_move = m_task_data->get("InfrastOperFaceHash")->rect_move; - HashImageAnalyzer hash_analyzer(m_image); - for (auto&& oper : m_result) { - Rect roi = oper.smiley.rect.move(hash_rect_move); - hash_analyzer.set_roi(roi); - hash_analyzer.analyze(); - oper.face_hash = hash_analyzer.get_hash().front(); - } + HashImageAnalyzer hash_analyzer(m_image); + for (auto&& oper : m_result) { + Rect roi = oper.smiley.rect.move(hash_rect_move); + hash_analyzer.set_roi(roi); + hash_analyzer.analyze(); + oper.face_hash = hash_analyzer.get_hash().front(); + } } void asst::InfrastOperImageAnalyzer::name_hash_analyze() { - LogTraceFunction; + LogTraceFunction; - const auto task_ptr = std::dynamic_pointer_cast( - m_task_data->get("InfrastOperNameHash")); + const auto task_ptr = std::dynamic_pointer_cast( + m_task_data->get("InfrastOperNameHash")); - HashImageAnalyzer hash_analyzer(m_image); - hash_analyzer.set_mask_range(task_ptr->mask_range); - hash_analyzer.set_need_bound(true); - for (auto&& oper : m_result) { - Rect roi = oper.smiley.rect.move(task_ptr->rect_move); - hash_analyzer.set_roi(roi); - hash_analyzer.analyze(); - oper.name_hash = hash_analyzer.get_hash().front(); - } + HashImageAnalyzer hash_analyzer(m_image); + hash_analyzer.set_mask_range(task_ptr->mask_range); + hash_analyzer.set_need_bound(true); + for (auto&& oper : m_result) { + Rect roi = oper.smiley.rect.move(task_ptr->rect_move); + hash_analyzer.set_roi(roi); + hash_analyzer.analyze(); + oper.name_hash = hash_analyzer.get_hash().front(); + } } void asst::InfrastOperImageAnalyzer::skill_analyze() { - LogTraceFunction; + LogTraceFunction; - const auto task_ptr = std::dynamic_pointer_cast( - m_task_data->get("InfrastSkills")); - const auto bright_thres = task_ptr->special_threshold; + const auto task_ptr = std::dynamic_pointer_cast( + m_task_data->get("InfrastSkills")); + const auto bright_thres = task_ptr->special_threshold; - MatchImageAnalyzer skill_analyzer(m_image); - skill_analyzer.set_mask_range(task_ptr->mask_range); - skill_analyzer.set_threshold(task_ptr->templ_threshold); + MatchImageAnalyzer skill_analyzer(m_image); + skill_analyzer.set_mask_range(task_ptr->mask_range); + skill_analyzer.set_threshold(task_ptr->templ_threshold); - for (auto&& oper : m_result) { - Rect roi = task_ptr->rect_move; - roi.x += oper.smiley.rect.x; - roi.y += oper.smiley.rect.y; + for (auto&& oper : m_result) { + Rect roi = task_ptr->rect_move; + roi.x += oper.smiley.rect.x; + roi.y += oper.smiley.rect.y; - // roi里面是干员的所有技能(两个技能),这里先分别裁剪出来 - static int skill_width = roi.height; - static int spacing = (roi.width - roi.height * MaxNumOfSkills) / (MaxNumOfSkills - 1); - static cv::Mat mask; - if (mask.empty()) { - mask = cv::Mat(skill_width, skill_width, CV_8UC1, cv::Scalar(0)); - int radius = skill_width / 2; - cv::circle(mask, cv::Point(radius, radius), radius, cv::Scalar(255, 255, 255), -1); - } + // roi里面是干员的所有技能(两个技能),这里先分别裁剪出来 + static int skill_width = roi.height; + static int spacing = (roi.width - roi.height * MaxNumOfSkills) / (MaxNumOfSkills - 1); + static cv::Mat mask; + if (mask.empty()) { + mask = cv::Mat(skill_width, skill_width, CV_8UC1, cv::Scalar(0)); + int radius = skill_width / 2; + cv::circle(mask, cv::Point(radius, radius), radius, cv::Scalar(255, 255, 255), -1); + } - cv::Mat all_skills_img = m_image(utils::make_rect(roi)); - std::string log_str = "[ "; - for (int i = 0; i != MaxNumOfSkills; ++i) { - int x = i * skill_width + spacing * i; - Rect skill_rect_in_roi(x, 0, skill_width, roi.height); - cv::Mat skill_image = all_skills_img(utils::make_rect(skill_rect_in_roi)); + cv::Mat all_skills_img = m_image(utils::make_rect(roi)); + std::string log_str = "[ "; + for (int i = 0; i != MaxNumOfSkills; ++i) { + int x = i * skill_width + spacing * i; + Rect skill_rect_in_roi(x, 0, skill_width, roi.height); + cv::Mat skill_image = all_skills_img(utils::make_rect(skill_rect_in_roi)); - // 过滤掉亮度阈值不够的,说明是暗的技能(不是当前设施的技能) - cv::Mat skill_gray; - cv::cvtColor(skill_image, skill_gray, cv::COLOR_BGR2GRAY); - cv::Scalar avg = cv::mean(skill_gray, mask); - if (avg[0] < bright_thres) { - continue; - } - Rect skill_rect = skill_rect_in_roi; - skill_rect.x += roi.x; - skill_rect.y += roi.y; + // 过滤掉亮度阈值不够的,说明是暗的技能(不是当前设施的技能) + cv::Mat skill_gray; + cv::cvtColor(skill_image, skill_gray, cv::COLOR_BGR2GRAY); + cv::Scalar avg = cv::mean(skill_gray, mask); + if (avg[0] < bright_thres) { + continue; + } + Rect skill_rect = skill_rect_in_roi; + skill_rect.x += roi.x; + skill_rect.y += roi.y; #ifdef ASST_DEBUG - cv::rectangle(m_image_draw, utils::make_rect(skill_rect), cv::Scalar(0, 255, 0), 2); + cv::rectangle(m_image_draw, utils::make_rect(skill_rect), cv::Scalar(0, 255, 0), 2); #endif - // 针对裁剪出来的每个技能进行识别 - skill_analyzer.set_roi(skill_rect); + // 针对裁剪出来的每个技能进行识别 + skill_analyzer.set_roi(skill_rect); - std::vector> possible_skills; - // 逐个该设施内所有可能的技能,取得分最高的 - for (const auto& [id, skill] : Resrc.infrast().get_skills(m_facility)) { - skill_analyzer.set_templ_name(skill.templ_name); + std::vector> possible_skills; + // 逐个该设施内所有可能的技能,取得分最高的 + for (const auto& [id, skill] : Resrc.infrast().get_skills(m_facility)) { + skill_analyzer.set_templ_name(skill.templ_name); - if (!skill_analyzer.analyze()) { - continue; - } - possible_skills.emplace_back(std::make_pair(skill, skill_analyzer.get_result())); - } - if (possible_skills.empty()) { - Log.error("skill has no recognition result"); - continue; - } - // 可能的结果多于1个,只可能是同一个技能不同等级的结果 - // 例如:标准化a、标准化b,这两个模板非常像,然后分数都超过了阈值 - // 如果原图是标准化a,是不可能匹配上标准化b的模板的,因为b的模板左半边多了半个环 - // 相反如果原图是标准化b,却有可能匹配上标准化a的模板,因为a的模板右半边的环,b的原图中也有 - // 所以如果结果是同类型的,只需要取里面等级最高的那个即可 - infrast::Skill most_confident_skills; - if (possible_skills.size() == 1) { - most_confident_skills = possible_skills.front().first; - } - else if (possible_skills.size() > 1) { - // 匹配得分最高的id作为基准,排除有识别错误,其他的技能混进来了的情况 - // 即排除容器中,除了有同一个技能的不同等级,还有别的技能的情况 - auto max_iter = std::max_element( - possible_skills.begin(), possible_skills.end(), - [](const auto& lhs, const auto& rhs) -> bool { - return lhs.second.score < rhs.second.score; - }); - double base_score = max_iter->second.score; - std::string base_id = max_iter->first.id; - size_t level_pos = 0; - // 倒着找,第一个不是数字的。前面就是技能基础id名字,后面的数字就是技能等级 - for (size_t j = base_id.size() - 1; j != 0; --j) { - if (!std::isdigit(base_id.at(j))) { - level_pos = j + 1; - break; - } - } - base_id = base_id.substr(0, level_pos); - std::string max_level; - for (const auto& [skill, skill_mr] : possible_skills) { - // 得分差距过大的,直接忽略 - if (base_score - skill_mr.score > 0.05) { - continue; - } - if (size_t find_pos = skill.id.find(base_id); - find_pos != std::string::npos) { - std::string cur_skill_level = skill.id.substr(base_id.size()); - if (max_level.empty() || cur_skill_level > max_level) { - max_level = cur_skill_level; - most_confident_skills = skill; - } - } // 这里对应的else就是上述的其他技能混进来了的情况 - } - } - Log.trace(most_confident_skills.id, most_confident_skills.names.front()); - std::string skill_id = most_confident_skills.id; - log_str += skill_id + " - " + most_confident_skills.names.front() + "; "; + if (!skill_analyzer.analyze()) { + continue; + } + possible_skills.emplace_back(std::make_pair(skill, skill_analyzer.get_result())); + } + if (possible_skills.empty()) { + Log.error("skill has no recognition result"); + continue; + } + // 可能的结果多于1个,只可能是同一个技能不同等级的结果 + // 例如:标准化a、标准化b,这两个模板非常像,然后分数都超过了阈值 + // 如果原图是标准化a,是不可能匹配上标准化b的模板的,因为b的模板左半边多了半个环 + // 相反如果原图是标准化b,却有可能匹配上标准化a的模板,因为a的模板右半边的环,b的原图中也有 + // 所以如果结果是同类型的,只需要取里面等级最高的那个即可 + infrast::Skill most_confident_skills; + if (possible_skills.size() == 1) { + most_confident_skills = possible_skills.front().first; + } + else if (possible_skills.size() > 1) { + // 匹配得分最高的id作为基准,排除有识别错误,其他的技能混进来了的情况 + // 即排除容器中,除了有同一个技能的不同等级,还有别的技能的情况 + auto max_iter = std::max_element( + possible_skills.begin(), possible_skills.end(), + [](const auto& lhs, const auto& rhs) -> bool { + return lhs.second.score < rhs.second.score; + }); + double base_score = max_iter->second.score; + std::string base_id = max_iter->first.id; + size_t level_pos = 0; + // 倒着找,第一个不是数字的。前面就是技能基础id名字,后面的数字就是技能等级 + for (size_t j = base_id.size() - 1; j != 0; --j) { + if (!std::isdigit(base_id.at(j))) { + level_pos = j + 1; + break; + } + } + base_id = base_id.substr(0, level_pos); + std::string max_level; + for (const auto& [skill, skill_mr] : possible_skills) { + // 得分差距过大的,直接忽略 + if (base_score - skill_mr.score > 0.05) { + continue; + } + if (size_t find_pos = skill.id.find(base_id); + find_pos != std::string::npos) { + std::string cur_skill_level = skill.id.substr(base_id.size()); + if (max_level.empty() || cur_skill_level > max_level) { + max_level = cur_skill_level; + most_confident_skills = skill; + } + } // 这里对应的else就是上述的其他技能混进来了的情况 + } + } + Log.trace(most_confident_skills.id, most_confident_skills.names.front()); + std::string skill_id = most_confident_skills.id; + log_str += skill_id + " - " + most_confident_skills.names.front() + "; "; #ifdef ASST_DEBUG - cv::Mat skill_mat = m_image(utils::make_rect(skill_rect)); + cv::Mat skill_mat = m_image(utils::make_rect(skill_rect)); #endif - oper.skills.emplace(std::move(most_confident_skills)); - } - if (!oper.skills.empty()) { - ++m_num_of_opers_with_skills; - } - Log.trace(log_str, "]"); - } + oper.skills.emplace(std::move(most_confident_skills)); + } + if (!oper.skills.empty()) { + ++m_num_of_opers_with_skills; + } + Log.trace(log_str, "]"); + } } void asst::InfrastOperImageAnalyzer::selected_analyze() { - LogTraceFunction; + LogTraceFunction; - const auto selected_task_ptr = std::dynamic_pointer_cast( - m_task_data->get("InfrastOperSelected")); - Rect rect_move = selected_task_ptr->rect_move; + const auto selected_task_ptr = std::dynamic_pointer_cast( + m_task_data->get("InfrastOperSelected")); + Rect rect_move = selected_task_ptr->rect_move; - for (auto&& oper : m_result) { - Rect selected_rect = rect_move; - selected_rect.x += oper.smiley.rect.x; - selected_rect.y += oper.smiley.rect.y; + for (auto&& oper : m_result) { + Rect selected_rect = rect_move; + selected_rect.x += oper.smiley.rect.x; + selected_rect.y += oper.smiley.rect.y; - cv::Mat roi = m_image(utils::make_rect(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; + cv::Mat roi = m_image(utils::make_rect(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); - oper.selected = count >= selected_task_ptr->templ_threshold; - oper.rect = selected_rect; // 先凑合用( - } + 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); + oper.selected = count >= selected_task_ptr->templ_threshold; + oper.rect = selected_rect; // 先凑合用( + } } void asst::InfrastOperImageAnalyzer::doing_analyze() { - LogTraceFunction; + LogTraceFunction; - const auto working_task_ptr = m_task_data->get("InfrastOperOnShift"); - Rect rect_move = working_task_ptr->rect_move; + const auto working_task_ptr = m_task_data->get("InfrastOperOnShift"); + Rect rect_move = working_task_ptr->rect_move; - MatchImageAnalyzer working_analyzer(m_image); - working_analyzer.set_task_info(working_task_ptr); + MatchImageAnalyzer working_analyzer(m_image); + working_analyzer.set_task_info(working_task_ptr); - for (auto&& oper : m_result) { - Rect working_rect = rect_move; - working_rect.x += oper.smiley.rect.x; - working_rect.y += oper.smiley.rect.y; - working_analyzer.set_roi(working_rect); - if (working_analyzer.analyze()) { - oper.doing = infrast::Doing::Working; + for (auto&& oper : m_result) { + Rect working_rect = rect_move; + working_rect.x += oper.smiley.rect.x; + working_rect.y += oper.smiley.rect.y; + working_analyzer.set_roi(working_rect); + if (working_analyzer.analyze()) { + oper.doing = infrast::Doing::Working; #ifdef ASST_DEBUG - cv::putText(m_image_draw, "Working", cv::Point(working_rect.x, working_rect.y), 1, 1, cv::Scalar(0, 0, 255), 2); + cv::putText(m_image_draw, "Working", cv::Point(working_rect.x, working_rect.y), 1, 1, cv::Scalar(0, 0, 255), 2); #endif - } - // TODO: infrast::Doing::Resting的识别 - } -} \ No newline at end of file + } + // TODO: infrast::Doing::Resting的识别 + } +} diff --git a/src/MeoAssistant/InfrastReceptionTask.cpp b/src/MeoAssistant/InfrastReceptionTask.cpp index 36dc7fe863..ad8e77847b 100644 --- a/src/MeoAssistant/InfrastReceptionTask.cpp +++ b/src/MeoAssistant/InfrastReceptionTask.cpp @@ -10,179 +10,179 @@ bool asst::InfrastReceptionTask::_run() { - m_all_available_opers.clear(); + m_all_available_opers.clear(); - swipe_to_the_right_of_main_ui(); - enter_facility(); - click_bottomleft_tab(); + swipe_to_the_right_of_main_ui(); + enter_facility(); + click_bottomleft_tab(); - close_end_of_clue_exchange(); + close_end_of_clue_exchange(); - get_clue(); - if (need_exit()) { - return false; - } - use_clue(); - if (need_exit()) { - return false; - } - click_return_button(); - click_bottomleft_tab(); - if (need_exit()) { - return false; - } - shift(); + get_clue(); + if (need_exit()) { + return false; + } + use_clue(); + if (need_exit()) { + return false; + } + click_return_button(); + click_bottomleft_tab(); + if (need_exit()) { + return false; + } + shift(); - return true; + return true; } bool asst::InfrastReceptionTask::close_end_of_clue_exchange() { - ProcessTask task_temp(*this, { "ReceptionFlag", "EndOfClueExchange" }); - return task_temp.run(); + ProcessTask task_temp(*this, { "ReceptionFlag", "EndOfClueExchange" }); + return task_temp.run(); } bool asst::InfrastReceptionTask::get_clue() { - ProcessTask task_temp(*this, { "InfrastClueSelfNew", "InfrastClueFriendNew", "InfrastClueSelfMaybeFull", "ReceptionFlag" }); - return task_temp.run(); + ProcessTask task_temp(*this, { "InfrastClueSelfNew", "InfrastClueFriendNew", "InfrastClueSelfMaybeFull", "ReceptionFlag" }); + return task_temp.run(); } bool asst::InfrastReceptionTask::use_clue() { - LogTraceFunction; - const static std::string clue_vacancy = "InfrastClueVacancy"; - const static std::vector clue_suffix = { - "No1", "No2", "No3", "No4", "No5", "No6", "No7" - }; + LogTraceFunction; + const static std::string clue_vacancy = "InfrastClueVacancy"; + const static std::vector clue_suffix = { + "No1", "No2", "No3", "No4", "No5", "No6", "No7" + }; - proc_clue_vacancy(); - if (unlock_clue_exchange()) { - proc_clue_vacancy(); - } + proc_clue_vacancy(); + if (unlock_clue_exchange()) { + proc_clue_vacancy(); + } - cv::Mat image = m_ctrler->get_image(); + cv::Mat image = m_ctrler->get_image(); - // 所有的空位分析一次,看看还缺哪些线索 - InfrastClueVacancyImageAnalyzer vacancy_analyzer(image); - vacancy_analyzer.set_to_be_analyzed(clue_suffix); - if (!vacancy_analyzer.analyze()) { - } - const auto& vacancy = vacancy_analyzer.get_vacancy(); - for (auto&& [id, _] : vacancy) { - Log.trace("InfrastReceptionTask | Vacancy", id); - } + // 所有的空位分析一次,看看还缺哪些线索 + InfrastClueVacancyImageAnalyzer vacancy_analyzer(image); + vacancy_analyzer.set_to_be_analyzed(clue_suffix); + if (!vacancy_analyzer.analyze()) { + } + const auto& vacancy = vacancy_analyzer.get_vacancy(); + for (auto&& [id, _] : vacancy) { + 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); + std::string product; + if (vacancy.size() == 1) { + product = vacancy.begin()->first; + } + else { + product = "General"; + } + Log.trace("InfrastReceptionTask | product", product); + set_product(product); - return true; + return true; } bool asst::InfrastReceptionTask::proc_clue_vacancy() { - LogTraceFunction; - const static std::string clue_vacancy = "InfrastClueVacancy"; - const static std::vector clue_suffix = { - "No1", "No2", "No3", "No4", "No5", "No6", "No7" - }; + LogTraceFunction; + const static std::string clue_vacancy = "InfrastClueVacancy"; + const static std::vector clue_suffix = { + "No1", "No2", "No3", "No4", "No5", "No6", "No7" + }; - cv::Mat image = m_ctrler->get_image(); - 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; - m_ctrler->click(vacancy); - int delay = m_task_data->get(clue_vacancy + clue)->rear_delay; - sleep(delay); + cv::Mat image = m_ctrler->get_image(); + 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; + m_ctrler->click(vacancy); + int delay = m_task_data->get(clue_vacancy + clue)->rear_delay; + sleep(delay); - // 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的) - //swipe_to_the_bottom_of_clue_list_on_the_right(); - image = m_ctrler->get_image(); - InfrastClueImageAnalyzer clue_analyzer(image); - if (!clue_analyzer.analyze()) { - continue; - } - m_ctrler->click(clue_analyzer.get_result().back().first); - sleep(delay); - } - return true; + // 识别右边列表中的线索,然后用最底下的那个(一般都是剩余时间最短的) + //swipe_to_the_bottom_of_clue_list_on_the_right(); + image = m_ctrler->get_image(); + InfrastClueImageAnalyzer clue_analyzer(image); + if (!clue_analyzer.analyze()) { + continue; + } + m_ctrler->click(clue_analyzer.get_result().back().first); + sleep(delay); + } + return true; } bool asst::InfrastReceptionTask::unlock_clue_exchange() { - ProcessTask task(*this, { "UnlockClues" }); - task.set_retry_times(2); - return task.run(); + ProcessTask task(*this, { "UnlockClues" }); + task.set_retry_times(2); + return task.run(); } bool asst::InfrastReceptionTask::send_clue() { - ProcessTask task(*this, { "SendClues" }); - return task.run(); + ProcessTask task(*this, { "SendClues" }); + return task.run(); } bool asst::InfrastReceptionTask::shift() { - LogTraceFunction; - const auto image = m_ctrler->get_image(); - MatchImageAnalyzer add_analyzer(image); + LogTraceFunction; + const auto image = m_ctrler->get_image(); + MatchImageAnalyzer add_analyzer(image); - const auto raw_task_ptr = m_task_data->get("InfrastAddOperator" + facility_name() + m_work_mode_name); - switch (raw_task_ptr->algorithm) { - case AlgorithmType::JustReturn: - if (raw_task_ptr->action == ProcessTaskAction::ClickRect) { - m_ctrler->click(raw_task_ptr->specific_rect); - } - break; - case AlgorithmType::MatchTemplate: { - add_analyzer.set_task_info(raw_task_ptr); + const auto raw_task_ptr = m_task_data->get("InfrastAddOperator" + facility_name() + m_work_mode_name); + switch (raw_task_ptr->algorithm) { + case AlgorithmType::JustReturn: + if (raw_task_ptr->action == ProcessTaskAction::ClickRect) { + m_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; - } - m_ctrler->click(add_analyzer.get_result().rect); - } break; - default: - break; - } - sleep(raw_task_ptr->rear_delay); + if (!add_analyzer.analyze()) { + return true; + } + m_ctrler->click(add_analyzer.get_result().rect); + } break; + default: + break; + } + sleep(raw_task_ptr->rear_delay); - for (int i = 0; i <= OperSelectRetryTimes; ++i) { - if (need_exit()) { - return false; - } - click_clear_button(); + for (int i = 0; i <= OperSelectRetryTimes; ++i) { + if (need_exit()) { + return false; + } + click_clear_button(); - if (!opers_detect_with_swipe()) { - return false; - } - swipe_to_the_left_of_operlist(); + if (!opers_detect_with_swipe()) { + return false; + } + swipe_to_the_left_of_operlist(); - optimal_calc(); - bool ret = opers_choose(); - if (!ret) { - m_all_available_opers.clear(); - swipe_to_the_left_of_operlist(); - continue; - } - break; - } - click_confirm_button(); - return true; -} \ No newline at end of file + optimal_calc(); + bool ret = opers_choose(); + if (!ret) { + m_all_available_opers.clear(); + swipe_to_the_left_of_operlist(); + continue; + } + break; + } + click_confirm_button(); + return true; +} diff --git a/src/MeoAssistant/Logger.hpp b/src/MeoAssistant/Logger.hpp index a034038fa1..8a69a66b02 100644 --- a/src/MeoAssistant/Logger.hpp +++ b/src/MeoAssistant/Logger.hpp @@ -149,7 +149,7 @@ namespace asst #ifdef ASST_DEBUG stream_args(std::cout, buff, std::forward(args)...); #endif - } + } template inline void stream_args(std::ostream& os, T&& first, Args&&... rest) @@ -181,7 +181,7 @@ namespace asst #else os << first << " "; // Don't fucking use gbk in linux #endif - } + } }; inline static std::string m_dirname; diff --git a/src/MeoAssistant/OcrImageAnalyzer.cpp b/src/MeoAssistant/OcrImageAnalyzer.cpp index 56153523dd..bad6657704 100644 --- a/src/MeoAssistant/OcrImageAnalyzer.cpp +++ b/src/MeoAssistant/OcrImageAnalyzer.cpp @@ -9,155 +9,155 @@ bool asst::OcrImageAnalyzer::analyze() { - m_ocr_result.clear(); + m_ocr_result.clear(); - std::vector preds_vec; + std::vector preds_vec; - if (!m_replace.empty()) { - TextRectProc text_replace = [&](TextRect& tr) -> bool { - for (const auto& [regex, new_str] : m_replace) { - tr.text = std::regex_replace(tr.text, std::regex(regex), new_str); - } - return true; - }; - preds_vec.emplace_back(text_replace); - } + if (!m_replace.empty()) { + TextRectProc text_replace = [&](TextRect& tr) -> bool { + for (const auto& [regex, new_str] : m_replace) { + tr.text = std::regex_replace(tr.text, std::regex(regex), new_str); + } + return true; + }; + preds_vec.emplace_back(text_replace); + } - if (!m_required.empty()) { - if (m_full_match) { - TextRectProc required_match = [&](TextRect& tr) -> bool { - return std::find(m_required.cbegin(), m_required.cend(), tr.text) != m_required.cend(); - }; - preds_vec.emplace_back(required_match); - } - else { - TextRectProc required_search = [&](TextRect& tr) -> bool { - auto is_sub = [&tr](const std::string& str) -> bool { - if (tr.text.find(str) == std::string::npos) { - return false; - } - tr.text = str; - return true; - }; - return std::find_if(m_required.cbegin(), m_required.cend(), is_sub) != m_required.cend(); - }; - preds_vec.emplace_back(required_search); - } - } + if (!m_required.empty()) { + if (m_full_match) { + TextRectProc required_match = [&](TextRect& tr) -> bool { + return std::find(m_required.cbegin(), m_required.cend(), tr.text) != m_required.cend(); + }; + preds_vec.emplace_back(required_match); + } + else { + TextRectProc required_search = [&](TextRect& tr) -> bool { + auto is_sub = [&tr](const std::string& str) -> bool { + if (tr.text.find(str) == std::string::npos) { + return false; + } + tr.text = str; + return true; + }; + return std::find_if(m_required.cbegin(), m_required.cend(), is_sub) != m_required.cend(); + }; + preds_vec.emplace_back(required_search); + } + } - preds_vec.emplace_back(m_pred); + preds_vec.emplace_back(m_pred); - TextRectProc all_pred = [&](TextRect& tr) -> bool { - for (const auto& pred : preds_vec) { - if (pred && !pred(tr)) { - return false; - } - } - return true; - }; + TextRectProc all_pred = [&](TextRect& tr) -> bool { + for (const auto& pred : preds_vec) { + if (pred && !pred(tr)) { + return false; + } + } + return true; + }; - auto& aip_ocr = Resrc.cfg().get_options().aip_ocr; - bool need_local = true; - if (aip_ocr.enable) { - if (aip_ocr.accurate) { - need_local = !AipOcr::get_instance().request_ocr_accurate(m_image, m_ocr_result, all_pred); - } - else { - need_local = !AipOcr::get_instance().request_ocr_general(m_image, m_ocr_result, all_pred); - } - } - if (need_local) { - m_ocr_result = Resrc.ocr().recognize(m_image, m_roi, all_pred, m_without_det); - } - //log.trace("ocr result", m_ocr_result); - return !m_ocr_result.empty(); + auto& aip_ocr = Resrc.cfg().get_options().aip_ocr; + bool need_local = true; + if (aip_ocr.enable) { + if (aip_ocr.accurate) { + need_local = !AipOcr::get_instance().request_ocr_accurate(m_image, m_ocr_result, all_pred); + } + else { + need_local = !AipOcr::get_instance().request_ocr_general(m_image, m_ocr_result, all_pred); + } + } + if (need_local) { + m_ocr_result = Resrc.ocr().recognize(m_image, m_roi, all_pred, m_without_det); + } + //log.trace("ocr result", m_ocr_result); + return !m_ocr_result.empty(); } void asst::OcrImageAnalyzer::set_use_cache(bool is_use) noexcept { - m_use_cache = is_use; + m_use_cache = is_use; } void asst::OcrImageAnalyzer::set_required(std::vector required, bool full_match) noexcept { - m_required = std::move(required); - m_full_match = full_match; + m_required = std::move(required); + m_full_match = full_match; } void asst::OcrImageAnalyzer::set_replace(std::unordered_map replace) noexcept { - m_replace = std::move(replace); + m_replace = std::move(replace); } void asst::OcrImageAnalyzer::set_task_info(OcrTaskInfo task_info) noexcept { - m_required = std::move(task_info.text); - m_full_match = task_info.full_match; - m_replace = std::move(task_info.replace_map); - m_use_cache = task_info.cache; + m_required = std::move(task_info.text); + m_full_match = task_info.full_match; + m_replace = std::move(task_info.replace_map); + m_use_cache = task_info.cache; - if (m_use_cache && !m_region_of_appeared.empty()) { - m_roi = m_region_of_appeared; - m_without_det = true; - } - else { - set_roi(task_info.roi); - m_without_det = false; - } + if (m_use_cache && !m_region_of_appeared.empty()) { + m_roi = m_region_of_appeared; + m_without_det = true; + } + else { + set_roi(task_info.roi); + m_without_det = false; + } } void asst::OcrImageAnalyzer::set_task_info(std::shared_ptr task_ptr) { - set_task_info(*std::dynamic_pointer_cast(task_ptr)); + set_task_info(*std::dynamic_pointer_cast(task_ptr)); } void asst::OcrImageAnalyzer::set_region_of_appeared(Rect region) noexcept { - m_region_of_appeared = std::move(region); - if (m_use_cache && !m_region_of_appeared.empty()) { - m_roi = m_region_of_appeared; - m_without_det = true; - } + m_region_of_appeared = std::move(region); + if (m_use_cache && !m_region_of_appeared.empty()) { + m_roi = m_region_of_appeared; + m_without_det = true; + } } void asst::OcrImageAnalyzer::set_pred(const TextRectProc& pred) { - m_pred = pred; + m_pred = pred; } const std::vector& asst::OcrImageAnalyzer::get_result() const noexcept { - return m_ocr_result; + return m_ocr_result; } void asst::OcrImageAnalyzer::sort_result() { - // 按位置排个序 - std::sort(m_ocr_result.begin(), m_ocr_result.end(), - [](const TextRect& lhs, const TextRect& rhs) -> bool { - if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序 - return lhs.rect.x < rhs.rect.x; - } - else { - return lhs.rect.y < rhs.rect.y; - } - } - ); + // 按位置排个序 + std::sort(m_ocr_result.begin(), m_ocr_result.end(), + [](const TextRect& lhs, const TextRect& rhs) -> bool { + if (std::abs(lhs.rect.y - rhs.rect.y) < 5) { // y差距较小则理解为是同一排的,按x排序 + return lhs.rect.x < rhs.rect.x; + } + else { + return lhs.rect.y < rhs.rect.y; + } + } + ); } void asst::OcrImageAnalyzer::sort_result_by_required() { - if (m_required.empty()) { - return; - } + if (m_required.empty()) { + return; + } - std::unordered_map m_req_cache; - for (size_t i = 0; i != m_required.size(); ++i) { - m_req_cache.emplace(m_required.at(i), i + 1); - } + std::unordered_map m_req_cache; + for (size_t i = 0; i != m_required.size(); ++i) { + m_req_cache.emplace(m_required.at(i), i + 1); + } - std::sort(m_ocr_result.begin(), m_ocr_result.end(), - [&m_req_cache](const auto& lhs, const auto& rhs) -> bool { - return m_req_cache[lhs.text] < m_req_cache[rhs.text]; - }); -} \ No newline at end of file + std::sort(m_ocr_result.begin(), m_ocr_result.end(), + [&m_req_cache](const auto& lhs, const auto& rhs) -> bool { + return m_req_cache[lhs.text] < m_req_cache[rhs.text]; + }); +} diff --git a/src/MeoAssistant/OcrPack.h b/src/MeoAssistant/OcrPack.h index 2458c27a0a..ec2c20ae06 100644 --- a/src/MeoAssistant/OcrPack.h +++ b/src/MeoAssistant/OcrPack.h @@ -7,24 +7,24 @@ namespace cv { - class Mat; + class Mat; } struct paddle_ocr_t; namespace asst { - class OcrPack final : public AbstractResource - { - public: - using AbstractResource::AbstractResource; - virtual ~OcrPack(); + class OcrPack final : public AbstractResource + { + public: + using AbstractResource::AbstractResource; + virtual ~OcrPack(); - virtual bool load(const std::string& dir) override; + virtual bool load(const std::string& dir) override; - std::vector recognize(const cv::Mat image, const TextRectProc& pred = nullptr, bool without_det = false); - std::vector recognize(const cv::Mat image, const Rect& roi, const TextRectProc& pred = nullptr, bool without_det = false); + std::vector recognize(const cv::Mat image, const TextRectProc& pred = nullptr, bool without_det = false); + std::vector recognize(const cv::Mat image, const Rect& roi, const TextRectProc& pred = nullptr, bool without_det = false); - private: - paddle_ocr_t* m_ocr = nullptr; - }; + private: + paddle_ocr_t* m_ocr = nullptr; + }; } diff --git a/src/MeoAssistant/ProcessTask.cpp b/src/MeoAssistant/ProcessTask.cpp index 27328d36fd..6a9d8eb8a1 100644 --- a/src/MeoAssistant/ProcessTask.cpp +++ b/src/MeoAssistant/ProcessTask.cpp @@ -15,226 +15,226 @@ using namespace asst; asst::ProcessTask::ProcessTask(const AbstractTask& abs, std::vector tasks_name) - : AbstractTask(abs), - m_raw_tasks_name(std::move(tasks_name)) + : AbstractTask(abs), + m_raw_tasks_name(std::move(tasks_name)) { - m_basic_info_cache = json::value(); + m_basic_info_cache = json::value(); } asst::ProcessTask::ProcessTask(AbstractTask&& abs, std::vector tasks_name) noexcept - : AbstractTask(std::move(abs)), - m_raw_tasks_name(std::move(tasks_name)) + : AbstractTask(std::move(abs)), + m_raw_tasks_name(std::move(tasks_name)) { - m_basic_info_cache = json::value(); + m_basic_info_cache = json::value(); } bool asst::ProcessTask::run() { - m_cur_tasks_name = m_raw_tasks_name; - for (m_cur_retry = 0; m_cur_retry <= m_retry_times; ++m_cur_retry) { - if (_run()) { - return true; - } - if (need_exit()) { - return false; - } - int delay = Resrc.cfg().get_options().task_delay; - sleep(delay); + m_cur_tasks_name = m_raw_tasks_name; + for (m_cur_retry = 0; m_cur_retry <= m_retry_times; ++m_cur_retry) { + if (_run()) { + return true; + } + if (need_exit()) { + return false; + } + int delay = Resrc.cfg().get_options().task_delay; + sleep(delay); - if (!on_run_fails()) { - return false; - } - } - callback(AsstMsg::SubTaskError, basic_info()); - return false; + if (!on_run_fails()) { + return false; + } + } + callback(AsstMsg::SubTaskError, basic_info()); + return false; } asst::ProcessTask& asst::ProcessTask::set_tasks(std::vector tasks_name) noexcept { - m_raw_tasks_name = std::move(tasks_name); - return *this; + m_raw_tasks_name = std::move(tasks_name); + return *this; } ProcessTask& asst::ProcessTask::set_times_limit(std::string name, int limit) { - m_times_limit.emplace(std::move(name), limit); - return *this; + m_times_limit.emplace(std::move(name), limit); + return *this; } ProcessTask& asst::ProcessTask::set_rear_delay(std::string name, int delay) { - m_rear_delay.emplace(std::move(name), delay); - return *this; + m_rear_delay.emplace(std::move(name), delay); + return *this; } bool ProcessTask::_run() { - LogTraceFunction; + LogTraceFunction; - auto& task_delay = Resrc.cfg().get_options().task_delay; - while (!m_cur_tasks_name.empty()) { - if (need_exit()) { - return false; - } - json::value info = basic_info(); - info["details"] = json::object{ - {"to_be_recognized", json::array(m_cur_tasks_name)}, - {"cur_retry", m_cur_retry}, - {"retry_times", m_retry_times} - }; - Log.info(info.to_string()); + auto& task_delay = Resrc.cfg().get_options().task_delay; + while (!m_cur_tasks_name.empty()) { + if (need_exit()) { + return false; + } + json::value info = basic_info(); + info["details"] = json::object{ + {"to_be_recognized", json::array(m_cur_tasks_name)}, + {"cur_retry", m_cur_retry}, + {"retry_times", m_retry_times} + }; + Log.info(info.to_string()); - Rect rect; - std::shared_ptr cur_task_ptr = nullptr; + Rect rect; + std::shared_ptr cur_task_ptr = nullptr; - // 如果第一个任务是JustReturn的,那就没必要再截图并计算了 - if (auto front_task_ptr = m_task_data->get(m_cur_tasks_name.front()); - front_task_ptr->algorithm == AlgorithmType::JustReturn) { - cur_task_ptr = front_task_ptr; - } - else { - const auto image = m_ctrler->get_image(); - ProcessTaskImageAnalyzer analyzer(image, m_cur_tasks_name); - analyzer.set_status(m_status); - if (!analyzer.analyze()) { - return false; - } - cur_task_ptr = analyzer.get_result(); - rect = analyzer.get_rect(); - } - if (need_exit()) { - return false; - } - std::string cur_name = cur_task_ptr->name; + // 如果第一个任务是JustReturn的,那就没必要再截图并计算了 + if (auto front_task_ptr = m_task_data->get(m_cur_tasks_name.front()); + front_task_ptr->algorithm == AlgorithmType::JustReturn) { + cur_task_ptr = front_task_ptr; + } + else { + const auto image = m_ctrler->get_image(); + ProcessTaskImageAnalyzer analyzer(image, m_cur_tasks_name); + analyzer.set_status(m_status); + if (!analyzer.analyze()) { + return false; + } + cur_task_ptr = analyzer.get_result(); + rect = analyzer.get_rect(); + } + if (need_exit()) { + return false; + } + std::string cur_name = cur_task_ptr->name; - const auto& res_move = cur_task_ptr->rect_move; - if (!res_move.empty()) { - rect.x += res_move.x; - rect.y += res_move.y; - rect.width = res_move.width; - rect.height = res_move.height; - } + const auto& res_move = cur_task_ptr->rect_move; + if (!res_move.empty()) { + rect.x += res_move.x; + rect.y += res_move.y; + rect.width = res_move.width; + rect.height = res_move.height; + } - int& exec_times = m_exec_times[cur_name]; + int& exec_times = m_exec_times[cur_name]; - int max_times = cur_task_ptr->max_times; - if (auto iter = m_times_limit.find(cur_name); - iter != m_times_limit.cend()) { - max_times = iter->second; - } + int max_times = cur_task_ptr->max_times; + if (auto iter = m_times_limit.find(cur_name); + iter != m_times_limit.cend()) { + max_times = iter->second; + } - if (exec_times >= max_times) { - Log.info("exec times exceeds the limit", info.to_string()); - m_cur_tasks_name = cur_task_ptr->exceeded_next; - sleep(task_delay); - continue; - } + if (exec_times >= max_times) { + Log.info("exec times exceeds the limit", info.to_string()); + m_cur_tasks_name = cur_task_ptr->exceeded_next; + sleep(task_delay); + continue; + } - m_cur_retry = 0; - ++exec_times; + m_cur_retry = 0; + ++exec_times; - info["details"] = json::object{ - { "task", cur_name }, - { "action", static_cast(cur_task_ptr->action) }, - { "exec_times", exec_times }, - { "max_times", max_times }, - { "algorithm", static_cast(cur_task_ptr->algorithm) } - }; + info["details"] = json::object{ + { "task", cur_name }, + { "action", static_cast(cur_task_ptr->action) }, + { "exec_times", exec_times }, + { "max_times", max_times }, + { "algorithm", static_cast(cur_task_ptr->algorithm) } + }; - callback(AsstMsg::SubTaskStart, info); + callback(AsstMsg::SubTaskStart, info); - // 前置固定延时 - if (!sleep(cur_task_ptr->pre_delay)) { - return false; - } + // 前置固定延时 + if (!sleep(cur_task_ptr->pre_delay)) { + return false; + } - bool need_stop = false; - switch (cur_task_ptr->action) { - case ProcessTaskAction::ClickRect: - rect = cur_task_ptr->specific_rect; - [[fallthrough]]; - case ProcessTaskAction::ClickSelf: - exec_click_task(rect); - break; - case ProcessTaskAction::ClickRand: { - auto&& [width, height] = m_ctrler->get_scale_size(); - const Rect full_rect(0, 0, width, height); - exec_click_task(full_rect); - } break; - case ProcessTaskAction::SwipeToTheLeft: - case ProcessTaskAction::SwipeToTheRight: - exec_swipe_task(cur_task_ptr->action); - break; - case ProcessTaskAction::DoNothing: - break; - case ProcessTaskAction::Stop: - Log.info("stop action", info.to_string()); - need_stop = true; - break; - default: - break; - } + bool need_stop = false; + switch (cur_task_ptr->action) { + case ProcessTaskAction::ClickRect: + rect = cur_task_ptr->specific_rect; + [[fallthrough]]; + case ProcessTaskAction::ClickSelf: + exec_click_task(rect); + break; + case ProcessTaskAction::ClickRand: { + auto&& [width, height] = m_ctrler->get_scale_size(); + const Rect full_rect(0, 0, width, height); + exec_click_task(full_rect); + } break; + case ProcessTaskAction::SwipeToTheLeft: + case ProcessTaskAction::SwipeToTheRight: + exec_swipe_task(cur_task_ptr->action); + break; + case ProcessTaskAction::DoNothing: + break; + case ProcessTaskAction::Stop: + Log.info("stop action", info.to_string()); + need_stop = true; + break; + default: + break; + } - m_status->set_data("Last" + cur_name, time(nullptr)); + m_status->set_data("Last" + cur_name, time(nullptr)); - // 减少其他任务的执行次数 - // 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效 - // 所以要给蓝色开始行动的次数减一 - for (const std::string& reduce : cur_task_ptr->reduce_other_times) { - --m_exec_times[reduce]; - } + // 减少其他任务的执行次数 + // 例如,进入吃理智药的界面了,相当于上一次点蓝色开始行动没生效 + // 所以要给蓝色开始行动的次数减一 + for (const std::string& reduce : cur_task_ptr->reduce_other_times) { + --m_exec_times[reduce]; + } - // 后置固定延时 - int rear_delay = cur_task_ptr->rear_delay; - if (auto iter = m_rear_delay.find(cur_name); - iter != m_rear_delay.cend()) { - rear_delay = iter->second; - } - if (!sleep(rear_delay)) { - return false; - } + // 后置固定延时 + int rear_delay = cur_task_ptr->rear_delay; + if (auto iter = m_rear_delay.find(cur_name); + iter != m_rear_delay.cend()) { + rear_delay = iter->second; + } + if (!sleep(rear_delay)) { + return false; + } - callback(AsstMsg::SubTaskCompleted, info); + callback(AsstMsg::SubTaskCompleted, info); - if (need_stop) { - return true; - } - m_cur_tasks_name = cur_task_ptr->next; - sleep(task_delay); - } + if (need_stop) { + return true; + } + m_cur_tasks_name = cur_task_ptr->next; + sleep(task_delay); + } - return true; + return true; } void ProcessTask::exec_click_task(const Rect& matched_rect) { - m_ctrler->click(matched_rect); + m_ctrler->click(matched_rect); } void asst::ProcessTask::exec_swipe_task(ProcessTaskAction action) { - const auto&& [width, height] = m_ctrler->get_scale_size(); + const auto&& [width, height] = m_ctrler->get_scale_size(); - const static Rect right_rect( - static_cast(width * 0.8), - static_cast(height * 0.4), - static_cast(width * 0.1), - static_cast(height * 0.2)); + const static Rect right_rect( + static_cast(width * 0.8), + static_cast(height * 0.4), + static_cast(width * 0.1), + static_cast(height * 0.2)); - const static Rect left_rect( - static_cast(width * 0.1), - static_cast(height * 0.4), - static_cast(width * 0.1), - static_cast(height * 0.2)); + const static Rect left_rect( + static_cast(width * 0.1), + static_cast(height * 0.4), + static_cast(width * 0.1), + static_cast(height * 0.2)); - switch (action) { - case asst::ProcessTaskAction::SwipeToTheLeft: - m_ctrler->swipe(left_rect, right_rect); - break; - case asst::ProcessTaskAction::SwipeToTheRight: - m_ctrler->swipe(right_rect, left_rect); - break; - default: // 走不到这里,TODO 报个错 - break; - } -} \ No newline at end of file + switch (action) { + case asst::ProcessTaskAction::SwipeToTheLeft: + m_ctrler->swipe(left_rect, right_rect); + break; + case asst::ProcessTaskAction::SwipeToTheRight: + m_ctrler->swipe(right_rect, left_rect); + break; + default: // 走不到这里,TODO 报个错 + break; + } +} diff --git a/src/MeoAssistant/ProcessTask.h b/src/MeoAssistant/ProcessTask.h index 71e221a9c5..9c761c085e 100644 --- a/src/MeoAssistant/ProcessTask.h +++ b/src/MeoAssistant/ProcessTask.h @@ -5,32 +5,32 @@ namespace asst { - // 流程任务,按照配置文件里的设置的流程运行 - class ProcessTask : public AbstractTask - { - public: - using AbstractTask::AbstractTask; - ProcessTask(const AbstractTask& abs, std::vector tasks_name); - ProcessTask(AbstractTask&& abs, std::vector tasks_name) noexcept; + // 流程任务,按照配置文件里的设置的流程运行 + class ProcessTask : public AbstractTask + { + public: + using AbstractTask::AbstractTask; + ProcessTask(const AbstractTask& abs, std::vector tasks_name); + ProcessTask(AbstractTask&& abs, std::vector tasks_name) noexcept; - virtual ~ProcessTask() = default; + virtual ~ProcessTask() = default; - virtual bool run() override; + virtual bool run() override; - ProcessTask& set_tasks(std::vector tasks_name) noexcept; - ProcessTask& set_times_limit(std::string name, int limit); - ProcessTask& set_rear_delay(std::string name, int delay); + ProcessTask& set_tasks(std::vector tasks_name) noexcept; + ProcessTask& set_times_limit(std::string name, int limit); + ProcessTask& set_rear_delay(std::string name, int delay); - protected: - virtual bool _run() override; + protected: + virtual bool _run() override; - void exec_click_task(const Rect& matched_rect); - void exec_swipe_task(ProcessTaskAction action); + void exec_click_task(const Rect& matched_rect); + void exec_swipe_task(ProcessTaskAction action); - std::vector m_raw_tasks_name; - std::vector m_cur_tasks_name; - std::unordered_map m_rear_delay; - std::unordered_map m_times_limit; - std::unordered_map m_exec_times; - }; + std::vector m_raw_tasks_name; + std::vector m_cur_tasks_name; + std::unordered_map m_rear_delay; + std::unordered_map m_times_limit; + std::unordered_map m_exec_times; + }; } diff --git a/src/MeoAssistant/ProcessTaskImageAnalyzer.cpp b/src/MeoAssistant/ProcessTaskImageAnalyzer.cpp index cafbbb57c8..983ccc5e58 100644 --- a/src/MeoAssistant/ProcessTaskImageAnalyzer.cpp +++ b/src/MeoAssistant/ProcessTaskImageAnalyzer.cpp @@ -10,137 +10,137 @@ #include "RuntimeStatus.h" asst::ProcessTaskImageAnalyzer::ProcessTaskImageAnalyzer(const cv::Mat image, std::vector tasks_name) - : AbstractImageAnalyzer(image), - m_tasks_name(std::move(tasks_name)) + : AbstractImageAnalyzer(image), + m_tasks_name(std::move(tasks_name)) { - ; + ; } asst::ProcessTaskImageAnalyzer::~ProcessTaskImageAnalyzer() = default; bool asst::ProcessTaskImageAnalyzer::match_analyze(std::shared_ptr task_ptr) { - if (!m_match_analyzer) { - m_match_analyzer = std::make_unique(m_image); - } - const auto match_task_ptr = std::dynamic_pointer_cast(task_ptr); - m_match_analyzer->set_task_info(match_task_ptr); - auto region_opt = m_status->get_region(match_task_ptr->name); - if (region_opt) { - m_match_analyzer->set_region_of_appeared(region_opt.value()); - } + if (!m_match_analyzer) { + m_match_analyzer = std::make_unique(m_image); + } + const auto match_task_ptr = std::dynamic_pointer_cast(task_ptr); + m_match_analyzer->set_task_info(match_task_ptr); + auto region_opt = m_status->get_region(match_task_ptr->name); + if (region_opt) { + m_match_analyzer->set_region_of_appeared(region_opt.value()); + } - if (m_match_analyzer->analyze()) { - m_result = match_task_ptr; - m_result_rect = m_match_analyzer->get_result().rect; - m_status->set_region(match_task_ptr->name, m_result_rect); - return true; - } - return false; + if (m_match_analyzer->analyze()) { + m_result = match_task_ptr; + m_result_rect = m_match_analyzer->get_result().rect; + m_status->set_region(match_task_ptr->name, m_result_rect); + return true; + } + return false; } bool asst::ProcessTaskImageAnalyzer::ocr_analyze(std::shared_ptr task_ptr) { - std::shared_ptr ocr_task_ptr = std::dynamic_pointer_cast(task_ptr); + std::shared_ptr ocr_task_ptr = std::dynamic_pointer_cast(task_ptr); - // 先尝试从缓存的结果里找 - //for (const TextRect& tr : m_ocr_cache) { - // TextRect temp = tr; - // for (const auto& [regex, new_str] : ocr_task_ptr->replace_map) { - // temp.text = std::regex_replace(temp.text, std::regex(regex), new_str); - // } - // for (const auto& text : ocr_task_ptr->text) { - // bool flag = false; - // if (ocr_task_ptr->full_match) { - // if (temp.text == text) { - // flag = true; - // } - // } - // else if (temp.text.find(text) != std::string::npos) { - // flag = true; - // } - // // 即使找到了,也得在ROI里才行,不然过滤掉 - // if (flag && ocr_task_ptr->roi.include(tr.rect)) { - // m_result = ocr_task_ptr; - // m_result_rect = tr.rect; - // ocr_task_ptr->region_of_appeared = m_result_rect; - // Log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found in cache", tr.to_string()); - // return true; - // } - // } - //} - if (!m_ocr_analyzer) { - m_ocr_analyzer = std::make_unique(m_image); - } - m_ocr_analyzer->set_task_info(ocr_task_ptr); - auto region_opt = m_status->get_region(ocr_task_ptr->name); - if (region_opt) { - m_ocr_analyzer->set_region_of_appeared(region_opt.value()); - } + // 先尝试从缓存的结果里找 + //for (const TextRect& tr : m_ocr_cache) { + // TextRect temp = tr; + // for (const auto& [regex, new_str] : ocr_task_ptr->replace_map) { + // temp.text = std::regex_replace(temp.text, std::regex(regex), new_str); + // } + // for (const auto& text : ocr_task_ptr->text) { + // bool flag = false; + // if (ocr_task_ptr->full_match) { + // if (temp.text == text) { + // flag = true; + // } + // } + // else if (temp.text.find(text) != std::string::npos) { + // flag = true; + // } + // // 即使找到了,也得在ROI里才行,不然过滤掉 + // if (flag && ocr_task_ptr->roi.include(tr.rect)) { + // m_result = ocr_task_ptr; + // m_result_rect = tr.rect; + // ocr_task_ptr->region_of_appeared = m_result_rect; + // Log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found in cache", tr.to_string()); + // return true; + // } + // } + //} + if (!m_ocr_analyzer) { + m_ocr_analyzer = std::make_unique(m_image); + } + m_ocr_analyzer->set_task_info(ocr_task_ptr); + auto region_opt = m_status->get_region(ocr_task_ptr->name); + if (region_opt) { + m_ocr_analyzer->set_region_of_appeared(region_opt.value()); + } - bool ret = m_ocr_analyzer->analyze(); + bool ret = m_ocr_analyzer->analyze(); - if (ret) { - m_ocr_analyzer->sort_result_by_required(); - const auto& ocr_result = m_ocr_analyzer->get_result(); - auto& res = ocr_result.front(); - m_result = ocr_task_ptr; - m_result_rect = res.rect; - m_status->set_region(ocr_task_ptr->name, m_result_rect); - //m_ocr_cache.insert(m_ocr_cache.end(), ocr_result.begin(), ocr_result.end()); - Log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found", res.to_string()); - } - return ret; + if (ret) { + m_ocr_analyzer->sort_result_by_required(); + const auto& ocr_result = m_ocr_analyzer->get_result(); + auto& res = ocr_result.front(); + m_result = ocr_task_ptr; + m_result_rect = res.rect; + m_status->set_region(ocr_task_ptr->name, m_result_rect); + //m_ocr_cache.insert(m_ocr_cache.end(), ocr_result.begin(), ocr_result.end()); + Log.trace("ProcessTaskImageAnalyzer::ocr_analyze | found", res.to_string()); + } + return ret; } void asst::ProcessTaskImageAnalyzer::reset() noexcept { - //m_ocr_cache.clear(); - m_ocr_analyzer = nullptr; - m_match_analyzer = nullptr; + //m_ocr_cache.clear(); + m_ocr_analyzer = nullptr; + m_match_analyzer = nullptr; } bool asst::ProcessTaskImageAnalyzer::analyze() { - m_result = nullptr; - m_result_rect = Rect(); + m_result = nullptr; + m_result_rect = Rect(); - for (const std::string& task_name : m_tasks_name) { - auto task_ptr = m_task_data->get(task_name); + for (const std::string& task_name : m_tasks_name) { + auto task_ptr = m_task_data->get(task_name); - switch (task_ptr->algorithm) { - case AlgorithmType::JustReturn: - m_result = task_ptr; - return true; - case AlgorithmType::MatchTemplate: - if (match_analyze(task_ptr)) { - return true; - } - break; - case AlgorithmType::OcrDetect: - if (ocr_analyze(task_ptr)) { - return true; - } - break; - default: - break; - } - } - return false; + switch (task_ptr->algorithm) { + case AlgorithmType::JustReturn: + m_result = task_ptr; + return true; + case AlgorithmType::MatchTemplate: + if (match_analyze(task_ptr)) { + return true; + } + break; + case AlgorithmType::OcrDetect: + if (ocr_analyze(task_ptr)) { + return true; + } + break; + default: + break; + } + } + return false; } void asst::ProcessTaskImageAnalyzer::set_image(const cv::Mat image) { - AbstractImageAnalyzer::set_image(image); - reset(); + AbstractImageAnalyzer::set_image(image); + reset(); } void asst::ProcessTaskImageAnalyzer::set_tasks(std::vector tasks_name) { - m_tasks_name = std::move(tasks_name); + m_tasks_name = std::move(tasks_name); } void asst::ProcessTaskImageAnalyzer::set_status(std::shared_ptr status) noexcept { - m_status = status; -} \ No newline at end of file + m_status = status; +} diff --git a/src/MeoAssistant/ProcessTaskImageAnalyzer.h b/src/MeoAssistant/ProcessTaskImageAnalyzer.h index b4bf8ed397..283a4db755 100644 --- a/src/MeoAssistant/ProcessTaskImageAnalyzer.h +++ b/src/MeoAssistant/ProcessTaskImageAnalyzer.h @@ -9,56 +9,56 @@ namespace asst { - class OcrImageAnalyzer; - class MatchImageAnalyzer; - class RuntimeStatus; + class OcrImageAnalyzer; + class MatchImageAnalyzer; + class RuntimeStatus; - class ProcessTaskImageAnalyzer final : public AbstractImageAnalyzer - { - public: - using AbstractImageAnalyzer::AbstractImageAnalyzer; - ProcessTaskImageAnalyzer(const cv::Mat image, const Rect& roi) = delete; - ProcessTaskImageAnalyzer(const cv::Mat image, std::vector tasks_name); - virtual ~ProcessTaskImageAnalyzer(); + class ProcessTaskImageAnalyzer final : public AbstractImageAnalyzer + { + public: + using AbstractImageAnalyzer::AbstractImageAnalyzer; + ProcessTaskImageAnalyzer(const cv::Mat image, const Rect& roi) = delete; + ProcessTaskImageAnalyzer(const cv::Mat image, std::vector tasks_name); + virtual ~ProcessTaskImageAnalyzer(); - virtual bool analyze() override; - void set_image(const cv::Mat image); + virtual bool analyze() override; + void set_image(const cv::Mat image); - void set_tasks(std::vector tasks_name); - void set_status(std::shared_ptr status) noexcept; + void set_tasks(std::vector tasks_name); + void set_status(std::shared_ptr status) noexcept; - std::shared_ptr get_result() const noexcept - { - return m_result; - } - const Rect& get_rect() const noexcept - { - return m_result_rect; - } + std::shared_ptr get_result() const noexcept + { + return m_result; + } + const Rect& get_rect() const noexcept + { + return m_result_rect; + } - ProcessTaskImageAnalyzer& operator=(const ProcessTaskImageAnalyzer&) = delete; - ProcessTaskImageAnalyzer& operator=(ProcessTaskImageAnalyzer&&) = delete; + ProcessTaskImageAnalyzer& operator=(const ProcessTaskImageAnalyzer&) = delete; + ProcessTaskImageAnalyzer& operator=(ProcessTaskImageAnalyzer&&) = delete; - private: - // 该分析器不支持外部设置ROI - virtual void set_roi(const Rect& roi) noexcept override - { - AbstractImageAnalyzer::set_roi(roi); - } - virtual void set_image(const cv::Mat image, const Rect& roi) - { - AbstractImageAnalyzer::set_image(image, roi); - } - bool match_analyze(std::shared_ptr task_ptr); - bool ocr_analyze(std::shared_ptr task_ptr); - void reset() noexcept; + private: + // 该分析器不支持外部设置ROI + virtual void set_roi(const Rect& roi) noexcept override + { + AbstractImageAnalyzer::set_roi(roi); + } + virtual void set_image(const cv::Mat image, const Rect& roi) + { + AbstractImageAnalyzer::set_image(image, roi); + } + bool match_analyze(std::shared_ptr task_ptr); + bool ocr_analyze(std::shared_ptr task_ptr); + void reset() noexcept; - std::unique_ptr m_ocr_analyzer; - std::unique_ptr m_match_analyzer; - std::vector m_tasks_name; - std::shared_ptr m_result = nullptr; - std::shared_ptr m_status = nullptr; - Rect m_result_rect; - //std::vector m_ocr_cache; - }; + std::unique_ptr m_ocr_analyzer; + std::unique_ptr m_match_analyzer; + std::vector m_tasks_name; + std::shared_ptr m_result = nullptr; + std::shared_ptr m_status = nullptr; + Rect m_result_rect; + //std::vector m_ocr_cache; + }; } diff --git a/src/MeoAssistant/RecruitConfiger.h b/src/MeoAssistant/RecruitConfiger.h index 99dd8e34fc..bf37e01360 100644 --- a/src/MeoAssistant/RecruitConfiger.h +++ b/src/MeoAssistant/RecruitConfiger.h @@ -10,49 +10,49 @@ namespace asst { - // 干员信息,公开招募相关 - struct RecruitOperInfo - { - std::string name; - std::string type; - int level = 0; - std::string sex; - std::unordered_set tags; - bool hidden = false; - std::string name_en; - }; + // 干员信息,公开招募相关 + struct RecruitOperInfo + { + std::string name; + std::string type; + int level = 0; + std::string sex; + std::unordered_set tags; + bool hidden = false; + std::string name_en; + }; - // 公开招募的干员组合 - struct RecruitCombs - { - std::vector tags; - std::vector opers; - int max_level = 0; - int min_level = 0; - double avg_level = 0; - }; + // 公开招募的干员组合 + struct RecruitCombs + { + std::vector tags; + std::vector opers; + int max_level = 0; + int min_level = 0; + double avg_level = 0; + }; - class RecruitConfiger : public AbstractConfiger - { - public: - virtual ~RecruitConfiger() = default; - constexpr static int CorrectNumberOfTags = 5; + class RecruitConfiger : public AbstractConfiger + { + public: + virtual ~RecruitConfiger() = default; + constexpr static int CorrectNumberOfTags = 5; - const std::unordered_set& get_all_tags() const noexcept - { - return m_all_tags; - } - const std::vector& get_all_opers() const noexcept - { - return m_all_opers; - } + const std::unordered_set& get_all_tags() const noexcept + { + return m_all_tags; + } + const std::vector& get_all_opers() const noexcept + { + return m_all_opers; + } - protected: - virtual bool parse(const json::value& json) override; + protected: + virtual bool parse(const json::value& json) override; - std::unordered_set m_all_tags; - std::unordered_set m_all_types; + std::unordered_set m_all_tags; + std::unordered_set m_all_types; - std::vector m_all_opers; - }; + std::vector m_all_opers; + }; } diff --git a/src/MeoAssistant/RecruitImageAnalyzer.h b/src/MeoAssistant/RecruitImageAnalyzer.h index bdde90dd29..1207c216cd 100644 --- a/src/MeoAssistant/RecruitImageAnalyzer.h +++ b/src/MeoAssistant/RecruitImageAnalyzer.h @@ -5,50 +5,50 @@ namespace asst { - class RecruitImageAnalyzer final : public AbstractImageAnalyzer - { - public: - using AbstractImageAnalyzer::AbstractImageAnalyzer; - RecruitImageAnalyzer(const cv::Mat image, const Rect& roi) = delete; - virtual ~RecruitImageAnalyzer() = default; + class RecruitImageAnalyzer final : public AbstractImageAnalyzer + { + public: + using AbstractImageAnalyzer::AbstractImageAnalyzer; + RecruitImageAnalyzer(const cv::Mat image, const Rect& roi) = delete; + virtual ~RecruitImageAnalyzer() = default; - virtual bool analyze() override; + virtual bool analyze() override; - const std::vector& get_tags_result() const noexcept - { - return m_tags_result; - } - const std::vector& get_set_time_rect() const noexcept - { - return m_set_time_rect; - } - Rect get_confirm_rect() const noexcept - { - return m_confirm_rect; - } - Rect get_refresh_rect() const noexcept - { - return m_refresh_rect; - } + const std::vector& get_tags_result() const noexcept + { + return m_tags_result; + } + const std::vector& get_set_time_rect() const noexcept + { + return m_set_time_rect; + } + Rect get_confirm_rect() const noexcept + { + return m_confirm_rect; + } + Rect get_refresh_rect() const noexcept + { + return m_refresh_rect; + } - private: - // 该分析器不支持外部设置ROI - virtual void set_roi(const Rect& roi) noexcept override - { - AbstractImageAnalyzer::set_roi(roi); - } - virtual void set_image(const cv::Mat image, const Rect& roi) - { - AbstractImageAnalyzer::set_image(image, roi); - } - bool tags_analyze(); - bool time_analyze(); - bool confirm_analyze(); - bool refresh_analyze(); + private: + // 该分析器不支持外部设置ROI + virtual void set_roi(const Rect& roi) noexcept override + { + AbstractImageAnalyzer::set_roi(roi); + } + virtual void set_image(const cv::Mat image, const Rect& roi) + { + AbstractImageAnalyzer::set_image(image, roi); + } + bool tags_analyze(); + bool time_analyze(); + bool confirm_analyze(); + bool refresh_analyze(); - std::vector m_tags_result; - std::vector m_set_time_rect; - Rect m_confirm_rect; - Rect m_refresh_rect; - }; + std::vector m_tags_result; + std::vector m_set_time_rect; + Rect m_confirm_rect; + Rect m_refresh_rect; + }; } diff --git a/src/MeoAssistant/RecruitTask.cpp b/src/MeoAssistant/RecruitTask.cpp index 50ea4df009..3feb9bc288 100644 --- a/src/MeoAssistant/RecruitTask.cpp +++ b/src/MeoAssistant/RecruitTask.cpp @@ -44,7 +44,7 @@ bool RecruitTask::_run() } json::value info = basic_info(); info["what"] = "RecruitTagsDetected"; - info["details"] = json::object { + info["details"] = json::object{ { "tags", json::array(all_tags_json_vector) } }; callback(AsstMsg::SubTaskExtraInfo, info); @@ -56,7 +56,7 @@ bool RecruitTask::_run() auto special_iter = std::find_first_of(SpecialTags.cbegin(), SpecialTags.cend(), all_tags_name.cbegin(), all_tags_name.cend()); if (special_iter != SpecialTags.cend()) { info["what"] = "RecruitSpecialTag"; - info["details"] = json::object { + info["details"] = json::object{ { "tag", *special_iter } }; callback(AsstMsg::SubTaskExtraInfo, info); diff --git a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h index e73b91aca1..9c7cd00da9 100644 --- a/src/MeoAssistant/RoguelikeBattleTaskPlugin.h +++ b/src/MeoAssistant/RoguelikeBattleTaskPlugin.h @@ -7,43 +7,43 @@ namespace asst { - class RoguelikeBattleTaskPlugin : public AbstractTaskPlugin - { - using Loc = asst::TilePack::BuildableType; - using Role = asst::BattleImageAnalyzer::Role; - public: - using AbstractTaskPlugin::AbstractTaskPlugin; - virtual ~RoguelikeBattleTaskPlugin() = default; + class RoguelikeBattleTaskPlugin : public AbstractTaskPlugin + { + using Loc = asst::TilePack::BuildableType; + using Role = asst::BattleImageAnalyzer::Role; + public: + using AbstractTaskPlugin::AbstractTaskPlugin; + virtual ~RoguelikeBattleTaskPlugin() = default; - virtual bool verify(AsstMsg msg, const json::value& details) const override; + virtual bool verify(AsstMsg msg, const json::value& details) const override; - void set_stage_name(std::string stage); + void set_stage_name(std::string stage); - protected: - virtual bool _run() override; + protected: + virtual bool _run() override; - bool get_stage_info(); - bool auto_battle(); - bool speed_up(); - bool use_skill(const Rect& rect); - void clear(); + bool get_stage_info(); + bool auto_battle(); + bool speed_up(); + bool use_skill(const Rect& rect); + void clear(); - // 通过资源文件离线计算可放置干员的位置,优先使用 - // 返回 可格子的位置 - Point get_placed(Loc buildable_type); + // 通过资源文件离线计算可放置干员的位置,优先使用 + // 返回 可格子的位置 + Point get_placed(Loc buildable_type); - // 计算摆放干员的朝向 - // 返回滑动的方向 - Point calc_direction(Point loc, Role role); + // 计算摆放干员的朝向 + // 返回滑动的方向 + Point calc_direction(Point loc, Role role); - bool m_used_opers = false; - int m_pre_hp = 0; + bool m_used_opers = false; + int m_pre_hp = 0; - std::unordered_map m_side_tile_info; - std::vector m_homes; - size_t m_cur_home_index = 0; - std::unordered_set m_used_tiles; + std::unordered_map m_side_tile_info; + std::vector m_homes; + size_t m_cur_home_index = 0; + std::unordered_set m_used_tiles; - std::string m_stage_name; - }; + std::string m_stage_name; + }; } diff --git a/src/MeoAssistant/TilePack.h b/src/MeoAssistant/TilePack.h index fc4fca15df..129ae87c41 100644 --- a/src/MeoAssistant/TilePack.h +++ b/src/MeoAssistant/TilePack.h @@ -7,59 +7,59 @@ namespace Map { - class TileCalc; + class TileCalc; } namespace asst { - class TilePack final : public AbstractResource - { - public: - enum class BuildableType - { - Invalid = -1, - None = 0, - Melee = 1, - Ranged = 2, - All = 3 - }; - enum class HeightType - { - Invalid = -1, - Highland = 0, - Floor = 1 - }; - enum class TileKey - { - Invalid = -1, - Forbidden, // 不能放干员,敌人也不能走 - Wall, // 可以放高台干员的位置 - Road, // 可以放地面干员,敌人也可以走 - Home, // 蓝门(可能还有其他的情况) - EnemyHome, // 红门(可能还有其他的情况) - Floor, // 不能放干员,但敌人可以走 - Hole, // 空降兵掉下去的地方( - Telin, // 不知道是啥 - Telout // 不知道是啥 - }; + class TilePack final : public AbstractResource + { + public: + enum class BuildableType + { + Invalid = -1, + None = 0, + Melee = 1, + Ranged = 2, + All = 3 + }; + enum class HeightType + { + Invalid = -1, + Highland = 0, + Floor = 1 + }; + enum class TileKey + { + Invalid = -1, + Forbidden, // 不能放干员,敌人也不能走 + Wall, // 可以放高台干员的位置 + Road, // 可以放地面干员,敌人也可以走 + Home, // 蓝门(可能还有其他的情况) + EnemyHome, // 红门(可能还有其他的情况) + Floor, // 不能放干员,但敌人可以走 + Hole, // 空降兵掉下去的地方( + Telin, // 不知道是啥 + Telout // 不知道是啥 + }; - struct TileInfo - { - BuildableType buildable = BuildableType::Invalid; - HeightType height = HeightType::Invalid; - TileKey key = TileKey::Invalid; - Point pos; // 像素坐标 - Point loc; // 格子位置 - }; - public: - using AbstractResource::AbstractResource; - virtual ~TilePack(); + struct TileInfo + { + BuildableType buildable = BuildableType::Invalid; + HeightType height = HeightType::Invalid; + TileKey key = TileKey::Invalid; + Point pos; // 像素坐标 + Point loc; // 格子位置 + }; + public: + using AbstractResource::AbstractResource; + virtual ~TilePack(); - virtual bool load(const std::string& dir) override; + virtual bool load(const std::string& dir) override; - std::unordered_map calc(const std::string& stage_code, bool side) const; + std::unordered_map calc(const std::string& stage_code, bool side) const; - private: - std::shared_ptr m_tile_calculator = nullptr; - }; + private: + std::shared_ptr m_tile_calculator = nullptr; + }; }