mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 18:20:39 +08:00
fix: 修复肉鸽阵容完备度中单个干员重复出现于多个干员组的问题 (#10806)
* fix: 修复肉鸽阵容完备度中单个干员重复出现于多个干员组的问题 * style: clang
This commit is contained in:
@@ -6,50 +6,94 @@
|
||||
|
||||
#include "Utils/Logger.hpp"
|
||||
|
||||
const asst::RoguelikeOperInfo& asst::RoguelikeRecruitConfig::get_oper_info(const std::string& theme,
|
||||
const std::string& name) noexcept
|
||||
const asst::RoguelikeOperInfo&
|
||||
asst::RoguelikeRecruitConfig::get_oper_info(const std::string& theme, const std::string& oper_name) noexcept
|
||||
{
|
||||
auto& opers = m_all_opers.at(theme);
|
||||
if (opers.contains(name)) {
|
||||
return opers.at(name);
|
||||
if (opers.contains(oper_name)) {
|
||||
return opers.at(oper_name);
|
||||
}
|
||||
else {
|
||||
RoguelikeOperInfo info;
|
||||
info.name = name;
|
||||
info.group_id = get_group_id(theme, name);
|
||||
opers.emplace(name, std::move(info));
|
||||
return opers.at(name);
|
||||
info.name = oper_name;
|
||||
info.group_id = get_group_ids_of_oper(theme, oper_name);
|
||||
opers.emplace(oper_name, std::move(info));
|
||||
return opers.at(oper_name);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> asst::RoguelikeRecruitConfig::get_group_info(const std::string& theme) const noexcept
|
||||
const asst::RoguelikeGroupInfo&
|
||||
asst::RoguelikeRecruitConfig::get_group_info(const std::string& theme, const std::string& group_name) noexcept
|
||||
{
|
||||
auto& groups = m_all_groups.at(theme);
|
||||
if (groups.contains(group_name)) {
|
||||
return groups.at(group_name);
|
||||
}
|
||||
else {
|
||||
RoguelikeGroupInfo info;
|
||||
info.name = group_name;
|
||||
groups.emplace(group_name, std::move(info));
|
||||
return groups.at(group_name);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> asst::RoguelikeRecruitConfig::get_group_names(const std::string& theme) const noexcept
|
||||
{
|
||||
return m_oper_groups.at(theme);
|
||||
}
|
||||
|
||||
const std::vector<asst::RecruitPriorityOffset> asst::RoguelikeRecruitConfig::get_team_complete_info(
|
||||
const std::string& theme) const noexcept
|
||||
const std::vector<asst::RecruitPriorityOffset>
|
||||
asst::RoguelikeRecruitConfig::get_team_complete_info(const std::string& theme) const noexcept
|
||||
{
|
||||
return m_team_complete_comdition.at(theme);
|
||||
return m_team_complete_condition.at(theme);
|
||||
}
|
||||
|
||||
std::vector<int> asst::RoguelikeRecruitConfig::get_group_id(const std::string& theme,
|
||||
const std::string& name) const noexcept
|
||||
std::vector<int> asst::RoguelikeRecruitConfig::get_group_ids_of_oper(
|
||||
const std::string& theme,
|
||||
const std::string& oper_name) const noexcept
|
||||
{
|
||||
auto& opers = m_all_opers.at(theme);
|
||||
if (auto find_iter = opers.find(name); find_iter != opers.cend()) {
|
||||
if (auto find_iter = opers.find(oper_name); find_iter != opers.cend()) {
|
||||
return find_iter->second.group_id;
|
||||
}
|
||||
else {
|
||||
const auto& role = BattleData.get_role(name);
|
||||
const auto& role = BattleData.get_role(oper_name);
|
||||
if (role != battle::Role::Pioneer && role != battle::Role::Tank && role != battle::Role::Warrior &&
|
||||
role != battle::Role::Special)
|
||||
role != battle::Role::Special) {
|
||||
return { static_cast<int>(m_oper_groups.at(theme).size()) - 2 };
|
||||
else
|
||||
}
|
||||
else {
|
||||
return { static_cast<int>(m_oper_groups.at(theme).size()) - 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int asst::RoguelikeRecruitConfig::get_group_id_from_name(
|
||||
const std::string& theme,
|
||||
const std::string& group_name) noexcept
|
||||
{
|
||||
auto& groups = m_all_groups.at(theme);
|
||||
if (groups.contains(group_name)) {
|
||||
return groups.at(group_name).id;
|
||||
}
|
||||
else {
|
||||
RoguelikeGroupInfo info;
|
||||
info.name = group_name;
|
||||
groups.emplace(group_name, std::move(info));
|
||||
return groups.at(group_name).id;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string
|
||||
asst::RoguelikeRecruitConfig::get_group_name_from_id(const std::string& theme, const int group_id) const noexcept
|
||||
{
|
||||
auto it = ranges::find_if(m_all_groups.at(theme), [&](const auto& group) { return group.second.id == group_id; });
|
||||
if (it != m_all_groups.at(theme).end()) {
|
||||
return it->first;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRecruitConfig::parse(const json::value& json)
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -61,52 +105,60 @@ bool asst::RoguelikeRecruitConfig::parse(const json::value& json)
|
||||
int group_id = 0;
|
||||
//{"name":干员组名, "opers":组内干员组成的array}
|
||||
for (const auto& group_json : json.at("priority").as_array()) {
|
||||
m_oper_groups[theme].emplace_back(group_json.at("name").as_string());
|
||||
const std::string group_name = group_json.at("name").as_string();
|
||||
m_oper_groups[theme].emplace_back(group_name);
|
||||
|
||||
// 干员组信息
|
||||
RoguelikeGroupInfo group_info;
|
||||
group_info.name = group_name;
|
||||
group_info.id = group_id;
|
||||
|
||||
// 干员在组内的顺序,int
|
||||
int order_in_group = 0;
|
||||
// 遍历"opers"数组
|
||||
for (const auto& oper_info : group_json.at("opers").as_array()) {
|
||||
std::string name = oper_info.at("name").as_string();
|
||||
for (const auto& oper_json : group_json.at("opers").as_array()) {
|
||||
std::string name = oper_json.at("name").as_string();
|
||||
group_info.opers.emplace(name);
|
||||
// 肉鸽干员招募信息
|
||||
RoguelikeOperInfo info;
|
||||
RoguelikeOperInfo oper_info;
|
||||
auto iter = m_all_opers[theme].find(name);
|
||||
if (iter != m_all_opers[theme].end()) {
|
||||
// 干员已存在时仅做更新
|
||||
info = iter->second;
|
||||
oper_info = iter->second;
|
||||
}
|
||||
info.name = name;
|
||||
info.group_id.push_back(group_id);
|
||||
info.order_in_group[group_id] = order_in_group;
|
||||
info.recruit_priority = oper_info.get("recruit_priority", info.recruit_priority);
|
||||
info.promote_priority = oper_info.get("promote_priority", info.promote_priority);
|
||||
info.is_alternate = oper_info.get("is_alternate", info.is_alternate);
|
||||
info.skill = oper_info.get("skill", info.skill);
|
||||
info.alternate_skill = oper_info.get("alternate_skill", info.alternate_skill);
|
||||
info.skill_usage =
|
||||
static_cast<battle::SkillUsage>(oper_info.get("skill_usage", static_cast<int>(info.skill_usage)));
|
||||
info.skill_times = oper_info.get("skill_times", info.skill_times);
|
||||
info.alternate_skill_usage = static_cast<battle::SkillUsage>(
|
||||
oper_info.get("alternate_skill_usage", static_cast<int>(info.alternate_skill_usage)));
|
||||
info.alternate_skill_times = oper_info.get("alternate_skill_times", info.alternate_skill_times);
|
||||
info.is_key = oper_info.get("is_key", info.is_key);
|
||||
info.is_start = oper_info.get("is_start", info.is_start);
|
||||
info.auto_retreat = oper_info.get("auto_retreat", info.auto_retreat);
|
||||
oper_info.name = name;
|
||||
oper_info.group_id.push_back(group_id);
|
||||
oper_info.order_in_group[group_id] = order_in_group;
|
||||
oper_info.recruit_priority = oper_json.get("recruit_priority", oper_info.recruit_priority);
|
||||
oper_info.promote_priority = oper_json.get("promote_priority", oper_info.promote_priority);
|
||||
oper_info.is_alternate = oper_json.get("is_alternate", oper_info.is_alternate);
|
||||
oper_info.skill = oper_json.get("skill", oper_info.skill);
|
||||
oper_info.alternate_skill = oper_json.get("alternate_skill", oper_info.alternate_skill);
|
||||
oper_info.skill_usage =
|
||||
static_cast<battle::SkillUsage>(oper_json.get("skill_usage", static_cast<int>(oper_info.skill_usage)));
|
||||
oper_info.skill_times = oper_json.get("skill_times", oper_info.skill_times);
|
||||
oper_info.alternate_skill_usage = static_cast<battle::SkillUsage>(
|
||||
oper_json.get("alternate_skill_usage", static_cast<int>(oper_info.alternate_skill_usage)));
|
||||
oper_info.alternate_skill_times = oper_json.get("alternate_skill_times", oper_info.alternate_skill_times);
|
||||
oper_info.is_key = oper_json.get("is_key", oper_info.is_key);
|
||||
oper_info.is_start = oper_json.get("is_start", oper_info.is_start);
|
||||
oper_info.auto_retreat = oper_json.get("auto_retreat", oper_info.auto_retreat);
|
||||
|
||||
// __________________will-be-removed-begin__________________
|
||||
info.recruit_priority_when_team_full =
|
||||
oper_info.get("recruit_priority_when_team_full", info.recruit_priority - 100);
|
||||
info.promote_priority_when_team_full =
|
||||
oper_info.get("promote_priority_when_team_full", info.promote_priority + 300);
|
||||
if (auto opt = oper_info.find<json::array>("recruit_priority_offset")) {
|
||||
oper_info.recruit_priority_when_team_full =
|
||||
oper_json.get("recruit_priority_when_team_full", oper_info.recruit_priority - 100);
|
||||
oper_info.promote_priority_when_team_full =
|
||||
oper_json.get("promote_priority_when_team_full", oper_info.promote_priority + 300);
|
||||
if (auto opt = oper_json.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);
|
||||
oper_info.recruit_priority_offset.emplace_back(offset_pair);
|
||||
}
|
||||
}
|
||||
info.offset_melee = oper_info.get("offset_melee", false);
|
||||
oper_info.offset_melee = oper_json.get("offset_melee", false);
|
||||
// __________________will-be-removed-end__________________
|
||||
|
||||
if (auto opt = oper_info.find<json::array>("recruit_priority_offsets")) {
|
||||
if (auto opt = oper_json.find<json::array>("recruit_priority_offsets")) {
|
||||
for (const auto& offset_json : opt.value()) {
|
||||
RecruitPriorityOffset offset;
|
||||
for (const auto& group : offset_json.at("groups").as_array()) {
|
||||
@@ -115,40 +167,63 @@ bool asst::RoguelikeRecruitConfig::parse(const json::value& json)
|
||||
offset.threshold = offset_json.get("threshold", 0);
|
||||
offset.is_less = offset_json.get("is_less", false);
|
||||
offset.offset = offset_json.get("offset", 0);
|
||||
info.recruit_priority_offsets.emplace_back(std::move(offset));
|
||||
oper_info.recruit_priority_offsets.emplace_back(std::move(offset));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto opt = oper_info.find<json::array>("collection_priority_offsets")) {
|
||||
if (auto opt = oper_json.find<json::array>("collection_priority_offsets")) {
|
||||
for (const auto& offset_json : opt.value()) {
|
||||
CollectionPriorityOffset offset;
|
||||
offset.collection = offset_json.get("collection", "");
|
||||
offset.offset = offset_json.get("offset", 0);
|
||||
info.collection_priority_offsets.emplace_back(std::move(offset));
|
||||
oper_info.collection_priority_offsets.emplace_back(std::move(offset));
|
||||
}
|
||||
}
|
||||
|
||||
m_all_opers[theme][name] = std::move(info);
|
||||
m_all_opers[theme][name] = std::move(oper_info);
|
||||
order_in_group++;
|
||||
}
|
||||
m_all_groups[theme][group_name] = std::move(group_info);
|
||||
group_id++;
|
||||
}
|
||||
|
||||
// 对所有存在 offset 组的干员进行初始化
|
||||
for (auto& [oper_name, oper_info] : m_all_opers[theme]) { // 所有干员
|
||||
if (oper_info.recruit_priority_offsets.empty()) {
|
||||
continue;
|
||||
}
|
||||
for (auto& offset : oper_info.recruit_priority_offsets) { // 干员的所有 offset 策略组
|
||||
for (auto& group : offset.groups) { // 策略组内的每个干员组
|
||||
offset.opers.insert( // 向当前策略组计入这个干员组的无重复干员
|
||||
get_group_info(theme, group).opers.begin(),
|
||||
get_group_info(theme, group).opers.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& condition_json : json.at("team_complete_condition").as_array()) {
|
||||
RecruitPriorityOffset condition;
|
||||
for (const auto& group : condition_json.at("groups").as_array()) {
|
||||
condition.groups.emplace_back(group.as_string());
|
||||
std::string group_name = group.as_string();
|
||||
condition.groups.emplace_back(group_name);
|
||||
|
||||
condition.opers.insert( // 向当前策略组计入这个干员组的无重复干员
|
||||
get_group_info(theme, group_name).opers.begin(),
|
||||
get_group_info(theme, group_name).opers.end());
|
||||
}
|
||||
condition.threshold = condition_json.at("threshold").as_integer();
|
||||
m_team_complete_comdition[theme].emplace_back(std::move(condition));
|
||||
m_team_complete_require[theme] += condition.threshold;
|
||||
m_team_complete_condition[theme].emplace_back(std::move(condition));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::RoguelikeRecruitConfig::clear(const std::string& key)
|
||||
void asst::RoguelikeRecruitConfig::clear(const std::string& theme)
|
||||
{
|
||||
m_all_opers.erase(key);
|
||||
m_oper_groups.erase(key);
|
||||
m_team_complete_comdition.erase(key);
|
||||
m_all_opers.erase(theme);
|
||||
m_all_groups.erase(theme);
|
||||
m_oper_groups.erase(theme);
|
||||
m_team_complete_condition.erase(theme);
|
||||
m_team_complete_require.erase(theme);
|
||||
}
|
||||
|
||||
@@ -11,67 +11,97 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
// 招募priority影响因子
|
||||
struct RecruitPriorityOffset
|
||||
// 招募 priority 影响因子
|
||||
struct RecruitPriorityOffset
|
||||
{
|
||||
std::vector<std::string> groups; // 造成优先级改变的所有干员组
|
||||
std::unordered_set<std::string> opers; // 所有干员组的所有无重复干员
|
||||
int threshold = 0; // 已拥有组内干员数量阈值,达到此阈值时应用优先级改变
|
||||
bool is_less = false; // 高于阈值时改变还是低于阈值时改变
|
||||
int offset = 0; // 优先级改变的大小
|
||||
};
|
||||
|
||||
// 收藏品 priority 影响因子
|
||||
struct CollectionPriorityOffset
|
||||
{
|
||||
std::string collection; // 造成优先级改变的藏品名称
|
||||
int offset = 0; // 优先级改变的大小
|
||||
};
|
||||
|
||||
// 干员信息,战斗相关
|
||||
struct RoguelikeOperInfo
|
||||
{
|
||||
std::string name; // 干员名
|
||||
std::vector<int> group_id = {}; // 干员组 id,允许一个干员存在于多个干员组
|
||||
std::unordered_map<int, int> order_in_group; // 干员在干员组内的顺序 干员组 id:干员组内顺序
|
||||
int recruit_priority = 0; // 招募优先级 (0-1000)
|
||||
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; // [deprecated]
|
||||
bool offset_melee = false; // [deprecated]
|
||||
bool is_key = false; // 是否为核心干员
|
||||
bool is_start = false; // 是否为开局干员
|
||||
std::vector<RecruitPriorityOffset> recruit_priority_offsets; // 招募 priority 影响因子
|
||||
std::vector<CollectionPriorityOffset> collection_priority_offsets; // 收藏品 priority 影响因子
|
||||
bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动)
|
||||
int skill = 0; // 使用几技能
|
||||
int alternate_skill = 0; // 当没有指定技能时使用的备选技能,一般是6星干员未精二且精二后使用3技能时才需要指定
|
||||
battle::SkillUsage skill_usage = battle::SkillUsage::Possibly; // 技能使用模式
|
||||
int skill_times = 1; // 技能使用次数
|
||||
battle::SkillUsage alternate_skill_usage = battle::SkillUsage::Possibly; // 备选技能使用模式
|
||||
int alternate_skill_times = 1; // 备选技能使用次数
|
||||
int auto_retreat = 0; // 部署几秒后自动撤退
|
||||
};
|
||||
|
||||
// 干员组信息,招募相关
|
||||
struct RoguelikeGroupInfo
|
||||
{
|
||||
std::string name; // 干员组名
|
||||
std::unordered_set<std::string> opers; // 干员组的所有干员名(用于查重)
|
||||
int id = -1; // 干员组 id
|
||||
|
||||
// 干员组的所有干员,后续可以基于此重构干员信息存储、作战部署
|
||||
std::vector<std::shared_ptr<RoguelikeOperInfo>> opers_in_order; // 未初始化,暂不可用
|
||||
};
|
||||
|
||||
class RoguelikeRecruitConfig final : public SingletonHolder<RoguelikeRecruitConfig>, public AbstractConfig
|
||||
{
|
||||
public:
|
||||
virtual ~RoguelikeRecruitConfig() override = default;
|
||||
|
||||
const RoguelikeOperInfo& get_oper_info(const std::string& theme, const std::string& oper_name) noexcept;
|
||||
const RoguelikeGroupInfo& get_group_info(const std::string& theme, const std::string& group_name) noexcept;
|
||||
const std::vector<RecruitPriorityOffset> get_team_complete_info(const std::string& theme) const noexcept;
|
||||
std::vector<int> get_group_ids_of_oper(const std::string& theme, const std::string& oper_name)
|
||||
const noexcept; // renamed from "get_group_id"
|
||||
const std::vector<std::string> get_group_names(const std::string& theme)
|
||||
const noexcept; // 获取该肉鸽内用到的干员组[干员组1,干员组2, ...], renamed from "get_group_info"
|
||||
int get_group_id_from_name(
|
||||
const std::string& theme,
|
||||
const std::string& group_name) noexcept; // 用干员组名获取干员组 id
|
||||
const std::string
|
||||
get_group_name_from_id(const std::string& theme, const int group_id) const noexcept; // 用干员组 id 获取干员组名
|
||||
|
||||
int get_team_complete_require(const std::string& theme) noexcept
|
||||
{
|
||||
std::vector<std::string> groups; // 造成优先级改变的干员组
|
||||
int threshold = 0; // 已拥有组内干员数量阈值,达到此阈值时应用优先级改变
|
||||
bool is_less = false; // 高于阈值时改变还是低于阈值时改变
|
||||
int offset = 0; // 优先级改变的大小
|
||||
};
|
||||
return m_team_complete_require[theme];
|
||||
} // 所有完备度要求的数值总和
|
||||
|
||||
// 收藏品priority影响因子
|
||||
struct CollectionPriorityOffset
|
||||
{
|
||||
std::string collection; // 造成优先级改变的藏品名称
|
||||
int offset = 0; // 优先级改变的大小
|
||||
};
|
||||
// 干员信息,战斗相关
|
||||
struct RoguelikeOperInfo
|
||||
{
|
||||
std::string name;
|
||||
std::vector<int> group_id = {}; // 干员组id,允许一个干员存在于多个干员组
|
||||
std::unordered_map<int, int> order_in_group; // 干员在干员组内的顺序 干员组id:干员组内顺序
|
||||
int recruit_priority = 0; // 招募优先级 (0-1000)
|
||||
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; // [deprecated]
|
||||
bool offset_melee = false; // [deprecated]
|
||||
bool is_key = false; // 是否为核心干员
|
||||
bool is_start = false; // 是否为开局干员
|
||||
std::vector<RecruitPriorityOffset> recruit_priority_offsets; // 招募priority影响因子
|
||||
std::vector<CollectionPriorityOffset> collection_priority_offsets; // 收藏品priority影响因子
|
||||
bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动)
|
||||
int skill = 0; // 使用几技能
|
||||
int alternate_skill = 0; // 当没有指定技能时使用的备选技能,一般是6星干员未精二且精二后使用3技能时才需要指定
|
||||
battle::SkillUsage skill_usage = battle::SkillUsage::Possibly; // 技能使用模式
|
||||
int skill_times = 1; // 技能使用次数
|
||||
battle::SkillUsage alternate_skill_usage = battle::SkillUsage::Possibly; // 备选技能使用模式
|
||||
int alternate_skill_times = 1; // 备选技能使用次数
|
||||
int auto_retreat = 0; // 部署几秒后自动撤退
|
||||
};
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
class RoguelikeRecruitConfig final : public SingletonHolder<RoguelikeRecruitConfig>, public AbstractConfig
|
||||
{
|
||||
public:
|
||||
virtual ~RoguelikeRecruitConfig() override = default;
|
||||
void clear(const std::string& theme);
|
||||
|
||||
const RoguelikeOperInfo& get_oper_info(const std::string& theme, const std::string& name) noexcept;
|
||||
// 获取该肉鸽内用到的干员组[干员组1,干员组2,...]
|
||||
const std::vector<std::string> get_group_info(const std::string& theme) const noexcept;
|
||||
const std::vector<RecruitPriorityOffset> get_team_complete_info(const std::string& theme) const noexcept;
|
||||
std::vector<int> get_group_id(const std::string& theme, const std::string& name) const noexcept;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, RoguelikeOperInfo>>
|
||||
m_all_opers; // <theme, <oper_name, oper_info>>
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, RoguelikeGroupInfo>>
|
||||
m_all_groups; // <theme, <group_name, group_info>>
|
||||
std::unordered_map<std::string, std::vector<std::string>>
|
||||
m_oper_groups; // <theme, group_names> 保留此变量以保证可能用到的有序性
|
||||
std::unordered_map<std::string, std::vector<RecruitPriorityOffset>> m_team_complete_condition; // <theme, offsets>
|
||||
std::unordered_map<std::string, int> m_team_complete_require; // <theme, require> 阵容完备所需干员总数
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
void clear(const std::string& theme);
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, RoguelikeOperInfo>> m_all_opers;
|
||||
std::unordered_map<std::string, std::vector<std::string>> m_oper_groups;
|
||||
std::unordered_map<std::string, std::vector<RecruitPriorityOffset>> m_team_complete_comdition;
|
||||
};
|
||||
|
||||
inline static auto& RoguelikeRecruit = RoguelikeRecruitConfig::get_instance();
|
||||
inline static auto& RoguelikeRecruit = RoguelikeRecruitConfig::get_instance();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ asst::RoguelikeBattleTaskPlugin::RoguelikeBattleTaskPlugin(
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const
|
||||
{
|
||||
if (msg != AsstMsg::SubTaskCompleted
|
||||
|| details.get("subtask", std::string()) != "ProcessTask") {
|
||||
if (msg != AsstMsg::SubTaskCompleted || details.get("subtask", std::string()) != "ProcessTask") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,8 +107,7 @@ void asst::RoguelikeBattleTaskPlugin::wait_until_start_button_clicked()
|
||||
.run();
|
||||
}
|
||||
|
||||
std::string
|
||||
asst::RoguelikeBattleTaskPlugin::oper_name_in_config(const battle::DeploymentOper& oper) const
|
||||
std::string asst::RoguelikeBattleTaskPlugin::oper_name_in_config(const battle::DeploymentOper& oper) const
|
||||
{
|
||||
if (oper.role == Role::Warrior && oper.name == "阿米娅") {
|
||||
return "阿米娅-WARRIOR"; // 在 BattleData.json 中有
|
||||
@@ -192,7 +190,8 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
|
||||
if (opt == std::nullopt) {
|
||||
opt = RoguelikeCopilot.get_stage_data(m_stage_name);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
opt = RoguelikeCopilot.get_stage_data(m_stage_name);
|
||||
}
|
||||
|
||||
@@ -202,10 +201,9 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
|
||||
m_blacklist_location = opt->blacklist_location;
|
||||
m_role_order = opt->role_order;
|
||||
m_force_deploy_direction = opt->force_deploy_direction;
|
||||
m_force_air_defense =
|
||||
AirDefenseData { .stop_blocking_deploy_num = opt->stop_deploy_blocking_num,
|
||||
.deploy_air_defense_num = opt->force_deploy_air_defense_num,
|
||||
.ban_medic = opt->force_ban_medic };
|
||||
m_force_air_defense = AirDefenseData { .stop_blocking_deploy_num = opt->stop_deploy_blocking_num,
|
||||
.deploy_air_defense_num = opt->force_deploy_air_defense_num,
|
||||
.ban_medic = opt->force_ban_medic };
|
||||
m_deploy_plan = opt->deploy_plan;
|
||||
m_retreat_plan = opt->retreat_plan;
|
||||
}
|
||||
@@ -224,10 +222,9 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
|
||||
}
|
||||
else {
|
||||
auto homes_pos = m_homes | views::transform(&ReplacementHome::location);
|
||||
auto invalid_homes_pos = homes_pos | views::filter([&](const auto& home_pos) {
|
||||
return !m_normal_tile_info.contains(home_pos);
|
||||
})
|
||||
| views::transform(&Point::to_string);
|
||||
auto invalid_homes_pos =
|
||||
homes_pos | views::filter([&](const auto& home_pos) { return !m_normal_tile_info.contains(home_pos); }) |
|
||||
views::transform(&Point::to_string);
|
||||
if (!invalid_homes_pos.empty()) {
|
||||
Log.error("No replacement homes point:", invalid_homes_pos);
|
||||
}
|
||||
@@ -249,14 +246,13 @@ bool asst::RoguelikeBattleTaskPlugin::calc_stage_info()
|
||||
return true;
|
||||
}
|
||||
|
||||
asst::battle::LocationType asst::RoguelikeBattleTaskPlugin::get_oper_location_type(
|
||||
const battle::DeploymentOper& oper) const
|
||||
asst::battle::LocationType
|
||||
asst::RoguelikeBattleTaskPlugin::get_oper_location_type(const battle::DeploymentOper& oper) const
|
||||
{
|
||||
return BattleData.get_location_type(oper_name_in_config(oper));
|
||||
}
|
||||
|
||||
asst::battle::OperPosition
|
||||
asst::RoguelikeBattleTaskPlugin::get_role_position(const battle::Role& role) const
|
||||
asst::battle::OperPosition asst::RoguelikeBattleTaskPlugin::get_role_position(const battle::Role& role) const
|
||||
{
|
||||
switch (role) {
|
||||
case battle::Role::Support:
|
||||
@@ -270,8 +266,7 @@ asst::battle::OperPosition
|
||||
return battle::OperPosition::Blocking;
|
||||
break;
|
||||
case battle::Role::Medic:
|
||||
return m_force_air_defense.ban_medic ? battle::OperPosition::None
|
||||
: battle::OperPosition::AirDefense;
|
||||
return m_force_air_defense.ban_medic ? battle::OperPosition::None : battle::OperPosition::AirDefense;
|
||||
break;
|
||||
case battle::Role::Special:
|
||||
case battle::Role::Drone:
|
||||
@@ -285,9 +280,7 @@ void asst::RoguelikeBattleTaskPlugin::cache_oper_elite_status()
|
||||
{
|
||||
const auto& oper_list = m_config->get_oper();
|
||||
for (const auto& oper : m_cur_deployment_opers) {
|
||||
m_oper_elite.emplace(
|
||||
oper.name,
|
||||
oper_list.contains(oper.name) ? oper_list.at(oper.name).elite : 0);
|
||||
m_oper_elite.emplace(oper.name, oper_list.contains(oper.name) ? oper_list.at(oper.name).elite : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,9 +311,7 @@ void asst::RoguelikeBattleTaskPlugin::set_position_full(const Point& loc, bool f
|
||||
}
|
||||
}
|
||||
|
||||
void asst::RoguelikeBattleTaskPlugin::set_position_full(
|
||||
const battle::DeploymentOper& oper,
|
||||
bool full)
|
||||
void asst::RoguelikeBattleTaskPlugin::set_position_full(const battle::DeploymentOper& oper, bool full)
|
||||
{
|
||||
set_position_full(get_oper_location_type(oper), full);
|
||||
}
|
||||
@@ -369,7 +360,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
// 构造当前地图的部署指令列表
|
||||
std::vector<DeployPlanInfo> deploy_plan_list;
|
||||
// 获取当前肉鸽的分组信息[干员组1名称,干员组2名称,...]
|
||||
const auto& groups = RoguelikeRecruit.get_group_info(m_config->get_theme());
|
||||
const auto& groups = RoguelikeRecruit.get_group_names(m_config->get_theme());
|
||||
// 获取当前肉鸽的分组内排名信息
|
||||
// const auto& group_rank = RoguelikeRecruit.get_group_rank(rogue_theme);
|
||||
for (const auto& oper : m_cur_deployment_opers) {
|
||||
@@ -383,8 +374,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
// 获取招募信息
|
||||
const auto& recruit_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), oper_name);
|
||||
// 获取会用到该干员的干员组[干员组1序号,干员组2序号,...]
|
||||
std::vector<int> group_ids =
|
||||
RoguelikeRecruit.get_group_id(m_config->get_theme(), oper_name);
|
||||
std::vector<int> group_ids = RoguelikeRecruit.get_group_ids_of_oper(m_config->get_theme(), oper_name);
|
||||
|
||||
for (const auto& group_id : group_ids) {
|
||||
// 当前干员组名,string类型
|
||||
@@ -393,12 +383,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
if (m_deploy_plan.contains(group_name)) {
|
||||
for (const auto& info : m_deploy_plan[group_name]) {
|
||||
if (m_kills < info.kill_lower_bound || m_kills > info.kill_upper_bound) {
|
||||
Log.debug(
|
||||
" deploy info",
|
||||
oper.name,
|
||||
"in group",
|
||||
group_name,
|
||||
"is waiting.");
|
||||
Log.debug(" deploy info", oper.name, "in group", group_name, "is waiting.");
|
||||
is_success = true; // 如果发现了待命干员,此函数最终返回true
|
||||
continue;
|
||||
}
|
||||
@@ -409,9 +394,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
// deploy_plan.oper_order_in_group = recruit_info.order_in_group.at(group_id);
|
||||
|
||||
deploy_plan.oper_order_in_group =
|
||||
recruit_info.order_in_group.empty()
|
||||
? INT_MAX
|
||||
: recruit_info.order_in_group.at(group_id);
|
||||
recruit_info.order_in_group.empty() ? INT_MAX : recruit_info.order_in_group.at(group_id);
|
||||
deploy_plan.rank = info.rank;
|
||||
deploy_plan.placed = info.location;
|
||||
deploy_plan.direction = info.direction;
|
||||
@@ -434,8 +417,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
}
|
||||
std::sort(deploy_plan_list.begin(), deploy_plan_list.end());
|
||||
for (const auto& deploy_plan : deploy_plan_list) {
|
||||
if (!m_used_tiles.contains(deploy_plan.placed)
|
||||
&& !m_blacklist_location.contains(deploy_plan.placed) /* 判断该位置是否已被占据 */) {
|
||||
if (!m_used_tiles.contains(deploy_plan.placed) &&
|
||||
!m_blacklist_location.contains(deploy_plan.placed) /* 判断该位置是否已被占据 */) {
|
||||
if (auto oper_it = ranges::find_if(
|
||||
m_cur_deployment_opers,
|
||||
[&](const auto& oper) { return oper.name == deploy_plan.oper_name; });
|
||||
@@ -455,8 +438,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_best_deploy()
|
||||
auto deployed_time = std::chrono::steady_clock::now();
|
||||
m_deployed_time.insert_or_assign(deploy_plan.oper_name, deployed_time);
|
||||
// 获取技能用法和使用次数
|
||||
const auto& oper_info =
|
||||
RoguelikeRecruit.get_oper_info(m_config->get_theme(), deploy_plan.oper_name);
|
||||
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), deploy_plan.oper_name);
|
||||
m_skill_usage[deploy_plan.oper_name] = oper_info.skill_usage;
|
||||
m_skill_times[deploy_plan.oper_name] = oper_info.skill_times;
|
||||
Log.trace(" best deploy is", deploy_plan.oper_name, "with rank", deploy_plan.rank);
|
||||
@@ -476,8 +458,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
|
||||
std::chrono::milliseconds(Config.get_options().roguelike_fight_screencap_interval);
|
||||
|
||||
// prevent our program from consuming too much CPU
|
||||
if (const auto now = std::chrono::steady_clock::now();
|
||||
prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
if (const auto now = std::chrono::steady_clock::now(); prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
Log.debug("Sleeping for framerate limit");
|
||||
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
|
||||
}
|
||||
@@ -502,8 +483,8 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
|
||||
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), name);
|
||||
auto iter = m_deployed_time.find(name);
|
||||
if (iter != m_deployed_time.end() && oper_info.auto_retreat > 0) {
|
||||
if (std::chrono::steady_clock::now() - m_deployed_time.at(name)
|
||||
>= oper_info.auto_retreat * std::chrono::seconds(1)) {
|
||||
if (std::chrono::steady_clock::now() - m_deployed_time.at(name) >=
|
||||
oper_info.auto_retreat * std::chrono::seconds(1)) {
|
||||
// 时间到了就撤退
|
||||
asst::BattleHelper::retreat_oper(name);
|
||||
m_deployed_time.erase(name);
|
||||
@@ -605,8 +586,7 @@ bool asst::RoguelikeBattleTaskPlugin::do_once()
|
||||
auto deployed_time = std::chrono::steady_clock::now();
|
||||
m_deployed_time.insert_or_assign(best_oper.name, deployed_time);
|
||||
// 获取技能用法和使用次数
|
||||
const auto& oper_info =
|
||||
RoguelikeRecruit.get_oper_info(m_config->get_theme(), best_oper.name);
|
||||
const auto& oper_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), best_oper.name);
|
||||
m_skill_usage[best_oper.name] = oper_info.skill_usage;
|
||||
m_skill_times[best_oper.name] = oper_info.skill_times;
|
||||
|
||||
@@ -633,8 +613,7 @@ void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(
|
||||
std::vector<size_t> contain_index;
|
||||
for (const Point& relative_pos : get_attack_range(oper, direction)) {
|
||||
Point absolute_pos = placed_loc + relative_pos;
|
||||
if (auto iter = m_blocking_for_home_index.find(absolute_pos);
|
||||
iter != m_blocking_for_home_index.end()) {
|
||||
if (auto iter = m_blocking_for_home_index.find(absolute_pos); iter != m_blocking_for_home_index.end()) {
|
||||
m_homes_status[iter->second].wait_medic = false;
|
||||
contain_index.emplace_back(iter->second);
|
||||
}
|
||||
@@ -659,8 +638,7 @@ void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(
|
||||
case OperPosition::AirDefense:
|
||||
if (!m_force_air_defense.has_finished_deploy_air_defense) {
|
||||
m_force_air_defense.has_deployed_air_defense_num++;
|
||||
if (m_force_air_defense.has_deployed_air_defense_num
|
||||
>= m_force_air_defense.deploy_air_defense_num) {
|
||||
if (m_force_air_defense.has_deployed_air_defense_num >= m_force_air_defense.deploy_air_defense_num) {
|
||||
m_force_air_defense.has_finished_deploy_air_defense = true;
|
||||
Log.info("FORCE RANGED OPER DEPLOY END");
|
||||
}
|
||||
@@ -670,8 +648,7 @@ void asst::RoguelikeBattleTaskPlugin::postproc_of_deployment_conditions(
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_force_air_defense.has_finished_deploy_air_defense
|
||||
&& position != OperPosition::AirDefense) {
|
||||
if (m_force_air_defense.has_finished_deploy_air_defense && position != OperPosition::AirDefense) {
|
||||
Log.info("FORCE RANGED OPER DEPLOY END");
|
||||
m_force_air_defense.has_finished_deploy_air_defense = true;
|
||||
}
|
||||
@@ -684,12 +661,7 @@ void asst::RoguelikeBattleTaskPlugin::check_drone_tiles()
|
||||
while ((!m_need_clear_tiles.empty()) && m_need_clear_tiles.top().placed_time < now_time) {
|
||||
const auto& placed_loc = m_need_clear_tiles.top().placed_loc;
|
||||
if (auto iter = m_used_tiles.find(placed_loc); iter != m_used_tiles.end()) {
|
||||
Log.info(
|
||||
"Drone at location (",
|
||||
placed_loc.x,
|
||||
",",
|
||||
placed_loc.y,
|
||||
") is recognized as retreated");
|
||||
Log.info("Drone at location (", placed_loc.x, ",", placed_loc.y, ") is recognized as retreated");
|
||||
set_position_full(placed_loc, false);
|
||||
m_battlefield_opers.erase(iter->second);
|
||||
m_used_tiles.erase(iter);
|
||||
@@ -783,20 +755,12 @@ std::optional<asst::battle::DeploymentOper> asst::RoguelikeBattleTaskPlugin::cal
|
||||
}
|
||||
}
|
||||
|
||||
bool use_air_defense = has_air_defense && !m_force_air_defense.has_finished_deploy_air_defense
|
||||
&& m_force_air_defense.has_deployed_blocking_num
|
||||
>= m_force_air_defense.stop_blocking_deploy_num
|
||||
&& !m_ranged_full;
|
||||
bool use_blocking =
|
||||
has_blocking && m_homes_status[m_cur_home_index].wait_blocking && !m_melee_full;
|
||||
bool use_air_defense =
|
||||
has_air_defense && !m_force_air_defense.has_finished_deploy_air_defense &&
|
||||
m_force_air_defense.has_deployed_blocking_num >= m_force_air_defense.stop_blocking_deploy_num && !m_ranged_full;
|
||||
bool use_blocking = has_blocking && m_homes_status[m_cur_home_index].wait_blocking && !m_melee_full;
|
||||
bool use_medic = has_medic && m_homes_status[m_cur_home_index].wait_medic && !m_ranged_full;
|
||||
Log.trace(
|
||||
"use_air_defense",
|
||||
use_air_defense,
|
||||
", use_blocking",
|
||||
use_blocking,
|
||||
", use_medic",
|
||||
use_medic);
|
||||
Log.trace("use_air_defense", use_air_defense, ", use_blocking", use_blocking, ", use_medic", use_medic);
|
||||
|
||||
std::vector<DeploymentOper> cur_available;
|
||||
for (const auto& oper : m_cur_deployment_opers) {
|
||||
@@ -805,9 +769,7 @@ std::optional<asst::battle::DeploymentOper> asst::RoguelikeBattleTaskPlugin::cal
|
||||
}
|
||||
}
|
||||
// 费用高的优先用,放前面
|
||||
ranges::sort(cur_available, [](const auto& lhs, const auto& rhs) {
|
||||
return lhs.cost > rhs.cost;
|
||||
});
|
||||
ranges::sort(cur_available, [](const auto& lhs, const auto& rhs) { return lhs.cost > rhs.cost; });
|
||||
|
||||
DeploymentOper best_oper;
|
||||
|
||||
@@ -889,8 +851,7 @@ void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
m_oper_elite.clear();
|
||||
}
|
||||
|
||||
std::vector<asst::Point>
|
||||
asst::RoguelikeBattleTaskPlugin::available_locations(const DeploymentOper& oper) const
|
||||
std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(const DeploymentOper& oper) const
|
||||
{
|
||||
auto type = get_oper_location_type(oper);
|
||||
if (type == LocationType::Invalid || type == LocationType::None) {
|
||||
@@ -899,18 +860,15 @@ std::vector<asst::Point>
|
||||
return available_locations(type);
|
||||
}
|
||||
|
||||
std::vector<asst::Point>
|
||||
asst::RoguelikeBattleTaskPlugin::available_locations(battle::LocationType type) const
|
||||
std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(battle::LocationType type) const
|
||||
{
|
||||
std::vector<Point> result;
|
||||
for (const auto& [loc, tile] : m_normal_tile_info) {
|
||||
bool position_matched =
|
||||
tile.buildable == type || tile.buildable == battle::LocationType::All;
|
||||
position_matched |= (type == battle::LocationType::All)
|
||||
&& (tile.buildable == battle::LocationType::Melee
|
||||
|| tile.buildable == battle::LocationType::Ranged);
|
||||
if (position_matched && tile.key != TilePack::TileKey::DeepSea
|
||||
&& // 水上要先放板子才能放人,肉鸽里也没板子,那就当作不可放置
|
||||
bool position_matched = tile.buildable == type || tile.buildable == battle::LocationType::All;
|
||||
position_matched |= (type == battle::LocationType::All) && (tile.buildable == battle::LocationType::Melee ||
|
||||
tile.buildable == battle::LocationType::Ranged);
|
||||
if (position_matched &&
|
||||
tile.key != TilePack::TileKey::DeepSea && // 水上要先放板子才能放人,肉鸽里也没板子,那就当作不可放置
|
||||
!m_used_tiles.contains(loc) && !m_blacklist_location.contains(loc)) {
|
||||
result.emplace_back(loc);
|
||||
}
|
||||
@@ -966,10 +924,8 @@ asst::battle::AttackRange asst::RoguelikeBattleTaskPlugin::get_attack_range(
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cur_direction : { DeployDirection::Right,
|
||||
DeployDirection::Up,
|
||||
DeployDirection::Left,
|
||||
DeployDirection::Down }) {
|
||||
for (auto cur_direction :
|
||||
{ DeployDirection::Right, DeployDirection::Up, DeployDirection::Left, DeployDirection::Down }) {
|
||||
if (cur_direction == direction) {
|
||||
break;
|
||||
}
|
||||
@@ -1023,12 +979,10 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo>
|
||||
// 取距离最近的N个点,计算分数。然后使用得分最高的点
|
||||
constexpr int CalcPointCount = 4;
|
||||
for (const auto& loc : available_loc | views::take(CalcPointCount)) {
|
||||
const auto& [cur_direction, cur_socre] =
|
||||
calc_best_direction_and_score(loc, oper, home.direction);
|
||||
const auto& [cur_direction, cur_socre] = calc_best_direction_and_score(loc, oper, home.direction);
|
||||
// 离得远的要扣分
|
||||
constexpr int DistWeights = -1050;
|
||||
int extra_dist =
|
||||
std::abs(loc.x - home.location.x) + std::abs(loc.y - home.location.y) - min_dist;
|
||||
int extra_dist = std::abs(loc.x - home.location.x) + std::abs(loc.y - home.location.y) - min_dist;
|
||||
int extra_dist_score = DistWeights * extra_dist;
|
||||
if (oper.role == battle::Role::Medic) { // 医疗干员离得远无所谓
|
||||
extra_dist_score = 0;
|
||||
@@ -1042,8 +996,7 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo>
|
||||
}
|
||||
|
||||
// 强制变化为确定的攻击方向
|
||||
if (auto iter = m_force_deploy_direction.find(best_location);
|
||||
iter != m_force_deploy_direction.end()) {
|
||||
if (auto iter = m_force_deploy_direction.find(best_location); iter != m_force_deploy_direction.end()) {
|
||||
if (iter->second.role.contains(oper.role)) {
|
||||
best_direction = iter->second.direction;
|
||||
}
|
||||
@@ -1052,11 +1005,10 @@ std::optional<asst::RoguelikeBattleTaskPlugin::DeployInfo>
|
||||
return DeployInfo { best_location, best_direction };
|
||||
}
|
||||
|
||||
asst::RoguelikeBattleTaskPlugin::DirectionAndScore
|
||||
asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score(
|
||||
Point loc,
|
||||
const battle::DeploymentOper& oper,
|
||||
DeployDirection recommended_direction) const
|
||||
asst::RoguelikeBattleTaskPlugin::DirectionAndScore asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score(
|
||||
Point loc,
|
||||
const battle::DeploymentOper& oper,
|
||||
DeployDirection recommended_direction) const
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
@@ -1112,10 +1064,8 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore
|
||||
int max_score = 0;
|
||||
DeployDirection best_direction = DeployDirection::None;
|
||||
|
||||
for (auto direction : { DeployDirection::Right,
|
||||
DeployDirection::Up,
|
||||
DeployDirection::Left,
|
||||
DeployDirection::Down }) {
|
||||
for (auto direction :
|
||||
{ DeployDirection::Right, DeployDirection::Up, DeployDirection::Left, DeployDirection::Down }) {
|
||||
int score = 0;
|
||||
// 按朝右算,后面根据方向做转换
|
||||
for (const Point& relative_pos : get_attack_range(oper, direction)) {
|
||||
@@ -1124,14 +1074,12 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore
|
||||
using TileKey = TilePack::TileKey;
|
||||
// 战斗干员朝向的权重
|
||||
static const std::unordered_map<TileKey, int> TileKeyFightWeights = {
|
||||
{ TileKey::Invalid, 0 }, { TileKey::Forbidden, 0 },
|
||||
{ TileKey::Wall, 500 }, { TileKey::Road, 1000 },
|
||||
{ TileKey::Home, 500 }, { TileKey::EnemyHome, 1000 },
|
||||
{ TileKey::Airport, 1000 }, { TileKey::Floor, 1000 },
|
||||
{ TileKey::Hole, 0 }, { TileKey::Telin, 700 },
|
||||
{ TileKey::Telout, 800 }, { TileKey::Grass, 500 },
|
||||
{ TileKey::DeepSea, 1000 }, { TileKey::Volcano, 1000 },
|
||||
{ TileKey::Healing, 1000 }, { TileKey::Fence, 800 },
|
||||
{ TileKey::Invalid, 0 }, { TileKey::Forbidden, 0 }, { TileKey::Wall, 500 },
|
||||
{ TileKey::Road, 1000 }, { TileKey::Home, 500 }, { TileKey::EnemyHome, 1000 },
|
||||
{ TileKey::Airport, 1000 }, { TileKey::Floor, 1000 }, { TileKey::Hole, 0 },
|
||||
{ TileKey::Telin, 700 }, { TileKey::Telout, 800 }, { TileKey::Grass, 500 },
|
||||
{ TileKey::DeepSea, 1000 }, { TileKey::Volcano, 1000 }, { TileKey::Healing, 1000 },
|
||||
{ TileKey::Fence, 800 },
|
||||
};
|
||||
// 治疗干员朝向的权重
|
||||
static const std::unordered_map<TileKey, int> TileKeyMedicWeights = {
|
||||
@@ -1146,19 +1094,16 @@ asst::RoguelikeBattleTaskPlugin::DirectionAndScore
|
||||
switch (oper.role) {
|
||||
case battle::Role::Medic:
|
||||
if (auto iter = m_used_tiles.find(absolute_pos);
|
||||
iter != m_used_tiles.cend()
|
||||
&& BattleData.get_role(iter->second)
|
||||
!= battle::Role::Drone) { // 根据哪个方向上人多决定朝向哪
|
||||
iter != m_used_tiles.cend() &&
|
||||
BattleData.get_role(iter->second) != battle::Role::Drone) { // 根据哪个方向上人多决定朝向哪
|
||||
score += 10000;
|
||||
}
|
||||
if (auto iter = m_side_tile_info.find(absolute_pos);
|
||||
iter != m_side_tile_info.end()) {
|
||||
if (auto iter = m_side_tile_info.find(absolute_pos); iter != m_side_tile_info.end()) {
|
||||
score += TileKeyMedicWeights.at(iter->second.key);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (auto iter = m_side_tile_info.find(absolute_pos);
|
||||
iter != m_side_tile_info.end()) {
|
||||
if (auto iter = m_side_tile_info.find(absolute_pos); iter != m_side_tile_info.end()) {
|
||||
score += TileKeyFightWeights.at(iter->second.key);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
bool asst::RoguelikeFormationTaskPlugin::verify(AsstMsg msg, const json::value& details) const
|
||||
{
|
||||
if (msg != AsstMsg::SubTaskCompleted
|
||||
|| details.get("subtask", std::string()) != "ProcessTask") {
|
||||
if (msg != AsstMsg::SubTaskCompleted || details.get("subtask", std::string()) != "ProcessTask") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,8 +72,7 @@ bool asst::RoguelikeFormationTaskPlugin::_run()
|
||||
}
|
||||
|
||||
auto& new_result = formation_analyzer.get_result();
|
||||
size_t new_selected_count =
|
||||
ranges::count_if(new_result, [](const auto& oper) { return oper.selected; });
|
||||
size_t new_selected_count = ranges::count_if(new_result, [](const auto& oper) { return oper.selected; });
|
||||
// 说明 select_count 计数没生效,即都没点上
|
||||
if (new_selected_count == pre_selected) {
|
||||
reselect = true;
|
||||
@@ -114,20 +112,16 @@ void asst::RoguelikeFormationTaskPlugin::clear_and_reselect()
|
||||
std::vector<asst::RoguelikeFormationImageAnalyzer::FormationOper> sorted_oper_list;
|
||||
std::unordered_set<std::string> oper_to_select; // 和上面的 vector 一致,用于快速查重
|
||||
|
||||
const auto& team_complete_condition =
|
||||
RoguelikeRecruit.get_team_complete_info(m_config->get_theme());
|
||||
const auto& group_list = RoguelikeRecruit.get_group_info(m_config->get_theme());
|
||||
const auto& team_complete_condition = RoguelikeRecruit.get_team_complete_info(m_config->get_theme());
|
||||
const auto& group_list = RoguelikeRecruit.get_group_names(m_config->get_theme());
|
||||
|
||||
for (const auto& condition : team_complete_condition) { // 优先选择阵容核心干员
|
||||
int count = 0;
|
||||
const int require = condition.threshold;
|
||||
for (const std::string& group_name : condition.groups) {
|
||||
auto group_filter = views::filter([&](const auto& oper) {
|
||||
const auto& group_ids =
|
||||
RoguelikeRecruit.get_group_id(m_config->get_theme(), oper.name);
|
||||
return ranges::any_of(group_ids, [&](int id) {
|
||||
return group_list[id] == group_name;
|
||||
});
|
||||
const auto& group_ids = RoguelikeRecruit.get_group_ids_of_oper(m_config->get_theme(), oper.name);
|
||||
return ranges::any_of(group_ids, [&](int id) { return group_list[id] == group_name; });
|
||||
});
|
||||
for (const auto& oper : oper_list | group_filter | views::take(require - count)) {
|
||||
if (!oper_to_select.contains(oper.name)) {
|
||||
@@ -143,12 +137,8 @@ void asst::RoguelikeFormationTaskPlugin::clear_and_reselect()
|
||||
}
|
||||
|
||||
std::erase_if(oper_list, [&](const auto& oper) { return oper_to_select.contains(oper.name); });
|
||||
ranges::stable_partition(oper_list, [](const auto& oper) {
|
||||
return !oper.name.starts_with("预备干员");
|
||||
});
|
||||
ranges::move(
|
||||
oper_list | views::take(13 - sorted_oper_list.size()),
|
||||
std::back_inserter(sorted_oper_list));
|
||||
ranges::stable_partition(oper_list, [](const auto& oper) { return !oper.name.starts_with("预备干员"); });
|
||||
ranges::move(oper_list | views::take(13 - sorted_oper_list.size()), std::back_inserter(sorted_oper_list));
|
||||
|
||||
for (const auto& oper : sorted_oper_list) {
|
||||
select(oper);
|
||||
@@ -174,9 +164,7 @@ bool asst::RoguelikeFormationTaskPlugin::analyze()
|
||||
|
||||
auto unique_filter = views::filter([&](const auto& oper) {
|
||||
// TODO: 这里没考虑多个相同预备干员的情况,不过影响应该不大
|
||||
return !ranges::any_of(oper_list, [&](const auto& existing_oper) {
|
||||
return oper.name == existing_oper.name;
|
||||
});
|
||||
return !ranges::any_of(oper_list, [&](const auto& existing_oper) { return oper.name == existing_oper.name; });
|
||||
});
|
||||
auto append_page_proj = views::transform([&](auto oper) {
|
||||
Log.info(__FUNCTION__, "oper: ", oper.name, " page: ", cur_page);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "RoguelikeRecruitTaskPlugin.h"
|
||||
|
||||
#include "Config/Miscellaneous/BattleDataConfig.h"
|
||||
#include "Config/Roguelike/RoguelikeRecruitConfig.h"
|
||||
#include "Config/TaskData.h"
|
||||
#include "Controller/Controller.h"
|
||||
#include "Status.h"
|
||||
@@ -47,11 +46,26 @@ asst::battle::Role asst::RoguelikeRecruitTaskPlugin::get_oper_role(const std::st
|
||||
bool asst::RoguelikeRecruitTaskPlugin::is_oper_melee(const std::string& name)
|
||||
{
|
||||
const auto role = get_oper_role(name);
|
||||
if (role != battle::Role::Pioneer && role != battle::Role::Tank && role != battle::Role::Warrior) return false;
|
||||
if (role != battle::Role::Pioneer && role != battle::Role::Tank && role != battle::Role::Warrior) {
|
||||
return false;
|
||||
}
|
||||
const auto loc = BattleData.get_location_type(name);
|
||||
return loc == battle::LocationType::Melee;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> asst::RoguelikeRecruitTaskPlugin::calculate_condition_oper(
|
||||
const RecruitPriorityOffset& condition,
|
||||
const std::unordered_map<std::string, RoguelikeOper>& chars_map)
|
||||
{
|
||||
std::unordered_set<std::string> opers; // 符合这个策略组的干员
|
||||
for (const auto& [oper_name, oper_level] : chars_map) {
|
||||
if (condition.opers.contains(oper_name)) {
|
||||
opers.insert(oper_name);
|
||||
}
|
||||
}
|
||||
return opers;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -88,50 +102,48 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
std::unordered_map<battle::Role, int> team_roles;
|
||||
int offset_melee_num = 0;
|
||||
for (const auto& [name, oper] : chars_map) {
|
||||
if (name.starts_with("预备干员")) continue;
|
||||
if (name.starts_with("预备干员")) {
|
||||
continue;
|
||||
}
|
||||
team_roles[battle::get_role_type(name)]++;
|
||||
if (is_oper_melee(name)) offset_melee_num++;
|
||||
}
|
||||
// __________________will-be-removed-end__________________
|
||||
|
||||
std::unordered_map<std::string, int> group_count;
|
||||
const auto& group_list = RoguelikeRecruit.get_group_info(m_config->get_theme());
|
||||
for (const auto& [name, oper] : chars_map) {
|
||||
std::vector<int> group_ids = RoguelikeRecruit.get_group_id(m_config->get_theme(), name);
|
||||
for (const auto& group_id : group_ids) {
|
||||
const std::string& group_name = group_list[group_id];
|
||||
group_count[group_name]++;
|
||||
if (is_oper_melee(name)) {
|
||||
offset_melee_num++;
|
||||
}
|
||||
}
|
||||
// __________________will-be-removed-end__________________
|
||||
|
||||
if (!m_starts_complete) {
|
||||
for (const auto& oper : chars_map) {
|
||||
auto& recruit_info = RoguelikeRecruit.get_oper_info(m_config->get_theme(), oper.first);
|
||||
if (recruit_info.is_start) m_starts_complete = true;
|
||||
if (recruit_info.is_start) {
|
||||
m_starts_complete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_team_complete) {
|
||||
bool complete = true;
|
||||
int complete_count = 0;
|
||||
int complete_require = 0;
|
||||
const auto& team_complete_condition = RoguelikeRecruit.get_team_complete_info(m_config->get_theme());
|
||||
for (const auto& condition : team_complete_condition) {
|
||||
int count = 0;
|
||||
complete_require += condition.threshold;
|
||||
for (const std::string& group_name : condition.groups) {
|
||||
count += group_count[group_name];
|
||||
complete_count += group_count[group_name];
|
||||
}
|
||||
if (count < condition.threshold) {
|
||||
for (const auto& condition : team_complete_condition) { // 每个完备度的策略组
|
||||
std::unordered_set<std::string> opers =
|
||||
calculate_condition_oper(condition, chars_map); // 符合这个策略组的干员
|
||||
|
||||
complete_count += int(opers.size());
|
||||
if (int(opers.size()) < condition.threshold) {
|
||||
complete = false;
|
||||
}
|
||||
|
||||
Log.trace("__FUNCTION__", "groups:", condition.groups);
|
||||
Log.trace("__FUNCTION__", "opers:", opers);
|
||||
}
|
||||
m_team_complete = complete;
|
||||
if (complete_count <= complete_require / 2 && m_recruit_count >= 10) {
|
||||
if (complete_count <= RoguelikeRecruit.get_team_complete_require(m_config->get_theme()) / 2 &&
|
||||
m_recruit_count >= 10) {
|
||||
// 如果第10次招募还没拿到半队key干员,说明账号阵容不齐,放开招募限制,有啥用啥吧
|
||||
m_team_complete = complete;
|
||||
m_team_complete = true;
|
||||
}
|
||||
Log.trace("__FUNCTION__", "complete_count:", complete_count, "m_team_complete:", m_team_complete);
|
||||
}
|
||||
|
||||
if (m_recruit_count >= 3 && !m_starts_complete) {
|
||||
@@ -180,7 +192,7 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
std::unordered_set<std::string> oper_names;
|
||||
const auto& oper_list = analyzer.get_result();
|
||||
bool stop_swipe = false;
|
||||
std::vector<std::string> owned_collection=m_config->get_collection();
|
||||
std::vector<std::string> owned_collection = m_config->get_collection();
|
||||
|
||||
int max_oper_x = 0;
|
||||
for (const auto& oper_info : oper_list) {
|
||||
@@ -199,9 +211,17 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
// 偏移低于阈值,代表划动没有效果,已经到达屏幕最右侧
|
||||
if (x_offset < 20 && y_offset < 5) {
|
||||
stop_swipe = true;
|
||||
Log.trace(__FUNCTION__, "| Page", i, "oper", oper_info.name,
|
||||
"last rect:", rect_it->second.to_string(), "current rect:", oper_info.rect.to_string(),
|
||||
" - stop swiping");
|
||||
Log.trace(
|
||||
__FUNCTION__,
|
||||
"| Page",
|
||||
i,
|
||||
"oper",
|
||||
oper_info.name,
|
||||
"last rect:",
|
||||
rect_it->second.to_string(),
|
||||
"current rect:",
|
||||
oper_info.rect.to_string(),
|
||||
" - stop swiping");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -248,8 +268,12 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
}
|
||||
else {
|
||||
// 精一55级以下,默认不招募
|
||||
Log.trace(__FUNCTION__, "| Ignored low level oper:", oper_info.name, oper_info.elite,
|
||||
oper_info.level);
|
||||
Log.trace(
|
||||
__FUNCTION__,
|
||||
"| Ignored low level oper:",
|
||||
oper_info.name,
|
||||
oper_info.elite,
|
||||
oper_info.level);
|
||||
}
|
||||
|
||||
if (temp_recruit_exist) { // 临时招募干员具有极高抓取优先级,但是在编队时会占用前面的位置
|
||||
@@ -277,29 +301,35 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
// }
|
||||
// __________________will-be-removed-end__________________
|
||||
for (const auto& priority_offset : recruit_info.recruit_priority_offsets) {
|
||||
int count = 0;
|
||||
for (const std::string& group_name : priority_offset.groups) {
|
||||
count += group_count[group_name];
|
||||
}
|
||||
std::unordered_set<std::string> opers = // 符合这个策略组的干员
|
||||
calculate_condition_oper(priority_offset, chars_map);
|
||||
if (priority_offset.is_less) {
|
||||
if (count <= priority_offset.threshold) priority += priority_offset.offset;
|
||||
if (int(opers.size()) <= priority_offset.threshold) {
|
||||
priority += priority_offset.offset;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (count >= priority_offset.threshold) priority += priority_offset.offset;
|
||||
if (int(opers.size()) >= priority_offset.threshold) {
|
||||
priority += priority_offset.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& priority_offset : recruit_info.collection_priority_offsets) {
|
||||
for (const auto& priority_offset : recruit_info.collection_priority_offsets) {
|
||||
auto iter =
|
||||
std::find(owned_collection.begin(), owned_collection.end(), priority_offset.collection);
|
||||
if (iter != owned_collection.end()) {
|
||||
priority += priority_offset.offset;
|
||||
}
|
||||
if (iter != owned_collection.end()) {
|
||||
priority += priority_offset.offset;
|
||||
}
|
||||
}
|
||||
if (!m_starts_complete) {
|
||||
if (!recruit_info.is_start) priority -= 1000;
|
||||
if (!recruit_info.is_start) {
|
||||
priority -= 1000;
|
||||
}
|
||||
}
|
||||
else if (!m_team_complete) {
|
||||
if (!recruit_info.is_key) priority -= 1000;
|
||||
if (!recruit_info.is_key) {
|
||||
priority -= 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,8 +340,9 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
}
|
||||
|
||||
// 添加到候选名单
|
||||
auto existing_it = ranges::find_if(
|
||||
recruit_list, [&](const RoguelikeRecruitInfo& pri) -> bool { return pri.name == recruit_info.name; });
|
||||
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;
|
||||
@@ -376,7 +407,7 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
if (info.elite != 2) {
|
||||
continue;
|
||||
}
|
||||
Log.trace(__FUNCTION__, "| Choose random elite 2:", info.name, info.elite, info.level);
|
||||
Log.trace(__FUNCTION__, "| Choose temporary recruitment elite 2:", info.name, info.elite, info.level);
|
||||
recruit_oper(info);
|
||||
return true;
|
||||
}
|
||||
@@ -403,8 +434,15 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
}
|
||||
|
||||
std::string char_name = selected_oper->name;
|
||||
Log.trace(__FUNCTION__, "| Top priority oper:", char_name, selected_oper->priority, "page",
|
||||
selected_oper->page_index, "/", i);
|
||||
Log.trace(
|
||||
__FUNCTION__,
|
||||
"| Top priority oper:",
|
||||
char_name,
|
||||
selected_oper->priority,
|
||||
"page",
|
||||
selected_oper->page_index,
|
||||
"/",
|
||||
i);
|
||||
|
||||
// 滑动方向
|
||||
// 页码大于一半: 从右往左划动
|
||||
@@ -426,8 +464,8 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
void asst::RoguelikeRecruitTaskPlugin::reset_in_run_variables()
|
||||
{
|
||||
m_recruit_count = 0;
|
||||
m_starts_complete = false;
|
||||
m_team_complete = false;
|
||||
m_starts_complete = false;
|
||||
m_team_complete = false;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRecruitTaskPlugin::lazy_recruit()
|
||||
@@ -438,7 +476,7 @@ bool asst::RoguelikeRecruitTaskPlugin::lazy_recruit()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeRecruitTaskPlugin::recruit_appointed_char(const std::string& char_name, bool is_rtl)
|
||||
bool asst::RoguelikeRecruitTaskPlugin::recruit_appointed_char(const std::string& char_name, bool is_rtl)
|
||||
{
|
||||
LogTraceFunction;
|
||||
// 最大滑动次数
|
||||
@@ -464,12 +502,15 @@ bool asst::RoguelikeRecruitTaskPlugin::lazy_recruit()
|
||||
if (analyzer.analyze()) {
|
||||
const auto& chars = analyzer.get_result();
|
||||
max_oper_x = ranges::max(chars | views::transform([&](const auto& x) { return x.rect.x; }));
|
||||
auto it = ranges::find_if(
|
||||
chars, [&](const battle::roguelike::Recruitment& oper) -> bool { return oper.name == char_name; });
|
||||
auto it = ranges::find_if(chars, [&](const battle::roguelike::Recruitment& oper) -> bool {
|
||||
return oper.name == char_name;
|
||||
});
|
||||
|
||||
std::unordered_set<std::string> oper_names;
|
||||
ranges::transform(chars, std::inserter(oper_names, oper_names.end()),
|
||||
std::mem_fn(&battle::roguelike::Recruitment::name));
|
||||
ranges::transform(
|
||||
chars,
|
||||
std::inserter(oper_names, oper_names.end()),
|
||||
std::mem_fn(&battle::roguelike::Recruitment::name));
|
||||
Log.info(__FUNCTION__, "| Oper list:", oper_names);
|
||||
|
||||
if (it != chars.cend()) {
|
||||
@@ -528,7 +569,9 @@ bool asst::RoguelikeRecruitTaskPlugin::recruit_support_char()
|
||||
auto core_opt = m_config->get_core_char();
|
||||
m_config->set_core_char("");
|
||||
if (!core_opt.empty()) {
|
||||
if (recruit_support_char(core_opt, MaxRefreshTimes)) return true;
|
||||
if (recruit_support_char(core_opt, MaxRefreshTimes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -566,17 +609,28 @@ bool asst::RoguelikeRecruitTaskPlugin::recruit_support_char(const std::string& n
|
||||
auto check_satisfy = [&use_nonfriend_support](const RecruitSupportCharInfo& chara) {
|
||||
return chara.is_friend || use_nonfriend_support;
|
||||
};
|
||||
std::copy_if(chars_page.begin(), chars_page.end(),
|
||||
std::inserter(satisfied_chars, std::begin(satisfied_chars)), check_satisfy);
|
||||
std::copy_if(
|
||||
chars_page.begin(),
|
||||
chars_page.end(),
|
||||
std::inserter(satisfied_chars, std::begin(satisfied_chars)),
|
||||
check_satisfy);
|
||||
|
||||
if (satisfied_chars.size()) break;
|
||||
if (satisfied_chars.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (page != MaxPageCnt - 1) {
|
||||
ProcessTask(*this, { "RoguelikeSupportSwipeRight" }).run();
|
||||
}
|
||||
if (page != MaxPageCnt - 1) ProcessTask(*this, { "RoguelikeSupportSwipeRight" }).run();
|
||||
}
|
||||
if (satisfied_chars.size()) break;
|
||||
if (satisfied_chars.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 刷新助战
|
||||
if (retry >= max_refresh) break;
|
||||
if (retry >= max_refresh) {
|
||||
break;
|
||||
}
|
||||
auto screen_refresh = ctrler()->get_image();
|
||||
RoguelikeRecruitSupportAnalyzer analyzer_refresh(screen_refresh);
|
||||
analyzer_refresh.set_mode(SupportAnalyzeMode::RefreshSupportBtn);
|
||||
@@ -585,7 +639,9 @@ bool asst::RoguelikeRecruitTaskPlugin::recruit_support_char(const std::string& n
|
||||
return false;
|
||||
}
|
||||
auto& refresh_info = analyzer_refresh.get_result_refresh();
|
||||
if (refresh_info.in_cooldown) sleep(refresh_info.remain_secs * 1000);
|
||||
if (refresh_info.in_cooldown) {
|
||||
sleep(refresh_info.remain_secs * 1000);
|
||||
}
|
||||
ctrler()->click(refresh_info.rect);
|
||||
sleep(Task.get("RoguelikeRefreshSupportBtnOcr")->post_delay);
|
||||
ProcessTask(*this, { "RoguelikeSupportSwipeLeft" }).run();
|
||||
@@ -641,20 +697,24 @@ void asst::RoguelikeRecruitTaskPlugin::slowly_swipe(bool to_left, int swipe_dist
|
||||
{
|
||||
std::string swipe_task_name =
|
||||
to_left ? "RoguelikeRecruitOperListSlowlySwipeToTheLeft" : "RoguelikeRecruitOperListSlowlySwipeToTheRight";
|
||||
if (!ControlFeat::support(ctrler()->support_features(),
|
||||
ControlFeat::PRECISE_SWIPE)) { // 不能精准滑动时不使用 swipe_dist 参数
|
||||
if (!ControlFeat::support(
|
||||
ctrler()->support_features(),
|
||||
ControlFeat::PRECISE_SWIPE)) { // 不能精准滑动时不使用 swipe_dist 参数
|
||||
ProcessTask(*this, { swipe_task_name }).run();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!to_left) swipe_dist = -swipe_dist;
|
||||
if (!to_left) {
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#pragma once
|
||||
#include "AbstractRoguelikeTaskPlugin.h"
|
||||
#include "Common/AsstBattleDef.h"
|
||||
#include "Config/Roguelike/RoguelikeRecruitConfig.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
// 干员招募信息
|
||||
struct RoguelikeRecruitInfo
|
||||
{
|
||||
std::string name; // 干员名字
|
||||
int priority = 0; // 招募优先级 (0-1000)
|
||||
int page_index = 0; // 所在页码 (用于判断翻页方向)
|
||||
std::string name; // 干员名字
|
||||
int priority = 0; // 招募优先级 (0-1000)
|
||||
int page_index = 0; // 所在页码 (用于判断翻页方向)
|
||||
bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动)
|
||||
};
|
||||
|
||||
@@ -49,10 +50,13 @@ private:
|
||||
bool recruit_appointed_char(const std::string& char_name, bool is_rtl = false);
|
||||
// 选择干员
|
||||
void select_oper(const battle::roguelike::Recruitment& oper);
|
||||
// 找出给定的 Offset 组所有满足条件的干员
|
||||
std::unordered_set<std::string> calculate_condition_oper(
|
||||
const RecruitPriorityOffset& condition,
|
||||
const std::unordered_map<std::string, RoguelikeOper>& chars_map);
|
||||
|
||||
int m_recruit_count = 0; // 第几次招募
|
||||
bool m_starts_complete =
|
||||
false; // 开局干员是否已经招募,阵容中必须有开局干员,没有前仅招募start干员或预备干员
|
||||
bool m_team_complete = false; // 阵容是否完备,阵容完备前,仅招募key干员或预备干员
|
||||
int m_recruit_count = 0; // 第几次招募
|
||||
bool m_starts_complete = false; // 开局干员是否已经招募,阵容中必须有开局干员,没有前仅招募start干员或预备干员
|
||||
bool m_team_complete = false; // 阵容是否完备,阵容完备前,仅招募key干员或预备干员
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user