mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-17 18:01:26 +08:00
feat: 新增根据队伍内已有同职业干员数量决定招募权重的选项
This commit is contained in:
@@ -20,21 +20,53 @@
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 750,
|
||||
"promote_priority_when_team_full_Doc": "不填默认 promote_priority + 100",
|
||||
"Doc": "编队满了且编队内所有干员 promote_priority 均大于200,才会走两个 when_team_full 的逻辑"
|
||||
"Doc": "编队满了且编队内所有干员 promote_priority 均大于200,才会走两个 when_team_full 的逻辑",
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
100,
|
||||
100
|
||||
],
|
||||
[
|
||||
200,
|
||||
-200
|
||||
]
|
||||
],
|
||||
"offset_doc": "队伍内已有100人同职业则权重+100,200人-200,已有数量从小到大"
|
||||
},
|
||||
{
|
||||
"name": "海沫",
|
||||
"skill": 1,
|
||||
"recruit_priority": 960,
|
||||
"recruit_priority": 500,
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
0,
|
||||
600
|
||||
],
|
||||
[
|
||||
2,
|
||||
-500
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "羽毛笔",
|
||||
"skill": 1,
|
||||
"recruit_priority": 950,
|
||||
"recruit_priority": 490,
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
0,
|
||||
600
|
||||
],
|
||||
[
|
||||
2,
|
||||
-490
|
||||
]
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"name": "百炼嘉维尔",
|
||||
@@ -47,17 +79,37 @@
|
||||
"name": "棘刺",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 780,
|
||||
"recruit_priority": 800,
|
||||
"promote_priority": 1000,
|
||||
"promote_priority_when_team_full": 1200
|
||||
"promote_priority_when_team_full": 1200,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
1,
|
||||
-780
|
||||
],
|
||||
[
|
||||
2,
|
||||
200
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "山",
|
||||
"skill": 2,
|
||||
"skill_usage": 2,
|
||||
"recruit_priority": 750,
|
||||
"recruit_priority": 400,
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
0,
|
||||
600
|
||||
],
|
||||
[
|
||||
2,
|
||||
-400
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "玛恩纳",
|
||||
@@ -65,7 +117,17 @@
|
||||
"alternate_skill": 2,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 700,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
0,
|
||||
-750
|
||||
],
|
||||
[
|
||||
4,
|
||||
100
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "煌",
|
||||
@@ -88,14 +150,30 @@
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 800,
|
||||
"promote_priority_when_team_full": 800
|
||||
"promote_priority_when_team_full": 800,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
3,
|
||||
100
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "艾丽妮",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 750
|
||||
"promote_priority": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
0,
|
||||
-750
|
||||
],
|
||||
[
|
||||
2,
|
||||
200
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "陈",
|
||||
@@ -321,14 +399,26 @@
|
||||
"skill": 2,
|
||||
"recruit_priority": 550,
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
2,
|
||||
-550
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "蓝毒",
|
||||
"skill": 1,
|
||||
"recruit_priority": 550,
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 750
|
||||
"promote_priority_when_team_full": 750,
|
||||
"recruit_priority_offset": [
|
||||
[
|
||||
2,
|
||||
-550
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "梅",
|
||||
|
||||
@@ -81,34 +81,7 @@ bool asst::RoguelikeCopilotConfiger::parse(const json::value& json)
|
||||
BattleRole::Warrior, BattleRole::Pioneer, BattleRole::Medic, BattleRole::Tank, BattleRole::Sniper,
|
||||
BattleRole::Caster, BattleRole::Support, BattleRole::Special, BattleRole::Drone,
|
||||
};
|
||||
static const std::unordered_map<std::string, BattleRole> NameToRole = {
|
||||
{ "warrior", BattleRole::Warrior }, { "WARRIOR", BattleRole::Warrior },
|
||||
{ "Warrior", BattleRole::Warrior }, { "近卫", BattleRole::Warrior },
|
||||
|
||||
{ "pioneer", BattleRole::Pioneer }, { "PIONEER", BattleRole::Pioneer },
|
||||
{ "Pioneer", BattleRole::Pioneer }, { "先锋", BattleRole::Pioneer },
|
||||
|
||||
{ "medic", BattleRole::Medic }, { "MEDIC", BattleRole::Medic },
|
||||
{ "Medic", BattleRole::Medic }, { "医疗", BattleRole::Medic },
|
||||
|
||||
{ "tank", BattleRole::Tank }, { "TANK", BattleRole::Tank },
|
||||
{ "Tank", BattleRole::Tank }, { "重装", BattleRole::Tank },
|
||||
|
||||
{ "sniper", BattleRole::Sniper }, { "SNIPER", BattleRole::Sniper },
|
||||
{ "Sniper", BattleRole::Sniper }, { "狙击", BattleRole::Sniper },
|
||||
|
||||
{ "caster", BattleRole::Caster }, { "CASTER", BattleRole::Caster },
|
||||
{ "Caster", BattleRole::Caster }, { "术师", BattleRole::Caster },
|
||||
|
||||
{ "support", BattleRole::Support }, { "SUPPORT", BattleRole::Support },
|
||||
{ "Support", BattleRole::Support }, { "辅助", BattleRole::Support },
|
||||
|
||||
{ "special", BattleRole::Special }, { "SPECIAL", BattleRole::Special },
|
||||
{ "Special", BattleRole::Special }, { "特种", BattleRole::Special },
|
||||
|
||||
{ "drone", BattleRole::Drone }, { "DRONE", BattleRole::Drone },
|
||||
{ "Drone", BattleRole::Drone }, { "无人机", BattleRole::Drone },
|
||||
};
|
||||
|
||||
auto to_lower = [](char c) -> char { return static_cast<char>(std::tolower(c)); };
|
||||
if (auto opt = stage_info.find<json::array>("role_order")) {
|
||||
const auto& raw_roles = opt.value();
|
||||
@@ -126,19 +99,19 @@ bool asst::RoguelikeCopilotConfiger::parse(const json::value& json)
|
||||
return std::move(name);
|
||||
});
|
||||
for (const std::string& role_name : roles) {
|
||||
auto iter = NameToRole.find(role_name);
|
||||
if (iter == NameToRole.end()) [[unlikely]] {
|
||||
const auto role = get_role_type(role_name);
|
||||
if (role == BattleRole::Unknown) [[unlikely]] {
|
||||
Log.error("Unknown BattleRole:", role_name);
|
||||
is_legal = false;
|
||||
break;
|
||||
}
|
||||
if (specified_role.contains(iter->second)) [[unlikely]] {
|
||||
if (specified_role.contains(role)) [[unlikely]] {
|
||||
Log.error("Duplicated BattleRole:", role_name);
|
||||
is_legal = false;
|
||||
break;
|
||||
}
|
||||
specified_role.emplace(iter->second);
|
||||
role_order.emplace_back(iter->second);
|
||||
specified_role.emplace(role);
|
||||
role_order.emplace_back(role);
|
||||
}
|
||||
if (is_legal) [[likely]] {
|
||||
ranges::copy(RoleOrder | filter([&](BattleRole role) { return !specified_role.contains(role); }),
|
||||
@@ -173,16 +146,16 @@ bool asst::RoguelikeCopilotConfiger::parse(const json::value& json)
|
||||
Log.error("Unknown direction");
|
||||
return false;
|
||||
}
|
||||
std::unordered_set<BattleRole> role;
|
||||
std::unordered_set<BattleRole> fd_role;
|
||||
for (auto& role_name : point["role"].as_array()) {
|
||||
auto iter = NameToRole.find(role_name.as_string());
|
||||
if (iter == NameToRole.end()) [[unlikely]] {
|
||||
const auto role = get_role_type(role_name.as_string());
|
||||
if (role == BattleRole::Unknown) [[unlikely]] {
|
||||
Log.error("Unknown role name:", role_name);
|
||||
return false;
|
||||
}
|
||||
role.emplace(iter->second);
|
||||
fd_role.emplace(role);
|
||||
}
|
||||
fd_dir.role = std::move(role);
|
||||
fd_dir.role = std::move(fd_role);
|
||||
data.force_deploy_direction.emplace(location, fd_dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json)
|
||||
info.alternate_skill = oper_info.get("alternate_skill", 0);
|
||||
info.skill_usage = static_cast<BattleSkillUsage>(oper_info.get("skill_usage", 1));
|
||||
info.alternate_skill_usage = static_cast<BattleSkillUsage>(oper_info.get("alternate_skill_usage", 1));
|
||||
if (auto opt = oper_info.find<json::array>("recruit_priority_offset")) {
|
||||
for (const auto& offset : opt.value()) {
|
||||
std::pair<int, int> offset_pair = std::make_pair(offset[0].as_integer(), offset[1].as_integer());
|
||||
info.recruit_priority_offset.emplace_back(offset_pair);
|
||||
}
|
||||
}
|
||||
|
||||
m_all_opers.emplace(name, std::move(info));
|
||||
m_ordered_all_opers_name.emplace_back(name);
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace asst
|
||||
int promote_priority = 0; // 晋升优先级 (0-1000)
|
||||
int recruit_priority_when_team_full = 0; // 队伍满时的招募优先级 (0-1000)
|
||||
int promote_priority_when_team_full = 0; // 队伍满时的晋升优先级 (0-1000)
|
||||
std::vector<std::pair<int, int>> recruit_priority_offset;
|
||||
bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动)
|
||||
int skill = 0;
|
||||
int alternate_skill = 0;
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace asst
|
||||
static inline const std::string RoguelikeTraderNoLongerBuy = "RoguelikeNoLongerBuy";
|
||||
static inline const std::string RoguelikeSkillUsagePrefix = "RoguelikeSkillUsage-";
|
||||
static inline const std::string RoguelikeTeamFullWithoutRookie = "RoguelikeTeamFullWithoutRookie";
|
||||
static inline const std::string RoguelikeTeamRoles = "RoguelikeTeamRoles";
|
||||
static inline const std::string RoguelikeTheme = "RoguelikeTheme";
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Controller.h"
|
||||
#include "ImageAnalyzer/RoguelikeRecruitImageAnalyzer.h"
|
||||
#include "Resource/RoguelikeRecruitConfiger.h"
|
||||
#include "Resource/BattleDataConfiger.h"
|
||||
#include "RuntimeStatus.h"
|
||||
#include "TaskData.h"
|
||||
#include "Utils/Logger.hpp"
|
||||
@@ -33,6 +34,11 @@ bool asst::RoguelikeRecruitTaskPlugin::verify(AsstMsg msg, const json::value& de
|
||||
}
|
||||
}
|
||||
|
||||
std::string asst::RoguelikeRecruitTaskPlugin::get_oper_role(const std::string& name)
|
||||
{
|
||||
return std::to_string(BattleData.get_role(name));
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -56,6 +62,9 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
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::string teamroles_str =
|
||||
m_status->get_str(RuntimeStatus::RoguelikeTeamRoles).value_or(json::value().to_string());
|
||||
json::value teamroles = json::parse(teamroles_str).value_or(json::value());
|
||||
|
||||
// 候选干员
|
||||
std::vector<RoguelikeRecruitInfo> recruit_list;
|
||||
@@ -153,6 +162,13 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
Log.trace(__FUNCTION__, "| Ignored low level oper:", oper_info.name, oper_info.elite,
|
||||
oper_info.level);
|
||||
}
|
||||
int role_num = teamroles.get(get_oper_role(oper_info.name), 0);
|
||||
for (const auto& offset_pair : ranges::reverse_view(recruit_info.recruit_priority_offset)) {
|
||||
if (role_num >= offset_pair.first) {
|
||||
priority += offset_pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 优先级为0,可能练度不够被忽略
|
||||
@@ -246,7 +262,7 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& char_name = selected_oper->name;
|
||||
std::string char_name = selected_oper->name;
|
||||
Log.trace(__FUNCTION__, "| Top priority oper:", char_name, selected_oper->priority, "page",
|
||||
selected_oper->page_index, "/", i);
|
||||
|
||||
@@ -355,6 +371,18 @@ void asst::RoguelikeRecruitTaskPlugin::select_oper(const BattleRecruitOperInfo&
|
||||
{ "level", oper.level },
|
||||
};
|
||||
m_status->set_str(RuntimeStatus::RoguelikeCharOverview, overview.to_string());
|
||||
|
||||
auto& recruit_info = RoguelikeRecruit.get_oper_info(oper.name);
|
||||
if (recruit_info.name.empty() || recruit_info.is_alternate) {
|
||||
return;
|
||||
}
|
||||
std::string teamroles_str =
|
||||
m_status->get_str(RuntimeStatus::RoguelikeTeamRoles).value_or(json::value().to_string());
|
||||
json::value teamroles = json::parse(teamroles_str).value_or(json::value());
|
||||
std::string role_name = get_oper_role(oper.name);
|
||||
int role_num = teamroles.get(role_name, 0) + 1;
|
||||
teamroles[std::move(role_name)] = role_num;
|
||||
m_status->set_str(RuntimeStatus::RoguelikeTeamRoles, teamroles.to_string());
|
||||
}
|
||||
|
||||
void asst::RoguelikeRecruitTaskPlugin::swipe_to_the_left_of_operlist(int loop_times)
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace asst
|
||||
|
||||
private:
|
||||
bool check_core_char();
|
||||
std::string get_oper_role(const std::string& name);
|
||||
// 招募指定干员
|
||||
//
|
||||
// 输入参数:
|
||||
|
||||
@@ -20,7 +20,7 @@ bool asst::RoguelikeResetTaskPlugin::verify(AsstMsg msg, const json::value& deta
|
||||
if (task_view.starts_with(roguelike_name)) {
|
||||
task_view.remove_prefix(roguelike_name.length());
|
||||
}
|
||||
if (task_view == "Roguelike@Start") {
|
||||
if (task_view == "Roguelike@StartExplore") {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -95,6 +95,64 @@ namespace asst
|
||||
Drone
|
||||
};
|
||||
|
||||
inline BattleRole get_role_type(const std::string& role_name)
|
||||
{
|
||||
static const std::unordered_map<std::string, BattleRole> NameToRole = {
|
||||
{ "warrior", BattleRole::Warrior }, { "WARRIOR", BattleRole::Warrior },
|
||||
{ "Warrior", BattleRole::Warrior }, { "近卫", BattleRole::Warrior },
|
||||
|
||||
{ "pioneer", BattleRole::Pioneer }, { "PIONEER", BattleRole::Pioneer },
|
||||
{ "Pioneer", BattleRole::Pioneer }, { "先锋", BattleRole::Pioneer },
|
||||
|
||||
{ "medic", BattleRole::Medic }, { "MEDIC", BattleRole::Medic },
|
||||
{ "Medic", BattleRole::Medic }, { "医疗", BattleRole::Medic },
|
||||
|
||||
{ "tank", BattleRole::Tank }, { "TANK", BattleRole::Tank },
|
||||
{ "Tank", BattleRole::Tank }, { "重装", BattleRole::Tank },
|
||||
|
||||
{ "sniper", BattleRole::Sniper }, { "SNIPER", BattleRole::Sniper },
|
||||
{ "Sniper", BattleRole::Sniper }, { "狙击", BattleRole::Sniper },
|
||||
|
||||
{ "caster", BattleRole::Caster }, { "CASTER", BattleRole::Caster },
|
||||
{ "Caster", BattleRole::Caster }, { "术师", BattleRole::Caster },
|
||||
|
||||
{ "support", BattleRole::Support }, { "SUPPORT", BattleRole::Support },
|
||||
{ "Support", BattleRole::Support }, { "辅助", BattleRole::Support },
|
||||
|
||||
{ "special", BattleRole::Special }, { "SPECIAL", BattleRole::Special },
|
||||
{ "Special", BattleRole::Special }, { "特种", BattleRole::Special },
|
||||
|
||||
{ "drone", BattleRole::Drone }, { "DRONE", BattleRole::Drone },
|
||||
{ "Drone", BattleRole::Drone }, { "无人机", BattleRole::Drone },
|
||||
};
|
||||
if (auto iter = NameToRole.find(role_name); iter != NameToRole.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return BattleRole::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
inline std::string to_string(const asst::BattleRole& role)
|
||||
{
|
||||
static const std::unordered_map<asst::BattleRole, std::string> RoleToName = {
|
||||
{ asst::BattleRole::Warrior, "Warrior" },
|
||||
{ asst::BattleRole::Pioneer, "Pioneer" },
|
||||
{ asst::BattleRole::Medic, "Medic" },
|
||||
{ asst::BattleRole::Tank, "Tank" },
|
||||
{ asst::BattleRole::Sniper, "Sniper" },
|
||||
{ asst::BattleRole::Caster, "Caster" },
|
||||
{ asst::BattleRole::Support, "Support" },
|
||||
{ asst::BattleRole::Special, "Special" },
|
||||
{ asst::BattleRole::Drone, "Drone" },
|
||||
{ asst::BattleRole::Unknown, "Unknown" }
|
||||
};
|
||||
return RoleToName.at(role);
|
||||
}
|
||||
}
|
||||
|
||||
namespace asst {
|
||||
struct BattleRealTimeOper
|
||||
{
|
||||
int cost = 0;
|
||||
|
||||
Reference in New Issue
Block a user