#include "RoguelikeFoldartalUseTaskPlugin.h" #include "Config/Roguelike/RoguelikeFoldartalConfig.h" #include "Config/TaskData.h" #include "Controller/Controller.h" #include "Status.h" #include "Task/ProcessTask.h" #include "Utils/Logger.hpp" #include "Vision/OCRer.h" bool asst::RoguelikeFoldartalUseTaskPlugin::verify(AsstMsg msg, const json::value& details) const { if (msg != AsstMsg::SubTaskStart || details.get("subtask", std::string()) != "ProcessTask") { return false; } if (m_roguelike_theme.empty()) { Log.error("Roguelike name doesn't exist!"); return false; } if (m_roguelike_theme != "Sami") { return false; } std::string mode = status()->get_properties(Status::RoguelikeMode).value(); std::string task_name_pre = m_roguelike_theme + "@Roguelike@Stage"; const std::string& task = details.get("details", "task", ""); std::string_view task_view = task; if (task_view.starts_with(task_name_pre)) { task_view.remove_prefix(task_name_pre.length()); } // 不知道是不是不对纵向节点用比较好,先写上 task_name_pre = "task_name_pre"; if (task_view.starts_with("Vertical")) { task_view.remove_prefix(task_name_pre.length()); } std::string task_name_suf = "AI6"; if (task_view.ends_with(task_name_suf)) { task_view.remove_suffix(task_name_suf.length()); } if (task_view == "CombatDps" || task_view == "EmergencyDps" || task_view == "FerociousPresage") { if (mode == "1" || mode == "4") { m_stage = "SkipBattle"; } else if (mode == "0") { m_stage = "Battle"; } return true; } if (task_view == "DreadfulFoe-5" && mode == "0") { m_stage = "Boss"; return true; } if (task_view == "Trader" && mode == "0") { m_stage = "Trader"; return true; } if (task_view == "Encounter" && mode == "0") { m_stage = "Encounter"; return true; } if ((task_view == "Gambling" || task_view == "EmergencyTransportation" || task_view == "WindAndRain") && mode == "0") { m_stage = "Gambling"; return true; } else { return false; } } bool asst::RoguelikeFoldartalUseTaskPlugin::_run() { LogTraceFunction; std::string theme = m_roguelike_theme; std::vector combination = RoguelikeFoldartal.get_combination(m_roguelike_theme); std::string overview_str = status()->get_str(Status::RoguelikeFoldartalOverview).value_or(json::value().to_string()); json::value overview_json = json::parse(overview_str).value_or(json::value()); m_all_boards = overview_json.as_array(); Log.debug(m_all_boards); for (const auto& usage : combination) { if (need_exit()) { return false; } if (m_stage == usage.usage) { // 用到没得用为止,但是只要没有使用成功就不继续使用 while (search_enable_pair(usage) && !need_exit()) { Log.info("Use board pairs"); } break; } } return true; } bool asst::RoguelikeFoldartalUseTaskPlugin::search_enable_pair(const auto& usage) { LogTraceFunction; for (const auto& pair : usage.pairs) { // 遍历上板子 for (const auto& up_board : pair.up_board) { // 遍历下板子 for (const auto& down_board : pair.down_board) { if (board_pair(up_board, down_board)) return true; } } } return false; } bool asst::RoguelikeFoldartalUseTaskPlugin::board_pair(const std::string& up_board, const std::string& down_board) { auto iter_up = std::find(m_all_boards.begin(), m_all_boards.end(), up_board); auto iter_down = std::find(m_all_boards.begin(), m_all_boards.end(), down_board); // 如果两个板子同时存在m_all_boards中 if (iter_up != m_all_boards.end() && iter_down != m_all_boards.end()) { if (use_board(up_board, down_board)) { // 用完删除上板子和下板子 m_all_boards.erase(iter_up); iter_down = std::find(m_all_boards.begin(), m_all_boards.end(), down_board); m_all_boards.erase(iter_down); status()->set_str(Status::RoguelikeFoldartalOverview, m_all_boards.to_string()); Log.debug("Board pair used"); return true; } } return false; } bool asst::RoguelikeFoldartalUseTaskPlugin::use_board(const std::string& up_board, const std::string& down_board) { Log.trace("Try to use the board pair", up_board, down_board); if (ProcessTask(*this, { m_roguelike_theme + "@Roguelike@Foldartal" }).run()) { swipe_to_top(); // todo:插入一个滑动时顺便更新密文板overview,因为有的板子可以用两次 if (search_and_click_board(up_board) && search_and_click_board(down_board)) { search_and_click_stage(); if (ProcessTask(*this, { m_roguelike_theme + "@Roguelike@FoldartalUseConfirm" }).run()) { return true; } } ProcessTask(*this, { m_roguelike_theme + "@Roguelike@FoldartalBack" }).run(); } return false; } bool asst::RoguelikeFoldartalUseTaskPlugin::search_and_click_board(const std::string& board) { Log.trace("Search and click the board", board); int max_retry = 10; int try_time = 0; while (try_time < max_retry && !need_exit()) { OCRer analyzer(ctrler()->get_image()); std::string task_name = m_roguelike_theme + "@Roguelike@FoldartalUseOcr"; analyzer.set_task_info(task_name); analyzer.set_required({ board }); if (!analyzer.analyze()) { // 往上滑 slowly_swipe(true); } else { ctrler()->click(analyzer.get_result().front().rect.move(Task.get(task_name)->rect_move)); return true; } try_time++; } return false; } bool asst::RoguelikeFoldartalUseTaskPlugin::search_and_click_stage() { Log.trace("Try to click stage", m_stage); // todo:根据坐标换算位置,根据节点类型设置识别优先度 // 重置到最左边 ProcessTask(*this, { "SwipeToTheLeft" }).run(); sleep(1000); // 节点会闪烁,所以这里不用单次Match if (ProcessTask(*this, { m_roguelike_theme + "@Roguelike@FoldartalUseOnStage" }).run()) { return true; } // 滑到最右边,萨米的地图只有两页,暂时先这么糊着,出现识别不到再写循环 ProcessTask(*this, { "SwipeToTheRight" }).run(); sleep(1000); if (ProcessTask(*this, { m_roguelike_theme + "@Roguelike@FoldartalUseOnStage" }).run()) { return true; } return false; } void asst::RoguelikeFoldartalUseTaskPlugin::swipe_to_top() { LogTraceFunction; int max_retry = 10; int try_time = 0; // 找到"布局"就到了最上面 while (try_time < max_retry && !need_exit()) { while (try_time < max_retry && !need_exit()) { OCRer analyzer(ctrler()->get_image()); analyzer.set_task_info(m_roguelike_theme + "@Roguelike@FoldartalUseOcr"); if (analyzer.analyze()) { return; } else { // 往下滑 ProcessTask(*this, { "RoguelikeFoldartalSwipeToTheDown" }).run(); } try_time++; } } } void asst::RoguelikeFoldartalUseTaskPlugin::slowly_swipe(bool to_up, int swipe_dist) { std::string swipe_task_name = to_up ? "RoguelikeFoldartalSlowlySwipeToTheUp" : "RoguelikeFoldartalSwipeToTheDown"; if (!ControlFeat::support(ctrler()->support_features(), ControlFeat::PRECISE_SWIPE)) { // 不能精准滑动时不使用 swipe_dist 参数 ProcessTask(*this, { swipe_task_name }).run(); return; } if (!to_up) swipe_dist = -swipe_dist; auto swipe_task = Task.get(swipe_task_name); const Rect& StartPoint = swipe_task->specific_rect; ctrler()->swipe(StartPoint, { StartPoint.x + swipe_dist - StartPoint.width, StartPoint.y, StartPoint.width, StartPoint.height }, swipe_task->special_params.empty() ? 0 : swipe_task->special_params.at(0), (swipe_task->special_params.size() < 2) ? false : swipe_task->special_params.at(1), (swipe_task->special_params.size() < 3) ? 1 : swipe_task->special_params.at(2), (swipe_task->special_params.size() < 4) ? 1 : swipe_task->special_params.at(3)); sleep(swipe_task->post_delay); }