diff --git a/resource/roguelike_recruit.json b/resource/roguelike_recruit.json index a0d6b07811..e5acb28cad 100644 --- a/resource/roguelike_recruit.json +++ b/resource/roguelike_recruit.json @@ -13,270 +13,394 @@ "Warrior": [ { "name": "帕拉斯", - "skill": 1 + "skill": 1, + "recruit_priority": 900, + "promote_priority": 850 }, { "name": "Sharp", - "skill": 1 + "skill": 1, + "recruit_priority": 650, + "promote_priority": 600 }, { "name": "艾丽妮", - "skill": 1 + "skill": 1, + "recruit_priority": 800, + "promote_priority": 750 }, { "name": "煌", - "skill": 2 + "skill": 2, + "recruit_priority": 700, + "promote_priority": 650 }, { "name": "山", "skill": 2, - "skill_usage": 2 + "skill_usage": 2, + "recruit_priority": 700, + "promote_priority": 650 }, { "name": "羽毛笔", - "skill": 1 + "skill": 1, + "recruit_priority": 600, + "promote_priority": 550 }, { "name": "银灰", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 500, + "promote_priority": 800 }, { "name": "陈", - "skill": 2 + "skill": 2, + "recruit_priority": 500, + "promote_priority": 600 }, { "name": "拉普兰德", - "skill": 2 + "skill": 2, + "recruit_priority": 450, + "promote_priority": 400 }, { "name": "幽灵鲨", - "skill": 2 + "skill": 2, + "recruit_priority": 450, + "promote_priority": 400 }, { "name": "龙舌兰", - "skill": 1 + "skill": 1, + "recruit_priority": 400, + "promote_priority": 400 }, { "name": "星极", - "skill": 2 + "skill": 2, + "recruit_priority": 400, + "promote_priority": 400 }, { "name": "棘刺", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 350, + "promote_priority": 1000 }, { "name": "柏喙", - "skill": 2 + "skill": 2, + "recruit_priority": 350, + "promote_priority": 300 }, { "name": "泡普卡", - "skill": 1 + "skill": 1, + "recruit_priority": 10, + "promote_priority": 0 }, { "name": "月见夜", - "skill": 1 + "skill": 1, + "recruit_priority": 10, + "promote_priority": 0 }, { "name": "玫兰莎", - "skill": 1 + "skill": 1, + "recruit_priority": 10, + "promote_priority": 0 } ], "Pioneer": [ { "name": "风笛", - "skill": 2 + "skill": 2, + "recruit_priority": 600, + "promote_priority": 500 }, { "name": "芬", - "skill": 1 + "skill": 1, + "recruit_priority": 200, + "promote_priority": 0 }, { "name": "桃金娘", - "skill": 1 + "skill": 1, + "recruit_priority": 200, + "promote_priority": 550 }, { "name": "翎羽", - "skill": 1 + "skill": 1, + "recruit_priority": 10, + "promote_priority": 0 } ], "Tank": [ { "name": "泥岩", - "skill": 2 + "skill": 2, + "recruit_priority": 750, + "promote_priority": 650 }, { "name": "塞雷娅", - "skill": 2 + "skill": 2, + "recruit_priority": 800, + "promote_priority": 650 }, { "name": "斑点", - "skill": 1 + "skill": 1, + "recruit_priority": 100, + "promote_priority": 0 }, { "name": "卡缇", - "skill": 1 + "skill": 1, + "recruit_priority": 80, + "promote_priority": 0 } ], "Sniper": [ { "name": "假日威龙陈", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 900, + "promote_priority": 900 }, { "name": "Stormeye", - "skill": 1 + "skill": 1, + "recruit_priority": 750, + "promote_priority": 650 }, { "name": "能天使", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 700, + "promote_priority": 650 }, { "name": "空弦", - "skill": 1 + "skill": 1, + "recruit_priority": 650, + "promote_priority": 600 }, { "name": "克洛丝", - "skill": 1 + "skill": 1, + "recruit_priority": 150, + "promote_priority": 0 }, { "name": "安德切尔", - "skill": 1 + "skill": 1, + "recruit_priority": 100, + "promote_priority": 0 }, { "name": "空爆", - "skill": 1 + "skill": 1, + "recruit_priority": 80, + "promote_priority": 0 } ], "Caster": [ { "name": "澄闪", "skill": 3, - "alternate_skill": 2 + "alternate_skill": 2, + "recruit_priority": 700, + "promote_priority": 800 }, { "name": "Pith", - "skill": 1 + "skill": 1, + "recruit_priority": 600, + "promote_priority": 600 }, { "name": "艾雅法拉", - "skill": 2 + "skill": 2, + "recruit_priority": 600, + "promote_priority": 600 }, { "name": "阿米娅", - "skill": 1 + "skill": 1, + "recruit_priority": 500, + "promote_priority": 500 }, { "name": "史都华德", - "skill": 1 + "skill": 1, + "recruit_priority": 100, + "promote_priority": 0 }, { "name": "炎熔", - "skill": 1 + "skill": 1, + "recruit_priority": 80, + "promote_priority": 0 } ], "Special": [ { "name": "归溟幽灵鲨", - "skill": 2 + "skill": 2, + "recruit_priority": 700, + "promote_priority": 700 }, { "name": "孑", - "skill": 2 + "skill": 2, + "recruit_priority": 80, + "promote_priority": 600 } ], "Medic": [ { "name": "凯尔希", "skill": 3, - "alternate_skill": 2 + "alternate_skill": 2, + "recruit_priority": 650, + "promote_priority": 600 }, { "name": "Touch", - "skill": 1 + "skill": 1, + "recruit_priority": 600, + "promote_priority": 500 }, { "name": "夜莺", - "skill": 2 + "skill": 2, + "recruit_priority": 550, + "promote_priority": 450 }, { "name": "白面鸮", - "skill": 2 + "skill": 2, + "recruit_priority": 550, + "promote_priority": 450 }, { "name": "桑葚", - "skill": 1 + "skill": 1, + "recruit_priority": 500, + "promote_priority": 400 }, { "name": "蜜莓", - "skill": 1 + "skill": 1, + "recruit_priority": 700, + "promote_priority": 1 }, { "name": "苏苏洛", - "skill": 2 + "skill": 2, + "recruit_priority": 500, + "promote_priority": 400 }, { "name": "调香师", - "skill": 1 + "skill": 1, + "recruit_priority": 700, + "promote_priority": 650 }, { "name": "安赛尔", - "skill": 1 + "skill": 1, + "recruit_priority": 100, + "promote_priority": 0 }, { "name": "芙蓉", - "skill": 1 + "skill": 1, + "recruit_priority": 80, + "promote_priority": 0 } ], "Support": [ { "name": "灵知", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 10, + "promote_priority": 500 }, { "name": "令", "skill": 3, - "alternate_skill": 1 + "alternate_skill": 1, + "recruit_priority": 10, + "promote_priority": 900 }, { "name": "铃兰", "skill": 3, - "alternate_skill": 2 + "alternate_skill": 2, + "recruit_priority": 10, + "promote_priority": 300 }, { "name": "安洁莉娜", "skill": 3, - "alternate_skill": 2 + "alternate_skill": 2, + "recruit_priority": 10, + "promote_priority": 200 }, { "name": "浊心斯卡蒂", - "skill": 2 + "skill": 2, + "recruit_priority": 10, + "promote_priority": 500 }, { "name": "梓兰", - "skill": 1 + "skill": 1, + "recruit_priority": 10, + "promote_priority": 0 } ], "Alternate": [ { "name": "预备干员-近战", - "skill": 0 + "skill": 0, + "recruit_priority": 5, + "promote_priority": 0, + "is_alternate": true }, { "name": "预备干员-狙击", - "skill": 0 + "skill": 0, + "recruit_priority": 5, + "promote_priority": 0, + "is_alternate": true }, { "name": "预备干员-术师", - "skill": 0 + "skill": 0, + "recruit_priority": 5, + "promote_priority": 0, + "is_alternate": true }, { "name": "预备干员-后勤", - "skill": 0 + "skill": 0, + "recruit_priority": 5, + "promote_priority": 0, + "is_alternate": true } ] } diff --git a/resource/tasks.json b/resource/tasks.json index 9a590b721d..f46da21960 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -4405,6 +4405,53 @@ 300 ] }, + "RoguelikeRecruitSwipeToTheLeftBegin": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 1080, + 200, + 100, + 300 + ], + "preDelay": 300, + "rearDelay": 1000, + "maxTimes": 2, + "Doc": "这里的preDelay作为滑动duration使用,rearDelay作为滑动额外延时使用,maxTimes作为重复次数使用" + }, + "RoguelikeRecruitSwipeToTheLeftEnd": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 400, + 200, + 100, + 300 + ] + }, + "RoguelikeRecruitSlowlySwipeRightRect": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 780, + 200, + 100, + 300 + ], + "preDelay": 1500, + "rearDelay": 300, + "Doc": "这里的preDelay作为滑动duration使用,rearDelay作为滑动额外延时使用" + }, + "RoguelikeRecruitSlowlySwipeLeftRect": { + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [ + 360, + 200, + 100, + 300 + ] + }, "InfrastOperMoodProgressBar": { "template": "empty.png", "templThreshold": 160, diff --git a/src/MeoAssistant/RoguelikeRecruitConfiger.cpp b/src/MeoAssistant/RoguelikeRecruitConfiger.cpp index cddfa66b1e..374c59ebd6 100644 --- a/src/MeoAssistant/RoguelikeRecruitConfiger.cpp +++ b/src/MeoAssistant/RoguelikeRecruitConfiger.cpp @@ -29,6 +29,9 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json) std::string name = oper_info.at("name").as_string(); RoguelikeOperInfo info; info.name = name; + info.recruit_priority = oper_info.get("recruit_priority", 0); + info.promote_priority = oper_info.get("promote_priority", 0); + info.is_alternate = oper_info.get("is_alternate", false); info.skill = oper_info.at("skill").as_integer(); info.alternate_skill = oper_info.get("alternate_skill", 0); info.skill_usage = static_cast(oper_info.get("skill_usage", 1)); diff --git a/src/MeoAssistant/RoguelikeRecruitConfiger.h b/src/MeoAssistant/RoguelikeRecruitConfiger.h index aea8cdc3b1..c59e392ea8 100644 --- a/src/MeoAssistant/RoguelikeRecruitConfiger.h +++ b/src/MeoAssistant/RoguelikeRecruitConfiger.h @@ -15,6 +15,9 @@ namespace asst struct RoguelikeOperInfo { std::string name; + int recruit_priority = 0; // 招募优先级 (0-1000) + int promote_priority = 0; // 晋升优先级 (0-1000) + bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动) int skill = 0; int alternate_skill = 0; BattleSkillUsage skill_usage = BattleSkillUsage::Possibly; diff --git a/src/MeoAssistant/RoguelikeRecruitTaskPlugin.cpp b/src/MeoAssistant/RoguelikeRecruitTaskPlugin.cpp index 41fdfafa57..83dc9249ee 100644 --- a/src/MeoAssistant/RoguelikeRecruitTaskPlugin.cpp +++ b/src/MeoAssistant/RoguelikeRecruitTaskPlugin.cpp @@ -38,76 +38,216 @@ bool asst::RoguelikeRecruitTaskPlugin::_run() recruited = true; }; + const auto& recruit_cfg = Resrc.roguelike_recruit(); + + // 编队信息 (已有角色) + std::string str_chars_info = m_status->get_str(RuntimeStatus::RoguelikeCharOverview).value_or(json::value().to_string()); + json::value json_chars_info = json::parse(str_chars_info).value_or(json::value()); + const auto& chars_map = json_chars_info.as_object(); + + // 候选干员 + std::vector recruit_list; + constexpr int SwipeTimes = 5; int i = 0; + + // 翻页找出所有候选干员 for (; i != SwipeTimes; ++i) { auto image = m_ctrler->get_image(); RoguelikeRecruitImageAnalyzer analyzer(image); if (!analyzer.analyze()) { - return false; - } - - const auto& oper_list = analyzer.get_result(); - for (const auto& info : oper_list) { - // 先看看有没有精二的 - if (info.elite != 2) { - continue; - } - recruit_oper(info); - break; - } - - if (!recruited) { - for (const auto& info : oper_list) { - if (!info.required) { - continue; - } - // 拿个精一 50 以上的 - if (info.elite == 0 || - (info.elite == 1 && info.level < 50)) { - continue; - } - recruit_oper(info); - break; - } - } - - if (!recruited) { - Log.info("All are lower"); - // 随便招个精一的 - for (const auto& info : oper_list) { - if (!info.required) { - continue; - } - if (info.elite == 0) { - continue; - } - recruit_oper(info); - break; - } - } - - // 这玩意选了也没啥用,不如省点理智 - //if (!recruited) { - // // 随便招个 - // Log.info("All are very lower"); - // auto info = oper_list.front(); - // recruit_oper(info); - //} - - // 没有合适的干员,尝试向右翻页 - if (!recruited) { - ProcessTask(*this, { "SlowlySwipeToTheRight" }).run(); + // 本页没有检测到符合条件的干员,向右滑动 + slowly_swipe(ProcessTaskAction::SlowlySwipeToTheRight); sleep(Task.get("Roguelike1Custom-HijackSquad")->rear_delay); continue; } - // 已经招募到合适的干员 - return true; + const auto& oper_list = analyzer.get_result(); + bool stop_swipe = false; + + for (const auto& oper_info : oper_list) { + // 查询招募配置 + auto& recruit_info = recruit_cfg.get_oper_info(oper_info.name); + if (recruit_info.name.empty()) { + continue; + } + + int priority = 0; + + // 查询编队是否已持有该干员 + const auto& char_opt = chars_map.find(oper_info.name); + + if (char_opt.has_value()) { + // 干员已在编队中 + int elite = char_opt.value().get("elite", 0); + if (elite == 2) { + // 干员已精二,忽略 + continue; + } + + if (recruit_info.is_alternate) { + // 预备干员可以重复招募 + priority = recruit_info.recruit_priority; + } + else { + // 干员待晋升 + priority = recruit_info.promote_priority; + } + } + else { + // 干员待招募 + if (oper_info.elite == 2) { + // TODO: 招募时已精二 (随机提升或对应分队) => promoted_priority + priority = recruit_info.recruit_priority; + } + else { + // 招募时未精二 + priority = recruit_info.recruit_priority; + } + } + + // 已经划到后备干员,且已有候选干员,假定后面的干员优先级不会更高,不再划动 + if (recruit_info.is_alternate && !recruit_list.empty()) { + stop_swipe = true; + } + + // 添加到候选名单 + auto existing_it = ranges::find_if(recruit_list, [&](const RoguelikeRecruitInfo& pri) -> bool { + return pri.name == recruit_info.name; + }); + if (existing_it == recruit_list.cend()) { + RoguelikeRecruitInfo info; + info.name = recruit_info.name; + info.priority = priority; + info.is_alternate = recruit_info.is_alternate; + info.page_index = i; + recruit_list.emplace_back(info); + } + } + + if (stop_swipe) { + break; + } + + // 向右滑动 + slowly_swipe(ProcessTaskAction::SlowlySwipeToTheRight); + sleep(Task.get("Roguelike1Custom-HijackSquad")->rear_delay); } - Log.info(__FUNCTION__, "did not choose oper."); - ProcessTask(*this, { "SwipeToTheLeft" }).set_times_limit("SwipeToTheLeft", i / 2 + 1).run(); + // 没有候选干员,进入后备逻辑 + if (recruit_list.empty()) { + Log.trace(__FUNCTION__, "| No oper in recruit list."); + + // 如果划动过,先划回最左边 + if (i != 0) { + swipe_to_the_left_of_operlist(i / 2 + 1); + } + + auto image = m_ctrler->get_image(); + RoguelikeRecruitImageAnalyzer analyzer(image); + if (!analyzer.analyze()) { + Log.error(__FUNCTION__, "| Random recruitment analyse failed"); + return false; + } + + const auto& oper_list = analyzer.get_result(); + + // 可能是晋升界面,随便选个精二的 + for (const auto& info : oper_list) { + if (info.elite != 2) { + continue; + } + Log.trace(__FUNCTION__, "| Choose random elite 2:", info.name, info.elite, info.level); + recruit_oper(info); + return true; + } + + // 仍然没选,随便选个精一 50 以上的 + for (const auto& info : oper_list) { + if (info.elite == 0 || + (info.elite == 1 && info.level < 50)) { + continue; + } + Log.trace(__FUNCTION__, "| Choose random elite 1:", info.name, info.elite, info.level); + recruit_oper(info); + return true; + } + + Log.trace(__FUNCTION__, "| Did not choose oper"); + return false; + } + + // 选择优先级最高的干员 + auto selected_oper = ranges::max_element(recruit_list, std::less{}, std::mem_fn(&RoguelikeRecruitInfo::priority)); + if (selected_oper == recruit_list.cend()) { + Log.trace(__FUNCTION__, "| No opers in recruit list."); + return false; + } + + auto& char_name = selected_oper->name; + Log.trace(__FUNCTION__, "| Top priority oper:", char_name, selected_oper->priority, "page", selected_oper->page_index, "/", i); + + // 滑动方向 + // 页码大于一半: 从右往左划动 + // 页码小于一半: 划回最左边再往右 + bool is_rtl = false; + + // 如果划动过,判断目标角色离哪个方向更近 + if (i != 0) { + is_rtl = (selected_oper->page_index * 2) >= i; + if (!is_rtl) { + // 从左往右需要先划回最左边 + swipe_to_the_left_of_operlist(i / 2 + 1); + } + } + + return check_char(char_name, is_rtl); +} + +bool asst::RoguelikeRecruitTaskPlugin::check_char(const std::string& char_name, bool is_rtl) +{ + LogTraceFunction; + + constexpr int SwipeTimes = 10; + int i = 0; + for (; i != SwipeTimes; ++i) { + auto image = m_ctrler->get_image(); + RoguelikeRecruitImageAnalyzer analyzer(image); + + // 只处理识别成功的情况,失败(无任何结果)时继续滑动 + if (analyzer.analyze()) { + const auto& chars = analyzer.get_result(); + auto it = ranges::find_if(chars, + [&](const BattleRecruitOperInfo& oper) -> bool { + return oper.name == char_name; + }); + + std::string oper_names = ""; + for (const auto& oper : chars) { + if (!oper_names.empty()) { + oper_names += ", "; + } + oper_names += oper.name; + } + Log.info(__FUNCTION__, "| Oper list:", oper_names); + + if (it != chars.cend()) { + select_oper(*it); + return true; + } + } + + // 没识别到目标干员,可能不在这一页,继续划动 + if (is_rtl) { + slowly_swipe(ProcessTaskAction::SlowlySwipeToTheLeft); + } + else { + slowly_swipe(ProcessTaskAction::SlowlySwipeToTheRight); + } + sleep(Task.get("Roguelike1Custom-HijackSquad")->rear_delay); + } + Log.info(__FUNCTION__, "| Cannot find oper `" + char_name + "`"); + swipe_to_the_left_of_operlist(); return false; } @@ -120,40 +260,7 @@ bool asst::RoguelikeRecruitTaskPlugin::check_core_char() return false; } m_status->set_str("RoguelikeCoreChar", ""); - constexpr int SwipeTimes = 10; - int i = 0; - for (; i != SwipeTimes; ++i) { - auto image = m_ctrler->get_image(); - RoguelikeRecruitImageAnalyzer analyzer(image); - if (!analyzer.analyze()) { - Log.info(__FUNCTION__, "| Unable to analyze image."); - break; - } - const auto& chars = analyzer.get_result(); - auto it = ranges::find_if(chars, - [&](const BattleRecruitOperInfo& oper) -> bool { - return oper.name == core_opt.value(); - }); - - std::string oper_names = ""; - for (const auto& oper : chars) { - if (!oper_names.empty()) { - oper_names += ", "; - } - oper_names += oper.name; - } - Log.info(__FUNCTION__, "| Oper list:", oper_names); - if (it == chars.cend()) { - ProcessTask(*this, { "SlowlySwipeToTheRight" }).run(); - sleep(Task.get("Roguelike1Custom-HijackSquad")->rear_delay); - continue; - } - select_oper(*it); - return true; - } - Log.info(__FUNCTION__, "| Cannot find oper `" + core_opt.value() + "`"); - ProcessTask(*this, { "SwipeToTheLeft" }).run(); - return false; + return check_char(core_opt.value()); } void asst::RoguelikeRecruitTaskPlugin::select_oper(const BattleRecruitOperInfo& oper) @@ -173,3 +280,41 @@ void asst::RoguelikeRecruitTaskPlugin::select_oper(const BattleRecruitOperInfo& }; m_status->set_str(RuntimeStatus::RoguelikeCharOverview, overview.to_string()); } + +void asst::RoguelikeRecruitTaskPlugin::swipe_to_the_left_of_operlist(int loop_times) +{ + LogTraceFunction; + static Rect begin_rect = Task.get("RoguelikeRecruitSwipeToTheLeftBegin")->specific_rect; // 1080, 200, 100, 300 + static Rect end_rect = Task.get("RoguelikeRecruitSwipeToTheLeftEnd")->specific_rect; // 400, 200, 100, 300 + static int duration = Task.get("RoguelikeRecruitSwipeToTheLeftBegin")->pre_delay; + static int extra_delay = Task.get("RoguelikeRecruitSwipeToTheLeftBegin")->rear_delay; + static int cfg_loop_times = Task.get("RoguelikeRecruitSwipeToTheLeftBegin")->max_times; + + for (int i = 0; i != cfg_loop_times * loop_times; ++i) { + if (need_exit()) { + return; + } + m_ctrler->swipe(end_rect, begin_rect, duration, true, 0, false); + } + sleep(extra_delay); +} + +void asst::RoguelikeRecruitTaskPlugin::slowly_swipe(ProcessTaskAction action) +{ + LogTraceFunction; + static Rect right_rect = Task.get("RoguelikeRecruitSlowlySwipeRightRect")->specific_rect; // 780, 200, 100, 300 + static Rect left_rect = Task.get("RoguelikeRecruitSlowlySwipeLeftRect")->specific_rect; // 360, 200, 100, 300 + static int duration = Task.get("RoguelikeRecruitSlowlySwipeRightRect")->pre_delay; + static int extra_delay = Task.get("RoguelikeRecruitSlowlySwipeRightRect")->rear_delay; + + switch (action) { + case asst::ProcessTaskAction::SlowlySwipeToTheLeft: + m_ctrler->swipe(left_rect, right_rect, duration, true, extra_delay, true); + break; + case asst::ProcessTaskAction::SlowlySwipeToTheRight: + m_ctrler->swipe(right_rect, left_rect, duration, true, extra_delay, true); + break; + default: // 走不到这里 + break; + } +} diff --git a/src/MeoAssistant/RoguelikeRecruitTaskPlugin.h b/src/MeoAssistant/RoguelikeRecruitTaskPlugin.h index 031221a6dd..879477053f 100644 --- a/src/MeoAssistant/RoguelikeRecruitTaskPlugin.h +++ b/src/MeoAssistant/RoguelikeRecruitTaskPlugin.h @@ -5,6 +5,14 @@ namespace asst { + // 干员招募信息 + struct RoguelikeRecruitInfo + { + std::string name; // 干员名字 + int priority = 0; // 招募优先级 (0-1000) + int page_index = 0; // 所在页码 (用于判断翻页方向) + bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动) + }; class RoguelikeRecruitTaskPlugin : public AbstractTaskPlugin { public: @@ -15,9 +23,20 @@ namespace asst protected: virtual bool _run() override; + // 滑动到干员列表的最左侧 + void swipe_to_the_left_of_operlist(int loop_times = 1); + // 缓慢向干员列表的左侧/右侧滑动 + void slowly_swipe(ProcessTaskAction action); private: bool check_core_char(); + // 招募指定干员 + // + // 输入参数: + // char_name: 干员名称 + // is_rtl: 滑动方向 (true: 从右向左; false: 从左向右,需要先滑动到最左侧) + // 返回值: 招募结果 (true: 成功; false: 失败) + bool check_char(const std::string& char_name, bool is_rtl = false); void select_oper(const BattleRecruitOperInfo& oper); }; }