mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-16 09:50:40 +08:00
feat: 自动战斗多作业支持保存干员组内编入的干员 (#14095)
* chore: 加个存储先 * feat: 与旧编队比较 * feat: 快速编队 * fix: 计数 * fix: 缓存了旧的组名 * perf: 快速退出 * fix: 跳过编队后修改编队状态 * fix: 完事 * fix: err * fix: 顺手一起修了
This commit is contained in:
@@ -29,8 +29,10 @@ struct OperatorRequirements
|
||||
// int elite = -1; // 精英化等级
|
||||
// int level = -1; // 干员等级
|
||||
// int skill_level = -1; // 技能等级
|
||||
int module = -1; // 模组编号 -1: 不切换模组 / 无要求, 0: 不使用模组, 1: 模组χ, 2: 模组γ, 3: 模组α, 4: 模组Δ
|
||||
// int potentiality = -1; // 潜能要求
|
||||
int module = -1; // 模组编号 -1: 不切换模组 / 无要求, 0: 不使用模组, 1: 模组χ, 2: 模组γ, 3: 模组α, 4: 模组Δ
|
||||
|
||||
bool operator==(const OperatorRequirements& req) const { return module == req.module; }
|
||||
};
|
||||
|
||||
// 干员编队状态
|
||||
@@ -50,7 +52,13 @@ struct OperUsage // 干员用法
|
||||
SkillUsage skill_usage = SkillUsage::NotUse;
|
||||
int skill_times = 1; // 使用技能的次数,默认为 1,兼容曾经的作业
|
||||
battle::OperatorRequirements requirements {}; // 练度需求
|
||||
OperStatus status = OperStatus::Unchecked; // 编队状态
|
||||
OperStatus status = OperStatus::Unchecked; // 编队状态, 可能有其他更好的位置存储
|
||||
|
||||
bool operator==(const OperUsage& other) const
|
||||
{
|
||||
return name == other.name && skill == other.skill && skill_usage == other.skill_usage &&
|
||||
skill_times == other.skill_times && requirements == other.requirements;
|
||||
}
|
||||
};
|
||||
|
||||
enum class DeployDirection
|
||||
|
||||
@@ -151,9 +151,10 @@ bool asst::CopilotTask::set_params(const json::value& params)
|
||||
size_t count = configs_cvt.size();
|
||||
// 追加任务
|
||||
m_subtasks.reserve(m_subtasks.size() * count);
|
||||
auto raw_end = m_subtasks.end();
|
||||
// 保存原始大小
|
||||
size_t original_size = m_subtasks.size();
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
m_subtasks.insert(m_subtasks.end(), m_subtasks.begin(), raw_end);
|
||||
m_subtasks.insert(m_subtasks.end(), m_subtasks.begin(), m_subtasks.begin() + original_size);
|
||||
}
|
||||
m_multi_copilot_plugin_ptr->set_multi_copilot_config(std::move(configs_cvt));
|
||||
m_has_subtasks_duplicate = true;
|
||||
@@ -202,9 +203,9 @@ bool asst::CopilotTask::set_params(const json::value& params)
|
||||
|
||||
// 追加
|
||||
m_subtasks.reserve(m_subtasks.size() * loop_times);
|
||||
auto raw_end = m_subtasks.end();
|
||||
size_t original_size = m_subtasks.size();
|
||||
for (size_t i = 1; i < loop_times; ++i) {
|
||||
m_subtasks.insert(m_subtasks.end(), m_subtasks.begin(), raw_end);
|
||||
m_subtasks.insert(m_subtasks.end(), m_subtasks.begin(), m_subtasks.begin() + original_size);
|
||||
}
|
||||
m_has_subtasks_duplicate = true;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,26 @@ bool asst::BattleFormationTask::_run()
|
||||
if (!parse_formation()) {
|
||||
return false;
|
||||
}
|
||||
else if (compare_formation()) {
|
||||
Log.info(__FUNCTION__, "| Formation is the same as last time, skip");
|
||||
for (auto& [name, opers] : m_formation | std::views::values | std::views::join) {
|
||||
const auto& pair_it =
|
||||
std::ranges::find_if(*m_opers_in_formation, [&](const auto& pair) { return pair.second == name; });
|
||||
if (pair_it == m_opers_in_formation->end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto oper_it =
|
||||
std::ranges::find_if(opers, [&](const battle::OperUsage& oper) { return oper.name == pair_it->first; });
|
||||
if (oper_it == opers.end()) {
|
||||
Log.error(__FUNCTION__, "| Cannot find oper ", pair_it->first, " in m_formation");
|
||||
}
|
||||
else {
|
||||
oper_it->status = battle::OperStatus::Selected; // 更新编队情况
|
||||
}
|
||||
}
|
||||
return true; // 编队未变更,跳过
|
||||
}
|
||||
|
||||
if (m_select_formation_index > 0 && !select_formation(m_select_formation_index, img)) {
|
||||
return false;
|
||||
@@ -182,19 +202,66 @@ bool asst::BattleFormationTask::_run()
|
||||
|
||||
void asst::BattleFormationTask::formation_with_last_opers()
|
||||
{
|
||||
std::vector<OperGroup*> opers;
|
||||
for (auto& [role, oper_groups] : m_formation) {
|
||||
for (auto& oper_group : oper_groups) {
|
||||
if (oper_group.second.size() != 1) {
|
||||
continue;
|
||||
}
|
||||
// 不支持干员组,以免选中练度更低的干员
|
||||
opers.emplace_back(&oper_group);
|
||||
}
|
||||
if (m_opers_in_formation->empty()) {
|
||||
return;
|
||||
}
|
||||
std::unordered_map<std::string, std::string> need_formation;
|
||||
m_opers_in_formation->swap(need_formation);
|
||||
|
||||
// 此时的oper_in_formation是根据上次编队结果复用的, 但是task此时已清空, 重新选一下
|
||||
const auto& opers_result = analyzer_opers(ctrler()->get_image());
|
||||
if (opers_result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (need_exit() || !select_opers_in_cur_page(opers)) {
|
||||
return;
|
||||
// 展平的干员组
|
||||
std::unordered_map<std::string, std::vector<asst::battle::OperUsage>*> formation_view;
|
||||
for (auto& group : m_formation | std::views::values | std::views::join) {
|
||||
formation_view.emplace(group.first, &group.second);
|
||||
}
|
||||
const int delay = Task.get("BattleQuickFormationOCR")->post_delay;
|
||||
for (auto it = need_formation.begin(); !need_exit() && it != need_formation.end();) {
|
||||
const std::string& oper_name = it->first;
|
||||
const std::string& group_name = it->second;
|
||||
|
||||
const auto& oper_in_page_it = std::ranges::find_if(opers_result, [&](const QuickFormationOper& op) {
|
||||
return !op.is_selected && op.text == oper_name;
|
||||
}); // 编队页中的干员
|
||||
if (oper_in_page_it == opers_result.end()) [[unlikely]] {
|
||||
Log.warn(__FUNCTION__, "| Oper", oper_name, "was selected last time, but not found in current page");
|
||||
++it;
|
||||
continue; // 该干员找不到, 一页只能放下10个干员, 可能被右侧挡住. 打回到正常编队逻辑
|
||||
}
|
||||
|
||||
// 直接通过组名从展平的映射中查找
|
||||
auto group_it = formation_view.find(group_name);
|
||||
if (group_it == formation_view.end()) {
|
||||
LogError << __FUNCTION__ << "| Group" << group_name << " not found in formation";
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 在找到的组中查找具体的干员
|
||||
auto oper_it =
|
||||
std::ranges::find_if(*(group_it->second), [&](battle::OperUsage& op) { return op.name == oper_name; });
|
||||
if (oper_it == group_it->second->end()) {
|
||||
LogError << __FUNCTION__ << "| Group" << group_name << ",Oper" << oper_name
|
||||
<< "was selected last time, but not found in current pair";
|
||||
++it;
|
||||
continue; // 很怪, 按理说不会找不到, 有组相同但是找不到干员
|
||||
}
|
||||
|
||||
ctrler()->click(oper_in_page_it->flag_rect);
|
||||
sleep(delay);
|
||||
|
||||
oper_it->status = battle::OperStatus::Selected;
|
||||
json::value info = basic_info_with_what("BattleFormationSelected");
|
||||
auto& details = info["details"];
|
||||
details["selected"] = oper_name;
|
||||
details["group_name"] = group_name;
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
m_opers_in_formation->emplace(oper_name, group_name);
|
||||
it = need_formation.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +563,7 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(const std::vector<OperG
|
||||
m_last_oper_name = opers_result.back().text;
|
||||
}
|
||||
|
||||
int delay = Task.get("BattleQuickFormationOCR")->post_delay;
|
||||
const int delay = Task.get("BattleQuickFormationOCR")->post_delay;
|
||||
battle::OperUsage* oper = nullptr;
|
||||
bool ret = true;
|
||||
for (const auto& res :
|
||||
@@ -624,7 +691,10 @@ bool asst::BattleFormationTask::parse_formation()
|
||||
groups = &SSSCopilot.get_data().groups;
|
||||
}
|
||||
|
||||
std::swap(m_formation, m_formation_last);
|
||||
m_formation.clear();
|
||||
m_opers_in_formation->clear();
|
||||
m_last_oper_name = std::string();
|
||||
for (const auto& [name, opers_vec] : *groups) {
|
||||
if (opers_vec.empty()) {
|
||||
continue;
|
||||
@@ -646,12 +716,48 @@ bool asst::BattleFormationTask::parse_formation()
|
||||
// for unknown, will use { "BattleQuickFormationRole-All", "BattleQuickFormationRole-All-OCR" }
|
||||
m_formation[same_role ? role : battle::Role::Unknown].emplace_back(name, opers_vec);
|
||||
}
|
||||
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
m_opers_in_formation->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleFormationTask::compare_formation()
|
||||
{
|
||||
for (auto& [role, groups_] : m_formation) {
|
||||
const auto& role_it = m_formation_last.find(role);
|
||||
if (role_it == m_formation_last.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& last_groups = role_it->second;
|
||||
for (auto& group : groups_) {
|
||||
auto last_group_it = std::ranges::find_if(last_groups, [&](const OperGroup& g) {
|
||||
return g.first == group.first && g.second == group.second;
|
||||
});
|
||||
if (last_group_it == last_groups.end()) { // fallback to only opers equal
|
||||
last_group_it =
|
||||
std::ranges::find_if(last_groups, [&](const OperGroup& g) { return g.second == group.second; });
|
||||
}
|
||||
if (last_group_it == last_groups.end()) {
|
||||
continue; // not find the same group in last formation
|
||||
}
|
||||
|
||||
const auto& oper_last_it = std::ranges::find_if(last_group_it->second, [&](const battle::OperUsage& op) {
|
||||
return op.status == battle ::OperStatus::Selected;
|
||||
});
|
||||
if (oper_last_it == last_group_it->second.cend()) [[unlikely]] {
|
||||
LogError << __FUNCTION__ << "| Group" << last_group_it->first
|
||||
<< "was selected last time, but no oper was selected";
|
||||
continue; // 旧编队中该干员组有干员被选中,但找不到被选中的干员
|
||||
}
|
||||
m_opers_in_formation->emplace(oper_last_it->name, group.first);
|
||||
last_groups.erase(last_group_it); // 移除已匹配的干员组
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<size_t>(std::ranges::distance(m_formation | std::views::values | std::views::join)) ==
|
||||
m_opers_in_formation->size();
|
||||
}
|
||||
|
||||
bool asst::BattleFormationTask::select_formation(int select_index, const cv::Mat& img)
|
||||
{
|
||||
// 编队不会触发改名的区域有两组
|
||||
|
||||
@@ -89,6 +89,8 @@ protected:
|
||||
|
||||
virtual bool _run() override;
|
||||
bool parse_formation();
|
||||
// 判断当前编队是否与上次相同, 同时为相同部分添加进已选中, return 是否完全相同
|
||||
bool compare_formation();
|
||||
bool is_formation_valid(const cv::Mat& img) const;
|
||||
bool select_formation(int select_index, const cv::Mat& img);
|
||||
bool enter_selection_page(const cv::Mat& img = cv::Mat());
|
||||
@@ -113,13 +115,14 @@ protected:
|
||||
|
||||
std::vector<QuickFormationOper> analyzer_opers(const cv::Mat& image);
|
||||
|
||||
std::string m_stage_name;
|
||||
std::unordered_map<battle::Role, std::vector<OperGroup>> m_formation; // 作业编队
|
||||
std::unordered_map<battle::Role, std::vector<OperGroup>> m_formation; // 作业编队
|
||||
std::unordered_map<battle::Role, std::vector<OperGroup>> m_formation_last; // 上次的编队
|
||||
// 编队中的干员名称-所属组名
|
||||
std::shared_ptr<std::unordered_map<std::string, std::string>> m_opers_in_formation =
|
||||
std::make_shared<std::unordered_map<std::string, std::string>>(); // 编队中的干员名称-所属组名
|
||||
bool m_add_trust = false; // 是否需要追加信赖干员
|
||||
bool m_ignore_requirements = false; // 是否跳过未满足的干员属性要求
|
||||
std::vector<std::pair<std::string, int>> m_user_additional; // 追加干员表,从头往后加
|
||||
std::make_shared<std::unordered_map<std::string, std::string>>();
|
||||
bool m_add_trust = false; // 是否需要追加信赖干员
|
||||
bool m_ignore_requirements = false; // 是否跳过未满足的干员属性要求
|
||||
std::vector<std::pair<std::string, int>> m_user_additional; // 追加干员表,从头往后加
|
||||
DataResource m_data_resource = DataResource::Copilot;
|
||||
std::vector<AdditionalFormation> m_additional;
|
||||
std::string m_last_oper_name;
|
||||
|
||||
Reference in New Issue
Block a user