mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 18:20:39 +08:00
Merge pull request #2167 from MaaAssistantArknights/perf/roguefight
perf: 优化肉鸽自动战斗逻辑
This commit is contained in:
@@ -30,7 +30,21 @@
|
||||
"key_kills": [ // 关键击杀数,可选,默认为空
|
||||
15, // 若不为空,干员技能会攒着,不是好了就放,而是等到击杀数达到时再释放
|
||||
... // 当跑完数组里所有的元素之后,再把干员技能变成好了就放
|
||||
]
|
||||
],
|
||||
"not_use_dice": false, // 若为true,则核心干员撤退后不使用骰子
|
||||
"role_order": [ // 部署干员的顺序
|
||||
"warrior",
|
||||
"pioneer",
|
||||
"medic",
|
||||
"tank",
|
||||
"sniper",
|
||||
"caster",
|
||||
"support",
|
||||
"special",
|
||||
"drone"
|
||||
],
|
||||
"stop_melee_deploy_num": 3 // 在部署了3人后立即部署2名远程干员
|
||||
"deploy_ranged_num": 2 // 填写上一项后此项必填
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,67 +15,80 @@
|
||||
"name": "Sharp",
|
||||
"skill": 1,
|
||||
"recruit_priority": 990,
|
||||
"promote_priority": 650
|
||||
"recruit_priority_when_team_full": 890,
|
||||
"recruit_priority_when_team_full_Doc": "不填默认 recruit_priority - 100",
|
||||
"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 的逻辑"
|
||||
},
|
||||
{
|
||||
"name": "海沫",
|
||||
"skill": 1,
|
||||
"recruit_priority": 960,
|
||||
"promote_priority": 650
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 750
|
||||
},
|
||||
{
|
||||
"name": "羽毛笔",
|
||||
"skill": 1,
|
||||
"recruit_priority": 950,
|
||||
"promote_priority": 650
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 750
|
||||
},
|
||||
{
|
||||
"name": "百炼嘉维尔",
|
||||
"skill": 3,
|
||||
"alternate_skill": 2,
|
||||
"skill": 2,
|
||||
"recruit_priority": 900,
|
||||
"promote_priority": 750
|
||||
"promote_priority": 750,
|
||||
"promote_priority_when_team_full": 950
|
||||
},
|
||||
{
|
||||
"name": "棘刺",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 780,
|
||||
"promote_priority": 1000
|
||||
"promote_priority": 1000,
|
||||
"promote_priority_when_team_full": 1200
|
||||
},
|
||||
{
|
||||
"name": "山",
|
||||
"skill": 2,
|
||||
"skill_usage": 2,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 550
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full":750
|
||||
},
|
||||
{
|
||||
"name": "玛恩纳",
|
||||
"skill": 3,
|
||||
"alternate_skill": 2,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 700
|
||||
"promote_priority": 700,
|
||||
"promote_priority_when_team_full":750
|
||||
},
|
||||
{
|
||||
"name": "煌",
|
||||
"skill": 2,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 640
|
||||
"promote_priority": 640,
|
||||
"promote_priority_when_team_full":750
|
||||
},
|
||||
{
|
||||
"name": "拉普兰德",
|
||||
"skill": 2,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 750
|
||||
"promote_priority": 750,
|
||||
"promote_priority_when_team_full":800
|
||||
},
|
||||
{
|
||||
"name": "银灰",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 750,
|
||||
"promote_priority": 800
|
||||
"promote_priority": 800,
|
||||
"promote_priority_when_team_full":800
|
||||
},
|
||||
{
|
||||
"name": "艾丽妮",
|
||||
@@ -149,9 +162,11 @@
|
||||
},
|
||||
{
|
||||
"name": "风笛",
|
||||
"skill": 2,
|
||||
"skill": 3,
|
||||
"alternate_skill": 2,
|
||||
"recruit_priority": 600,
|
||||
"promote_priority": 610
|
||||
"promote_priority": 610,
|
||||
"promote_priority_when_team_full": 850
|
||||
},
|
||||
{
|
||||
"name": "推进之王",
|
||||
@@ -209,19 +224,21 @@
|
||||
"name": "塞雷娅",
|
||||
"skill": 2,
|
||||
"recruit_priority": 800,
|
||||
"promote_priority": 760
|
||||
"promote_priority": 760,
|
||||
"promote_priority_when_team_full": 850
|
||||
},
|
||||
{
|
||||
"name": "瑕光",
|
||||
"skill": 1,
|
||||
"recruit_priority": 200,
|
||||
"promote_priority": 100
|
||||
"promote_priority": 200
|
||||
},
|
||||
{
|
||||
"name": "临光",
|
||||
"skill": 1,
|
||||
"recruit_priority": 400,
|
||||
"promote_priority": 1
|
||||
"promote_priority": 200,
|
||||
"promote_priority_when_team_full": 750
|
||||
},
|
||||
{
|
||||
"name": "古米",
|
||||
@@ -247,27 +264,31 @@
|
||||
"name": "Stormeye",
|
||||
"skill": 1,
|
||||
"recruit_priority": 900,
|
||||
"promote_priority": 650
|
||||
"promote_priority": 650,
|
||||
"promote_priority_when_team_full": 100
|
||||
},
|
||||
{
|
||||
"name": "能天使",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 900,
|
||||
"promote_priority": 1000
|
||||
},
|
||||
{
|
||||
"name": "空弦",
|
||||
"skill": 1,
|
||||
"recruit_priority": 800,
|
||||
"promote_priority": 800
|
||||
"promote_priority": 1000,
|
||||
"promote_priority_when_team_full": 1200
|
||||
},
|
||||
{
|
||||
"name": "假日威龙陈",
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 790,
|
||||
"promote_priority": 1000
|
||||
"recruit_priority": 810,
|
||||
"promote_priority": 1000,
|
||||
"promote_priority_when_team_full": 1000
|
||||
},
|
||||
{
|
||||
"name": "空弦",
|
||||
"skill": 1,
|
||||
"recruit_priority": 800,
|
||||
"promote_priority": 800,
|
||||
"promote_priority_when_team_full": 800
|
||||
},
|
||||
{
|
||||
"name": "鸿雪",
|
||||
@@ -299,13 +320,15 @@
|
||||
"name": "白金",
|
||||
"skill": 2,
|
||||
"recruit_priority": 550,
|
||||
"promote_priority": 550
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 750
|
||||
},
|
||||
{
|
||||
"name": "蓝毒",
|
||||
"skill": 1,
|
||||
"recruit_priority": 550,
|
||||
"promote_priority": 550
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 750
|
||||
},
|
||||
{
|
||||
"name": "梅",
|
||||
@@ -337,20 +360,23 @@
|
||||
"name": "Pith",
|
||||
"skill": 1,
|
||||
"recruit_priority": 900,
|
||||
"promote_priority": 600
|
||||
"promote_priority": 600,
|
||||
"promote_priority_when_team_full": 100
|
||||
},
|
||||
{
|
||||
"name": "澄闪",
|
||||
"skill": 3,
|
||||
"alternate_skill": 2,
|
||||
"recruit_priority": 700,
|
||||
"promote_priority": 1000
|
||||
"promote_priority": 1000,
|
||||
"promote_priority_when_team_full": 1200
|
||||
},
|
||||
{
|
||||
"name": "艾雅法拉",
|
||||
"skill": 2,
|
||||
"recruit_priority": 600,
|
||||
"promote_priority": 700
|
||||
"promote_priority": 700,
|
||||
"promote_priority_when_team_full":1000
|
||||
},
|
||||
{
|
||||
"name": "异客",
|
||||
@@ -421,7 +447,8 @@
|
||||
"name": "归溟幽灵鲨",
|
||||
"skill": 2,
|
||||
"recruit_priority": 700,
|
||||
"promote_priority": 700
|
||||
"promote_priority": 700,
|
||||
"promote_priority_when_team_full": 100
|
||||
},
|
||||
{
|
||||
"name": "砾",
|
||||
@@ -492,25 +519,29 @@
|
||||
"name": "蜜莓",
|
||||
"skill": 1,
|
||||
"recruit_priority": 700,
|
||||
"promote_priority": 610
|
||||
"promote_priority": 610,
|
||||
"promote_priority_when_team_full": 850
|
||||
},
|
||||
{
|
||||
"name": "桑葚",
|
||||
"skill": 1,
|
||||
"recruit_priority": 600,
|
||||
"promote_priority": 500
|
||||
"promote_priority": 500,
|
||||
"promote_priority_when_team_full": 850
|
||||
},
|
||||
{
|
||||
"name": "白面鸮",
|
||||
"skill": 2,
|
||||
"recruit_priority": 550,
|
||||
"promote_priority": 550
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 600
|
||||
},
|
||||
{
|
||||
"name": "华法琳",
|
||||
"skill": 2,
|
||||
"recruit_priority": 540,
|
||||
"promote_priority": 550
|
||||
"promote_priority": 550,
|
||||
"promote_priority_when_team_full": 600
|
||||
},
|
||||
{
|
||||
"name": "赫默",
|
||||
@@ -568,7 +599,8 @@
|
||||
"skill": 3,
|
||||
"alternate_skill": 1,
|
||||
"recruit_priority": 20,
|
||||
"promote_priority": 500
|
||||
"promote_priority": 500,
|
||||
"promote_priority_when_team_full": 900
|
||||
},
|
||||
{
|
||||
"name": "令",
|
||||
|
||||
@@ -1927,6 +1927,10 @@
|
||||
[
|
||||
"限星",
|
||||
"陨星"
|
||||
],
|
||||
[
|
||||
"般子",
|
||||
"骰子"
|
||||
]
|
||||
]
|
||||
},
|
||||
@@ -6074,6 +6078,10 @@
|
||||
"template": "empty.png",
|
||||
"templThreshold": 0.6
|
||||
},
|
||||
"DiceAvatarMatch": {
|
||||
"template": "empty.png",
|
||||
"templThreshold": 0.8
|
||||
},
|
||||
"BattleKillsFlag": {
|
||||
"roi": [
|
||||
150,
|
||||
@@ -8148,6 +8156,19 @@
|
||||
30
|
||||
]
|
||||
},
|
||||
"Roguelike1SkillSelectionTeamFull": {
|
||||
"cache": false,
|
||||
"roi": [
|
||||
135,
|
||||
22,
|
||||
1032,
|
||||
668
|
||||
],
|
||||
"maskRange": [
|
||||
200,
|
||||
255
|
||||
]
|
||||
},
|
||||
"Roguelike1StartAction": {
|
||||
"template": "Roguelike1StartAction.png",
|
||||
"Doc": "本任务注册了插件 RoguelikeSkillSelectionTaskPlugin、RoguelikeBattleTaskPlugin",
|
||||
|
||||
BIN
resource/template/Roguelike1SkillSelectionTeamFull.png
Normal file
BIN
resource/template/Roguelike1SkillSelectionTeamFull.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Utils/NoWarningCV.h"
|
||||
|
||||
#include "General/MatchImageAnalyzer.h"
|
||||
#include "General/MultiMatchImageAnalyzer.h"
|
||||
#include "General/OcrWithPreprocessImageAnalyzer.h"
|
||||
#include "Resource/RoguelikeRecruitConfiger.h"
|
||||
@@ -40,6 +41,8 @@ bool asst::RoguelikeSkillSelectionImageAnalyzer::analyze()
|
||||
m_result.emplace(std::move(name), std::move(skills));
|
||||
}
|
||||
|
||||
team_full_analyze();
|
||||
|
||||
return !m_result.empty();
|
||||
}
|
||||
|
||||
@@ -74,3 +77,10 @@ std::vector<asst::Rect> asst::RoguelikeSkillSelectionImageAnalyzer::skill_analyz
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void asst::RoguelikeSkillSelectionImageAnalyzer::team_full_analyze()
|
||||
{
|
||||
MatchImageAnalyzer analyzer(m_image);
|
||||
analyzer.set_task_info("Roguelike1SkillSelectionTeamFull");
|
||||
m_team_full = !analyzer.analyze();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace asst
|
||||
virtual bool analyze() override;
|
||||
|
||||
const auto& get_result() const noexcept { return m_result; }
|
||||
bool get_team_full() const noexcept { return m_team_full; }
|
||||
|
||||
private:
|
||||
// 该分析器不支持外部设置ROI
|
||||
@@ -22,7 +23,9 @@ namespace asst
|
||||
|
||||
std::string name_analyze(const Rect& roi);
|
||||
std::vector<Rect> skill_analyze(const Rect& roi);
|
||||
void team_full_analyze();
|
||||
|
||||
std::unordered_map<std::string, std::vector<Rect>> m_result; // 干员名 : 技能的位置(多个技能)
|
||||
bool m_team_full = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <meojson/json.hpp>
|
||||
|
||||
#include "Utils/AsstUtils.hpp"
|
||||
#include "Utils/Logger.hpp"
|
||||
|
||||
std::optional<asst::RoguelikeBattleData> asst::RoguelikeCopilotConfiger::get_stage_data(
|
||||
@@ -60,6 +61,94 @@ bool asst::RoguelikeCopilotConfiger::parse(const json::value& json)
|
||||
data.key_kills.emplace_back(static_cast<int>(key_kill));
|
||||
}
|
||||
}
|
||||
data.use_dice_stage = !stage_info.get("not_use_dice", false);
|
||||
|
||||
data.stop_melee_deploy_num = stage_info.get("stop_melee_deploy_num", INT_MAX);
|
||||
data.deploy_ranged_num = stage_info.get("deploy_ranged_num", 0);
|
||||
if (data.deploy_ranged_num == 0) {
|
||||
data.stop_melee_deploy_num = INT_MAX;
|
||||
}
|
||||
|
||||
constexpr int RoleNumber = 9;
|
||||
static constexpr std::array<BattleRole, RoleNumber> RoleOrder = {
|
||||
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();
|
||||
using views::filter, views::transform;
|
||||
std::unordered_set<BattleRole> specified_role;
|
||||
std::vector<BattleRole> role_order;
|
||||
bool is_legal = true;
|
||||
if (ranges::find_if_not(raw_roles | views::all, std::mem_fn(&json::value::is_string)) != raw_roles.end()) {
|
||||
Log.error("BattleRole should be string");
|
||||
return false;
|
||||
}
|
||||
auto roles = raw_roles | filter(&json::value::is_string) | transform(&json::value::as_string) |
|
||||
transform([&](std::string name) {
|
||||
ranges::for_each(name, to_lower);
|
||||
return std::move(name);
|
||||
});
|
||||
for (const std::string& role_name : roles) {
|
||||
auto iter = NameToRole.find(role_name);
|
||||
if (iter == NameToRole.end()) [[unlikely]] {
|
||||
Log.error("Unknown BattleRole:", role_name);
|
||||
is_legal = false;
|
||||
break;
|
||||
}
|
||||
if (specified_role.contains(iter->second)) [[unlikely]] {
|
||||
Log.error("Duplicated BattleRole:", role_name);
|
||||
is_legal = false;
|
||||
break;
|
||||
}
|
||||
specified_role.emplace(iter->second);
|
||||
role_order.emplace_back(iter->second);
|
||||
}
|
||||
if (is_legal) [[likely]] {
|
||||
ranges::copy(RoleOrder | filter([&](BattleRole role) { return !specified_role.contains(role); }),
|
||||
std::back_inserter(role_order));
|
||||
if (role_order.size() != RoleNumber) [[unlikely]] {
|
||||
Log.error("Unexpected role_order size:", role_order.size());
|
||||
return false;
|
||||
}
|
||||
ranges::move(role_order, data.role_order.begin());
|
||||
}
|
||||
else {
|
||||
data.role_order = RoleOrder;
|
||||
}
|
||||
}
|
||||
else {
|
||||
data.role_order = RoleOrder;
|
||||
}
|
||||
m_stage_data.emplace(std::move(stage_name), std::move(data));
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -28,6 +28,10 @@ bool asst::RoguelikeRecruitConfiger::parse(const json::value& json)
|
||||
info.name = name;
|
||||
info.recruit_priority = oper_info.get("recruit_priority", 0);
|
||||
info.promote_priority = oper_info.get("promote_priority", 0);
|
||||
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 + 100);
|
||||
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);
|
||||
|
||||
@@ -15,8 +15,10 @@ namespace asst
|
||||
struct RoguelikeOperInfo
|
||||
{
|
||||
std::string name;
|
||||
int recruit_priority = 0; // 招募优先级 (0-1000)
|
||||
int promote_priority = 0; // 晋升优先级 (0-1000)
|
||||
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)
|
||||
bool is_alternate = false; // 是否后备干员 (允许重复招募、划到后备干员时不再往右划动)
|
||||
int skill = 0;
|
||||
int alternate_skill = 0;
|
||||
|
||||
@@ -35,7 +35,7 @@ std::unordered_map<asst::Point, asst::TilePack::TileInfo> asst::TilePack::calc(c
|
||||
|
||||
bool ret = m_tile_calculator->run(stage_code, side, pos, tiles);
|
||||
|
||||
Log.trace("After tiles cacl run");
|
||||
Log.trace("After tiles calc run");
|
||||
if (!ret) {
|
||||
Log.info("Tiles calc error!");
|
||||
return {};
|
||||
|
||||
@@ -37,7 +37,10 @@ namespace asst
|
||||
static inline const std::string RoguelikeCharLevelPrefix = "RoguelikeLevel-";
|
||||
static inline const std::string RoguelikeCharRarityPrefix = "RoguelikeRarity-";
|
||||
static inline const std::string RoguelikeCharOverview = "RoguelikeOverview";
|
||||
static inline const std::string RoguelikeCoreChar = "RoguelikeCoreChar";
|
||||
static inline const std::string RoguelikeTraderNoLongerBuy = "RoguelikeNoLongerBuy";
|
||||
static inline const std::string RoguelikeSkillUsagePrefix = "RoguelikeSkillUsage-";
|
||||
static inline const std::string RoguelikeTeamFullWithoutRookie = "RoguelikeTeamFullWithoutRookie";
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, int64_t> m_number;
|
||||
|
||||
@@ -140,16 +140,19 @@ bool asst::RoguelikeBattleTaskPlugin::get_stage_info()
|
||||
auto opt = RoguelikeCopilot.get_stage_data(m_stage_name);
|
||||
if (opt && !opt->replacement_home.empty()) {
|
||||
m_homes = opt->replacement_home;
|
||||
std::string log_str = "[ ";
|
||||
for (auto& home : m_homes) {
|
||||
if (!m_normal_tile_info.contains(home.location)) {
|
||||
Log.error("No replacement home point", home.location.x, home.location.y);
|
||||
}
|
||||
log_str += "( " + std::to_string(home.location.x) + ", " + std::to_string(home.location.y) + " ), ";
|
||||
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);
|
||||
if (!invalid_homes_pos.empty()) {
|
||||
Log.error("No replacement homes point:", invalid_homes_pos);
|
||||
}
|
||||
log_str += "]";
|
||||
Log.info("replacement home:", log_str);
|
||||
Log.info("replacement home:", homes_pos | views::transform(&Point::to_string));
|
||||
m_blacklist_location = opt->blacklist_location;
|
||||
m_stage_use_dice = opt->use_dice_stage;
|
||||
m_role_order = opt->role_order;
|
||||
m_stop_melee_deploy_num = opt->stop_melee_deploy_num;
|
||||
m_deploy_ranged_num = opt->deploy_ranged_num;
|
||||
}
|
||||
else {
|
||||
for (const auto& [loc, side] : m_normal_tile_info) {
|
||||
@@ -157,6 +160,21 @@ bool asst::RoguelikeBattleTaskPlugin::get_stage_info()
|
||||
m_homes.emplace_back(ReplacementHome { loc, BattleDeployDirection::None });
|
||||
}
|
||||
}
|
||||
m_stage_use_dice = true;
|
||||
m_role_order = {
|
||||
BattleRole::Warrior, BattleRole::Pioneer, BattleRole::Medic, BattleRole::Tank, BattleRole::Sniper,
|
||||
BattleRole::Caster, BattleRole::Support, BattleRole::Special, BattleRole::Drone,
|
||||
};
|
||||
}
|
||||
m_wait_melee.assign(m_homes.size(), true);
|
||||
m_wait_medic.assign(m_homes.size(), true);
|
||||
m_indeed_no_medic.assign(m_homes.size(), false);
|
||||
// 认为开局时未在所有路线放干员时为紧急状态,并把路线压入栈
|
||||
// 由于开局路线设为0,故0不需要被压入栈
|
||||
m_cur_home_index = 0;
|
||||
m_is_cur_urgent = true;
|
||||
for (size_t index = m_homes.size() - 1; index > 0; index--) {
|
||||
m_next_urgent_index.push(index);
|
||||
}
|
||||
if (opt && !opt->key_kills.empty()) {
|
||||
std::string log_str = "[ ";
|
||||
@@ -186,6 +204,33 @@ bool asst::RoguelikeBattleTaskPlugin::get_stage_info()
|
||||
return calced;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::battle_pause()
|
||||
{
|
||||
return ProcessTask(*this, { "BattlePause" }).run();
|
||||
}
|
||||
|
||||
asst::RoguelikeBattleTaskPlugin::BattleRoleType asst::RoguelikeBattleTaskPlugin::get_role_type(const BattleRole& role)
|
||||
{
|
||||
switch (role) {
|
||||
case BattleRole::Medic:
|
||||
case BattleRole::Support:
|
||||
case BattleRole::Sniper:
|
||||
case BattleRole::Caster:
|
||||
return BattleRoleType::Ranged;
|
||||
break;
|
||||
case BattleRole::Pioneer:
|
||||
case BattleRole::Warrior:
|
||||
case BattleRole::Tank:
|
||||
return BattleRoleType::Melee;
|
||||
break;
|
||||
case BattleRole::Special:
|
||||
case BattleRole::Drone:
|
||||
default:
|
||||
return BattleRoleType::Other;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -207,6 +252,18 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
// }
|
||||
// }
|
||||
|
||||
// 将存在于场上超过限时的召唤物所在的地块重新设为可用
|
||||
Time_Point now_time = std::chrono::system_clock::now();
|
||||
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");
|
||||
m_opers_in_field.erase((iter->second).first);
|
||||
m_used_tiles.erase(iter);
|
||||
}
|
||||
m_need_clear_tiles.pop();
|
||||
}
|
||||
|
||||
const cv::Mat& image = m_ctrler->get_image();
|
||||
if (try_possible_skill(image)) {
|
||||
return true;
|
||||
@@ -220,23 +277,144 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
}
|
||||
|
||||
battle_analyzer.sort_opers_by_cost();
|
||||
const auto& opers = battle_analyzer.get_opers();
|
||||
auto opers = battle_analyzer.get_opers();
|
||||
if (opers.empty()) {
|
||||
return true;
|
||||
}
|
||||
// 如果已经放了一些人了,就不要再有费就下了
|
||||
size_t available_count = 0;
|
||||
size_t cooling_count = 0;
|
||||
battle_analyzer.set_target(BattleImageAnalyzer::Target::Oper);
|
||||
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
|
||||
int available_count = 0;
|
||||
int cooling_count = 0;
|
||||
std::vector<size_t> new_urgent;
|
||||
std::vector<std::string> cooling_opers;
|
||||
const auto use_oper_task_ptr = Task.get("BattleUseOper");
|
||||
bool has_dice = false;
|
||||
BattleRealTimeOper dice;
|
||||
for (const auto& oper : opers) {
|
||||
if (oper.available) {
|
||||
available_count += 1;
|
||||
}
|
||||
if (oper.cooling) {
|
||||
cooling_count += 1;
|
||||
if (oper.cooling) cooling_count++;
|
||||
if (oper.available) available_count++;
|
||||
}
|
||||
for (auto& oper : opers) {
|
||||
if (oper.role == BattleRole::Drone) {
|
||||
if (!m_dice_image.empty()) {
|
||||
MatchImageAnalyzer dice_analyzer(oper.avatar);
|
||||
dice_analyzer.set_task_info("DiceAvatarMatch");
|
||||
dice_analyzer.set_templ(m_dice_image);
|
||||
if (dice_analyzer.analyze()) {
|
||||
has_dice = true;
|
||||
oper.name = "骰子";
|
||||
dice = oper;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_used_tiles.size() >= std::max(m_homes.size(), static_cast<size_t>(2))) {
|
||||
// 超过一半的人费用都没好,那就不下人
|
||||
bool can_use_dice = m_stage_use_dice && has_dice;
|
||||
if (!can_use_dice) {
|
||||
m_use_dice = false;
|
||||
}
|
||||
// 如果发现有新撤退,就更新m_retreated_opers
|
||||
// 如果发现有新转好的,只更新m_last_cooling_count,在部署时从set中删去
|
||||
if (cooling_count > m_last_cooling_count) {
|
||||
battle_pause();
|
||||
int remain_add = cooling_count - m_last_cooling_count;
|
||||
Rect cur_rect;
|
||||
for (size_t i = 0; i < opers.size(); ++i) {
|
||||
const auto& cur_opers = battle_analyzer.get_opers();
|
||||
if (cur_opers.empty()) {
|
||||
// 只会在战斗结束时发生,战斗结束后cur_opers为空,导致访问越界
|
||||
return true;
|
||||
}
|
||||
size_t offset = opers.size() > cur_opers.size() ? opers.size() - cur_opers.size() : 0;
|
||||
const auto& oper = cur_opers.at(i - offset);
|
||||
if ((!oper.cooling) || oper.role == BattleRole::Drone) {
|
||||
continue;
|
||||
}
|
||||
cur_rect = oper.rect;
|
||||
m_ctrler->click(cur_rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
const cv::Mat& new_image = m_ctrler->get_image();
|
||||
|
||||
OcrWithPreprocessImageAnalyzer oper_name_analyzer(new_image);
|
||||
oper_name_analyzer.set_task_info("BattleOperName");
|
||||
oper_name_analyzer.set_replace(Task.get<OcrTaskInfo>("CharsNameOcrReplace")->replace_map);
|
||||
|
||||
std::string oper_name = "Unknown";
|
||||
if (oper_name_analyzer.analyze()) {
|
||||
oper_name_analyzer.sort_result_by_score();
|
||||
oper_name = oper_name_analyzer.get_result().front().text;
|
||||
}
|
||||
|
||||
battle_analyzer.set_image(new_image);
|
||||
battle_analyzer.analyze();
|
||||
battle_analyzer.sort_opers_by_cost();
|
||||
|
||||
if (oper_name == "Unknown") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_retreated_opers.contains(oper_name)) {
|
||||
continue;
|
||||
}
|
||||
auto iter = m_opers_in_field.find(oper_name);
|
||||
if (iter == m_opers_in_field.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.info(oper_name, "retreated");
|
||||
if (auto del_pos_melee = m_melee_for_home_index.find(iter->second);
|
||||
del_pos_melee != m_melee_for_home_index.end()) {
|
||||
m_wait_melee[del_pos_melee->second] = true;
|
||||
new_urgent.emplace_back(del_pos_melee->second);
|
||||
m_melee_for_home_index.erase(del_pos_melee);
|
||||
}
|
||||
else if (auto del_pos_medic = m_medic_for_home_index.find(iter->second);
|
||||
del_pos_medic != m_medic_for_home_index.end()) {
|
||||
for (const size_t& home_index : del_pos_medic->second) {
|
||||
m_wait_medic[home_index] = true;
|
||||
}
|
||||
m_medic_for_home_index.erase(del_pos_medic);
|
||||
}
|
||||
if (auto del_pos_tiles = m_used_tiles.find(iter->second); del_pos_tiles != m_used_tiles.end()) {
|
||||
if (get_role_type(del_pos_tiles->second.second) == BattleRoleType::Melee) {
|
||||
m_has_deployed_melee_num--;
|
||||
}
|
||||
m_used_tiles.erase(del_pos_tiles);
|
||||
}
|
||||
m_opers_in_field.erase(iter);
|
||||
m_retreated_opers.emplace(oper_name);
|
||||
remain_add--;
|
||||
if (!remain_add) break;
|
||||
}
|
||||
m_ctrler->click(cur_rect);
|
||||
battle_pause();
|
||||
}
|
||||
m_last_cooling_count = cooling_count;
|
||||
if (!new_urgent.empty()) {
|
||||
// 出现新的紧急情况,立即切到这条线路,并把其他紧急情况压入栈
|
||||
Log.info("New urgent situation detected");
|
||||
if (m_is_cur_urgent) {
|
||||
m_next_urgent_index.push(m_cur_home_index);
|
||||
}
|
||||
else {
|
||||
m_last_not_urgent = static_cast<int>(m_cur_home_index);
|
||||
}
|
||||
m_cur_home_index = new_urgent.at(0);
|
||||
for (size_t i = 1; i < new_urgent.size(); ++i) {
|
||||
m_next_urgent_index.push(new_urgent.at(i));
|
||||
}
|
||||
if (can_use_dice) {
|
||||
// 先放骰子,再放近战
|
||||
m_use_dice = true;
|
||||
m_next_urgent_index.push(new_urgent.at(0));
|
||||
}
|
||||
}
|
||||
else if ((!m_is_cur_urgent) && m_used_tiles.size() >= 2) {
|
||||
// 超过一半的人费用都没好,且没有紧急情况,那就不下人
|
||||
size_t not_cooling_count = opers.size() - cooling_count;
|
||||
if (available_count <= not_cooling_count / 2) {
|
||||
Log.trace("already used", m_used_tiles.size(), ", now_total", opers.size(), ", available", available_count,
|
||||
@@ -245,90 +423,153 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
}
|
||||
}
|
||||
|
||||
static const std::array<BattleRole, 9> RoleOrder = {
|
||||
BattleRole::Warrior, BattleRole::Pioneer, BattleRole::Medic, BattleRole::Tank, BattleRole::Sniper,
|
||||
BattleRole::Caster, BattleRole::Support, BattleRole::Special, BattleRole::Drone,
|
||||
};
|
||||
const auto use_oper_task_ptr = Task.get("BattleUseOper");
|
||||
const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper");
|
||||
|
||||
// 点击当前最合适的干员
|
||||
BattleRealTimeOper opt_oper;
|
||||
bool oper_found = false;
|
||||
|
||||
// 一个人都没有的时候,先下个地面单位(如果有的话)
|
||||
// 第二个人下奶(如果有的话)
|
||||
bool wait_melee = false;
|
||||
bool wait_medic = false;
|
||||
if (m_used_tiles.empty() || m_used_tiles.size() == 1) {
|
||||
for (const auto& op : opers) {
|
||||
if (m_used_tiles.empty()) {
|
||||
if (op.role == BattleRole::Warrior || op.role == BattleRole::Pioneer || op.role == BattleRole::Tank) {
|
||||
wait_melee = true;
|
||||
}
|
||||
}
|
||||
else if (m_used_tiles.size() == 1 && m_homes.size() == 1) {
|
||||
if (op.role == BattleRole::Medic) {
|
||||
wait_medic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto role : RoleOrder) {
|
||||
if (wait_melee) {
|
||||
if (role != BattleRole::Warrior && role != BattleRole::Pioneer && role != BattleRole::Tank) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (wait_medic) {
|
||||
if (role != BattleRole::Medic) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& oper : opers) {
|
||||
if (!oper.available) {
|
||||
continue;
|
||||
}
|
||||
if (oper.role == role) {
|
||||
opt_oper = oper;
|
||||
oper_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (oper_found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!oper_found) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 预计算干员是否有点地方放
|
||||
if (available_locations(opt_oper.role).empty()) {
|
||||
Log.info("Tiles full");
|
||||
if (cooling_count) {
|
||||
Log.info("has cooling, clear and re-calc");
|
||||
m_used_tiles.clear();
|
||||
}
|
||||
else {
|
||||
bool has_melee = false;
|
||||
bool has_medic = false;
|
||||
bool wait_melee = m_wait_melee[m_cur_home_index];
|
||||
bool wait_medic = m_wait_medic[m_cur_home_index];
|
||||
bool force_need_ranged =
|
||||
(!m_has_finished_deploy_ranged) && (m_has_deployed_melee_num >= m_stop_melee_deploy_num) && (!m_use_dice);
|
||||
if (m_use_dice) {
|
||||
opt_oper = std::move(dice);
|
||||
oper_found = true;
|
||||
if (available_locations(opt_oper.role).empty()) {
|
||||
Log.info("Tiles full");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 对于每个蓝门,先下个地面单位(如果有的话)
|
||||
// 第二个人下奶(如果有的话)
|
||||
bool has_ranged = false;
|
||||
for (const auto& op : opers) {
|
||||
const auto role_type = get_role_type(op.role);
|
||||
if (role_type == BattleRoleType::Melee && (!op.cooling)) {
|
||||
has_melee = true;
|
||||
}
|
||||
else if (role_type == BattleRoleType::Ranged && (!op.cooling)) {
|
||||
has_ranged = true;
|
||||
if (op.role == BattleRole::Medic && (!op.cooling)) {
|
||||
has_medic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_ctrler->click(opt_oper.rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
wait_melee &= has_melee;
|
||||
wait_medic &= has_medic;
|
||||
if (force_need_ranged) {
|
||||
Log.info("RANGED ROLE IS NEEDED UNDER FORCE");
|
||||
if (!has_ranged) {
|
||||
m_has_finished_deploy_ranged = true;
|
||||
Log.info("FORCE RANGED OPER DEPLOY END");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (auto role : m_role_order) {
|
||||
const auto role_type = get_role_type(role);
|
||||
if (force_need_ranged) {
|
||||
if (role_type != BattleRoleType::Ranged) continue;
|
||||
}
|
||||
else {
|
||||
if (wait_melee) {
|
||||
if (role_type != BattleRoleType::Melee) continue;
|
||||
}
|
||||
else if (wait_medic) {
|
||||
if (role != BattleRole::Medic) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OcrWithPreprocessImageAnalyzer oper_name_analyzer(m_ctrler->get_image());
|
||||
oper_name_analyzer.set_task_info("BattleOperName");
|
||||
oper_name_analyzer.set_replace(Task.get<OcrTaskInfo>("CharsNameOcrReplace")->replace_map);
|
||||
for (const auto& oper : opers) {
|
||||
if (!oper.available) {
|
||||
continue;
|
||||
}
|
||||
if (oper.name == "骰子") {
|
||||
continue;
|
||||
}
|
||||
if (oper.role == role) {
|
||||
opt_oper = oper;
|
||||
oper_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (oper_found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!oper_found) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string oper_name = "Unknown";
|
||||
if (oper_name_analyzer.analyze()) {
|
||||
oper_name_analyzer.sort_result_by_score();
|
||||
oper_name = oper_name_analyzer.get_result().front().text;
|
||||
opt_oper.name = oper_name;
|
||||
// 预计算干员是否有点地方放
|
||||
if (available_locations(opt_oper.role).empty()) {
|
||||
Log.info("Tiles full");
|
||||
if (force_need_ranged) {
|
||||
m_has_finished_deploy_ranged = true;
|
||||
Log.info("FORCE RANGED OPER DEPLOY END");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_first_deploy) {
|
||||
m_first_deploy = false;
|
||||
battle_pause();
|
||||
Rect cur_rect;
|
||||
for (size_t i = 0; i < opers.size(); ++i) {
|
||||
const auto& cur_opers = battle_analyzer.get_opers();
|
||||
if (cur_opers.empty()) {
|
||||
// 只会在战斗结束时发生,战斗结束后cur_opers为空,导致访问越界
|
||||
return true;
|
||||
}
|
||||
size_t offset = opers.size() > cur_opers.size() ? opers.size() - cur_opers.size() : 0;
|
||||
const auto& oper = cur_opers.at(i - offset);
|
||||
if (oper.role == BattleRole::Drone) {
|
||||
cur_rect = oper.rect;
|
||||
m_ctrler->click(cur_rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
const cv::Mat& new_image = m_ctrler->get_image();
|
||||
|
||||
OcrWithPreprocessImageAnalyzer oper_name_analyzer(new_image);
|
||||
oper_name_analyzer.set_task_info("BattleOperName");
|
||||
oper_name_analyzer.set_replace(Task.get<OcrTaskInfo>("CharsNameOcrReplace")->replace_map);
|
||||
|
||||
battle_analyzer.set_image(new_image);
|
||||
battle_analyzer.analyze();
|
||||
battle_analyzer.sort_opers_by_cost();
|
||||
|
||||
if (!oper_name_analyzer.analyze()) continue;
|
||||
oper_name_analyzer.sort_result_by_score();
|
||||
if (oper_name_analyzer.get_result().front().text == "骰子") {
|
||||
m_dice_image = opers.at(i).avatar;
|
||||
Log.info("Dice detected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cur_rect.empty()) {
|
||||
m_ctrler->click(cur_rect);
|
||||
}
|
||||
battle_pause();
|
||||
}
|
||||
|
||||
m_ctrler->click(opt_oper.rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
|
||||
OcrWithPreprocessImageAnalyzer oper_name_analyzer(m_ctrler->get_image());
|
||||
oper_name_analyzer.set_task_info("BattleOperName");
|
||||
oper_name_analyzer.set_replace(Task.get<OcrTaskInfo>("CharsNameOcrReplace")->replace_map);
|
||||
|
||||
opt_oper.name = "Unknown";
|
||||
if (oper_name_analyzer.analyze()) {
|
||||
oper_name_analyzer.sort_result_by_score();
|
||||
opt_oper.name = oper_name_analyzer.get_result().front().text;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算最优部署位置及方向
|
||||
@@ -345,18 +586,82 @@ bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
sleep(use_oper_task_ptr->rear_delay);
|
||||
|
||||
// 将方向转换为实际的 swipe end 坐标点
|
||||
constexpr int coeff = 500;
|
||||
Point end_point = placed_point + (direction * coeff);
|
||||
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay, true, 100);
|
||||
if (direction != Point::zero()) {
|
||||
constexpr int coeff = 500;
|
||||
Point end_point = placed_point + (direction * coeff);
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay, true, 100);
|
||||
}
|
||||
|
||||
if (opt_oper.role == BattleRole::Drone) {
|
||||
cancel_oper_selection();
|
||||
now_time = std::chrono::system_clock::now();
|
||||
if (opt_oper.name == "骰子") {
|
||||
m_need_clear_tiles.emplace(now_time + std::chrono::seconds(20), placed_loc);
|
||||
}
|
||||
else {
|
||||
m_need_clear_tiles.emplace(now_time + std::chrono::seconds(35), placed_loc);
|
||||
}
|
||||
}
|
||||
|
||||
m_used_tiles.emplace(placed_loc, oper_name);
|
||||
Log.info("Try to deploy oper", opt_oper.name);
|
||||
m_opers_used = true;
|
||||
++m_cur_home_index;
|
||||
m_used_tiles.emplace(placed_loc, std::make_pair(opt_oper.name, opt_oper.role));
|
||||
m_opers_in_field.emplace(opt_oper.name, placed_loc);
|
||||
m_retreated_opers.erase(opt_oper.name);
|
||||
if (get_role_type(opt_oper.role) == BattleRoleType::Melee) {
|
||||
m_has_deployed_melee_num++;
|
||||
}
|
||||
if (force_need_ranged) {
|
||||
m_has_deployed_ranged_num++;
|
||||
if (m_has_deployed_ranged_num >= m_deploy_ranged_num) {
|
||||
m_has_finished_deploy_ranged = true;
|
||||
Log.info("FORCE RANGED OPER DEPLOY END");
|
||||
}
|
||||
// 不改变当前index,直接进入下一步
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wait_melee) {
|
||||
m_wait_melee[m_cur_home_index] = false;
|
||||
m_melee_for_home_index.emplace(placed_loc, m_cur_home_index);
|
||||
}
|
||||
else if (wait_medic) {
|
||||
// 两次轮到wait_medic的轮次时仍没有医疗干员奶当前位置,即认为奶不到
|
||||
if (m_wait_medic[m_cur_home_index]) {
|
||||
m_indeed_no_medic[m_cur_home_index] = false;
|
||||
}
|
||||
else {
|
||||
if (m_indeed_no_medic[m_cur_home_index]) {
|
||||
m_wait_medic[m_cur_home_index] = false;
|
||||
}
|
||||
else {
|
||||
m_indeed_no_medic[m_cur_home_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有紧急需要部署的路线,那么下一条路线设为它
|
||||
if (m_next_urgent_index.empty()) {
|
||||
if (m_last_not_urgent != -1) {
|
||||
// 从上一次不是紧急的下一条路线开始
|
||||
m_cur_home_index = static_cast<size_t>(m_last_not_urgent) + 1;
|
||||
m_last_not_urgent = -1;
|
||||
}
|
||||
else {
|
||||
++m_cur_home_index;
|
||||
}
|
||||
m_is_cur_urgent = false;
|
||||
}
|
||||
else {
|
||||
if (m_last_not_urgent == -1) {
|
||||
m_last_not_urgent = static_cast<int>(m_cur_home_index);
|
||||
}
|
||||
m_is_cur_urgent = true;
|
||||
m_cur_home_index = m_next_urgent_index.top();
|
||||
Log.info("Enter urgent situtation");
|
||||
m_next_urgent_index.pop();
|
||||
}
|
||||
Log.info("To path", m_cur_home_index);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -405,15 +710,38 @@ void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
m_opers_used = false;
|
||||
m_pre_hp = 0;
|
||||
m_homes.clear();
|
||||
m_wait_melee.clear();
|
||||
m_wait_medic.clear();
|
||||
m_indeed_no_medic.clear();
|
||||
m_melee_for_home_index.clear();
|
||||
m_medic_for_home_index.clear();
|
||||
decltype(m_next_urgent_index) empty_stack;
|
||||
m_next_urgent_index.swap(empty_stack);
|
||||
m_is_cur_urgent = false;
|
||||
m_last_not_urgent = -1;
|
||||
m_blacklist_location.clear();
|
||||
decltype(m_key_kills) empty;
|
||||
m_key_kills.swap(empty);
|
||||
m_retreated_opers.clear();
|
||||
decltype(m_need_clear_tiles) empty_heap;
|
||||
m_need_clear_tiles.swap(empty_heap);
|
||||
decltype(m_key_kills) empty_queue;
|
||||
m_key_kills.swap(empty_queue);
|
||||
m_cur_home_index = 0;
|
||||
m_stage_name.clear();
|
||||
m_side_tile_info.clear();
|
||||
m_used_tiles.clear();
|
||||
m_opers_in_field.clear();
|
||||
m_kills = 0;
|
||||
m_total_kills = 0;
|
||||
m_use_dice = false;
|
||||
m_stage_use_dice = true;
|
||||
m_dice_image = cv::Mat();
|
||||
m_first_deploy = true;
|
||||
m_stop_melee_deploy_num = INT_MAX;
|
||||
m_deploy_ranged_num = 0;
|
||||
m_has_deployed_melee_num = 0;
|
||||
m_has_deployed_ranged_num = 0;
|
||||
m_has_finished_deploy_ranged = false;
|
||||
m_last_cooling_count = 0;
|
||||
|
||||
for (auto& [key, status] : m_restore_status) {
|
||||
m_status->set_number(key, status);
|
||||
@@ -433,8 +761,8 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
|
||||
MatchImageAnalyzer analyzer(image);
|
||||
analyzer.set_task_info(task_ptr);
|
||||
bool used = false;
|
||||
for (auto& [loc, name] : m_used_tiles) {
|
||||
std::string status_key = "RoguelikeSkillUsage-" + name;
|
||||
for (auto& [loc, oper_pair] : m_used_tiles) {
|
||||
std::string status_key = RuntimeStatus::RoguelikeSkillUsagePrefix + oper_pair.first;
|
||||
auto usage = BattleSkillUsage::Possibly;
|
||||
auto usage_opt = m_status->get_number(status_key);
|
||||
if (usage_opt) {
|
||||
@@ -548,34 +876,16 @@ std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(Ba
|
||||
LogTraceFunction;
|
||||
|
||||
auto buildable_type = Loc::All;
|
||||
switch (role) {
|
||||
case BattleRole::Medic:
|
||||
case BattleRole::Support:
|
||||
case BattleRole::Sniper:
|
||||
case BattleRole::Caster:
|
||||
const auto role_type = get_role_type(role);
|
||||
switch (role_type) {
|
||||
case BattleRoleType::Ranged:
|
||||
buildable_type = Loc::Ranged;
|
||||
break;
|
||||
case BattleRole::Pioneer:
|
||||
case BattleRole::Warrior:
|
||||
case BattleRole::Tank:
|
||||
case BattleRoleType::Melee:
|
||||
buildable_type = Loc::Melee;
|
||||
break;
|
||||
case BattleRole::Special:
|
||||
case BattleRole::Drone:
|
||||
case BattleRoleType::Other:
|
||||
default:
|
||||
//// 特种和无人机,有的只能放地面,有的又只能放高台,不好判断
|
||||
//// 笨办法,都试试,总有一次能成的
|
||||
//{
|
||||
// static Loc static_loc = Loc::Melee;
|
||||
// loc = static_loc;
|
||||
// if (static_loc == Loc::Melee) {
|
||||
// static_loc = Loc::Ranged;
|
||||
// }
|
||||
// else {
|
||||
// static_loc = Loc::Melee;
|
||||
// }
|
||||
//}
|
||||
// 大部分无人机都是可以摆在地上的,摆烂了
|
||||
buildable_type = Loc::Melee;
|
||||
break;
|
||||
}
|
||||
@@ -591,107 +901,9 @@ std::vector<asst::Point> asst::RoguelikeBattleTaskPlugin::available_locations(Ba
|
||||
return result;
|
||||
}
|
||||
|
||||
asst::RoguelikeBattleTaskPlugin::DeployInfo asst::RoguelikeBattleTaskPlugin::calc_best_plan(
|
||||
const BattleRealTimeOper& oper)
|
||||
asst::BattleAttackRange asst::RoguelikeBattleTaskPlugin::get_attack_range(const BattleRealTimeOper& oper)
|
||||
{
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
|
||||
Point home(5, 5); // 实在找不到家门了,随便取个点当家门用算了,一般是地图的中间
|
||||
Point recommended_direction;
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
const auto& rp_home = m_homes.at(m_cur_home_index);
|
||||
home = rp_home.location;
|
||||
static const std::unordered_map<BattleDeployDirection, Point> direction_map = {
|
||||
{ BattleDeployDirection::Up, Point::up() }, { BattleDeployDirection::Down, Point::down() },
|
||||
{ BattleDeployDirection::Left, Point::left() }, { BattleDeployDirection::Right, Point::right() },
|
||||
{ BattleDeployDirection::None, Point() },
|
||||
};
|
||||
recommended_direction = direction_map.at(rp_home.direction);
|
||||
}
|
||||
|
||||
auto comp_dist = [&](const Point& lhs, const Point& rhs) -> bool {
|
||||
int lhs_y_dist = std::abs(lhs.y - home.y);
|
||||
int lhs_dist = std::abs(lhs.x - home.x) + lhs_y_dist;
|
||||
int rhs_y_dist = std::abs(rhs.y - home.y);
|
||||
int rhs_dist = std::abs(rhs.x - home.x) + rhs_y_dist;
|
||||
// 距离一样选择 x 轴上的,因为一般的地图都是横向的长方向
|
||||
return lhs_dist == rhs_dist ? lhs_y_dist < rhs_y_dist : lhs_dist < rhs_dist;
|
||||
};
|
||||
|
||||
std::vector<Point> available_loc = available_locations(oper.role);
|
||||
|
||||
// 把所有可用的点按距离排个序
|
||||
ranges::sort(available_loc, comp_dist);
|
||||
|
||||
// 取距离最近的N个点,计算分数。然后使用得分最高的点
|
||||
constexpr int CalcPointCount = 4;
|
||||
if (available_loc.size() > CalcPointCount) {
|
||||
available_loc.erase(available_loc.begin() + CalcPointCount, available_loc.end());
|
||||
}
|
||||
|
||||
Point best_location;
|
||||
Point best_direction;
|
||||
int max_score = INT_MIN;
|
||||
|
||||
const auto& near_loc = available_loc.at(0);
|
||||
int min_dist = std::abs(near_loc.x - home.x) + std::abs(near_loc.y - home.y);
|
||||
|
||||
for (const auto& loc : available_loc) {
|
||||
auto cur_result = calc_best_direction_and_score(loc, oper, recommended_direction);
|
||||
// 离得远的要扣分
|
||||
constexpr int DistWeights = -1050;
|
||||
int extra_dist = std::abs(loc.x - home.x) + std::abs(loc.y - home.y) - min_dist;
|
||||
int extra_dist_score = DistWeights * extra_dist;
|
||||
if (oper.role == BattleRole::Medic) { // 医疗干员离得远无所谓
|
||||
extra_dist_score = 0;
|
||||
}
|
||||
|
||||
if (cur_result.second + extra_dist_score > max_score) {
|
||||
max_score = cur_result.second + extra_dist_score;
|
||||
best_location = loc;
|
||||
best_direction = cur_result.first;
|
||||
}
|
||||
}
|
||||
return { best_location, best_direction };
|
||||
}
|
||||
|
||||
std::pair<asst::Point, int> asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score(
|
||||
Point loc, const BattleRealTimeOper& oper, Point recommended_direction)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
// 根据家门的方向计算一下大概的朝向
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
Point home_loc(5, 5);
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
home_loc = m_homes.at(m_cur_home_index).location;
|
||||
}
|
||||
|
||||
auto sgn = [](const int& x) -> int {
|
||||
if (x > 0) return 1;
|
||||
if (x < 0) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
Point base_direction(0, 0);
|
||||
if (loc.x == home_loc.x) {
|
||||
base_direction.y = sgn(loc.y - home_loc.y);
|
||||
}
|
||||
else {
|
||||
base_direction.x = sgn(loc.x - home_loc.x);
|
||||
}
|
||||
Point home_direction(-base_direction.x, -base_direction.y);
|
||||
// 医疗反着算
|
||||
if (oper.role == BattleRole::Medic) {
|
||||
base_direction = -base_direction;
|
||||
}
|
||||
|
||||
int64_t elite = m_status->get_number(RuntimeStatus::RoguelikeCharElitePrefix + oper.name).value_or(0);
|
||||
// 按朝右算,后面根据方向做转换
|
||||
BattleAttackRange right_attack_range = BattleData.get_range(oper.name, elite);
|
||||
|
||||
if (right_attack_range == BattleDataConfiger::EmptyRange) {
|
||||
@@ -732,6 +944,137 @@ std::pair<asst::Point, int> asst::RoguelikeBattleTaskPlugin::calc_best_direction
|
||||
}
|
||||
}
|
||||
|
||||
return right_attack_range;
|
||||
}
|
||||
|
||||
asst::RoguelikeBattleTaskPlugin::DeployInfo asst::RoguelikeBattleTaskPlugin::calc_best_plan(
|
||||
const BattleRealTimeOper& oper)
|
||||
{
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
|
||||
Point home(5, 5); // 实在找不到家门了,随便取个点当家门用算了,一般是地图的中间
|
||||
Point recommended_direction;
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
const auto& rp_home = m_homes.at(m_cur_home_index);
|
||||
home = rp_home.location;
|
||||
static const std::unordered_map<BattleDeployDirection, Point> direction_map = {
|
||||
{ BattleDeployDirection::Up, Point::up() }, { BattleDeployDirection::Down, Point::down() },
|
||||
{ BattleDeployDirection::Left, Point::left() }, { BattleDeployDirection::Right, Point::right() },
|
||||
{ BattleDeployDirection::None, Point() },
|
||||
};
|
||||
recommended_direction = direction_map.at(rp_home.direction);
|
||||
}
|
||||
|
||||
auto comp_dist = [&](const Point& lhs, const Point& rhs) -> bool {
|
||||
int lhs_y_dist = std::abs(lhs.y - home.y);
|
||||
int lhs_dist = std::abs(lhs.x - home.x) + lhs_y_dist;
|
||||
int rhs_y_dist = std::abs(rhs.y - home.y);
|
||||
int rhs_dist = std::abs(rhs.x - home.x) + rhs_y_dist;
|
||||
// 距离一样选择 x 轴上的,因为一般的地图都是横向的长方向
|
||||
return lhs_dist == rhs_dist ? lhs_y_dist < rhs_y_dist : lhs_dist < rhs_dist;
|
||||
};
|
||||
|
||||
std::vector<Point> available_loc = available_locations(oper.role);
|
||||
|
||||
// 把所有可用的点按距离排个序
|
||||
ranges::sort(available_loc, comp_dist);
|
||||
|
||||
if (oper.name == "骰子") {
|
||||
return { available_loc.back(), Point::zero() };
|
||||
}
|
||||
|
||||
// 取距离最近的N个点,计算分数。然后使用得分最高的点
|
||||
constexpr int CalcPointCount = 4;
|
||||
if (available_loc.size() > CalcPointCount) {
|
||||
available_loc.erase(available_loc.begin() + CalcPointCount, available_loc.end());
|
||||
}
|
||||
|
||||
Point best_location;
|
||||
Point best_direction;
|
||||
int max_score = INT_MIN;
|
||||
|
||||
const auto& near_loc = available_loc.at(0);
|
||||
int min_dist = std::abs(near_loc.x - home.x) + std::abs(near_loc.y - home.y);
|
||||
|
||||
for (const auto& loc : available_loc) {
|
||||
auto cur_result = calc_best_direction_and_score(loc, oper, recommended_direction);
|
||||
// 离得远的要扣分
|
||||
constexpr int DistWeights = -1050;
|
||||
int extra_dist = std::abs(loc.x - home.x) + std::abs(loc.y - home.y) - min_dist;
|
||||
int extra_dist_score = DistWeights * extra_dist;
|
||||
if (oper.role == BattleRole::Medic) { // 医疗干员离得远无所谓
|
||||
extra_dist_score = 0;
|
||||
}
|
||||
|
||||
if (cur_result.second + extra_dist_score > max_score) {
|
||||
max_score = cur_result.second + extra_dist_score;
|
||||
best_location = loc;
|
||||
best_direction = cur_result.first;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是医疗干员,判断覆盖范围内有无第一次放置的干员
|
||||
if (oper.role == BattleRole::Medic) {
|
||||
BattleAttackRange right_attack_range = get_attack_range(oper);
|
||||
for (const Point& direction : { Point::right(), Point::up(), Point::left(), Point::down() }) {
|
||||
if (direction == best_direction) break;
|
||||
for (Point& point : right_attack_range)
|
||||
point = { point.y, -point.x };
|
||||
}
|
||||
std::vector<size_t> contain_index;
|
||||
for (const Point& relative_pos : right_attack_range) {
|
||||
Point absolute_pos = best_location + relative_pos;
|
||||
if (auto iter = m_melee_for_home_index.find(absolute_pos); iter != m_melee_for_home_index.end()) {
|
||||
m_wait_medic[iter->second] = false;
|
||||
contain_index.emplace_back(iter->second);
|
||||
}
|
||||
}
|
||||
if (!contain_index.empty()) {
|
||||
m_medic_for_home_index.emplace(best_location, std::move(contain_index));
|
||||
}
|
||||
}
|
||||
|
||||
return { best_location, best_direction };
|
||||
}
|
||||
|
||||
std::pair<asst::Point, int> asst::RoguelikeBattleTaskPlugin::calc_best_direction_and_score(
|
||||
Point loc, const BattleRealTimeOper& oper, Point recommended_direction)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
// 根据家门的方向计算一下大概的朝向
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
Point home_loc(5, 5);
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
home_loc = m_homes.at(m_cur_home_index).location;
|
||||
}
|
||||
|
||||
auto sgn = [](const int& x) -> int {
|
||||
if (x > 0) return 1;
|
||||
if (x < 0) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
Point base_direction(0, 0);
|
||||
if (loc.x == home_loc.x) {
|
||||
base_direction.y = sgn(loc.y - home_loc.y);
|
||||
}
|
||||
else {
|
||||
base_direction.x = sgn(loc.x - home_loc.x);
|
||||
}
|
||||
Point home_direction(-base_direction.x, -base_direction.y);
|
||||
// 医疗反着算
|
||||
if (oper.role == BattleRole::Medic) {
|
||||
base_direction = -base_direction;
|
||||
}
|
||||
|
||||
// 按朝右算,后面根据方向做转换
|
||||
BattleAttackRange right_attack_range = get_attack_range(oper);
|
||||
|
||||
int max_score = 0;
|
||||
Point opt_direction;
|
||||
|
||||
@@ -762,7 +1105,8 @@ std::pair<asst::Point, int> asst::RoguelikeBattleTaskPlugin::calc_best_direction
|
||||
|
||||
switch (oper.role) {
|
||||
case BattleRole::Medic:
|
||||
if (m_used_tiles.contains(absolute_pos)) // 根据哪个方向上人多决定朝向哪
|
||||
if (m_used_tiles.contains(absolute_pos) &&
|
||||
m_used_tiles[absolute_pos].second != BattleRole::Drone) // 根据哪个方向上人多决定朝向哪
|
||||
score += 10000;
|
||||
if (auto iter = m_side_tile_info.find(absolute_pos); iter != m_side_tile_info.end())
|
||||
score += TileKeyMedicWeights.at(iter->second.key);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
|
||||
#include "AbstractTaskPlugin.h"
|
||||
#include "ImageAnalyzer/BattleImageAnalyzer.h"
|
||||
@@ -13,6 +14,7 @@ namespace asst
|
||||
class RoguelikeBattleTaskPlugin : public AbstractTaskPlugin
|
||||
{
|
||||
using Loc = asst::TilePack::BuildableType;
|
||||
using Time_Point = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
public:
|
||||
using AbstractTaskPlugin::AbstractTaskPlugin;
|
||||
@@ -25,8 +27,12 @@ namespace asst
|
||||
protected:
|
||||
virtual bool _run() override;
|
||||
|
||||
enum class BattleRoleType {Melee, Ranged, Other};
|
||||
BattleRoleType get_role_type(const BattleRole& role);
|
||||
|
||||
void wait_for_start();
|
||||
bool get_stage_info();
|
||||
bool battle_pause();
|
||||
bool auto_battle();
|
||||
void all_melee_retreat();
|
||||
bool speed_up();
|
||||
@@ -46,6 +52,7 @@ namespace asst
|
||||
Point placed;
|
||||
Point direction;
|
||||
};
|
||||
BattleAttackRange get_attack_range(const BattleRealTimeOper& oper);
|
||||
DeployInfo calc_best_plan(const BattleRealTimeOper& oper);
|
||||
|
||||
// 计算摆放干员的朝向
|
||||
@@ -53,20 +60,54 @@ namespace asst
|
||||
std::pair<Point, int> calc_best_direction_and_score(Point loc, const BattleRealTimeOper& oper,
|
||||
Point recommended_direction);
|
||||
|
||||
struct DroneTile
|
||||
{
|
||||
Time_Point placed_time;
|
||||
Point placed_loc;
|
||||
DroneTile(Time_Point placed_time, Point placed_loc)
|
||||
{
|
||||
this->placed_time = placed_time;
|
||||
this->placed_loc = placed_loc;
|
||||
}
|
||||
bool operator<(const DroneTile& x) const { return x.placed_time < placed_time; }
|
||||
};
|
||||
|
||||
bool m_opers_used = false;
|
||||
bool m_is_cur_urgent = false;
|
||||
bool m_stage_use_dice = true;
|
||||
bool m_use_dice = false;
|
||||
bool m_first_deploy = true;
|
||||
int m_last_not_urgent = -1;
|
||||
int m_pre_hp = 0;
|
||||
int m_kills = 0;
|
||||
int m_total_kills = 0;
|
||||
int m_stop_melee_deploy_num = INT_MAX;
|
||||
int m_has_deployed_melee_num = 0;
|
||||
int m_deploy_ranged_num = 0;
|
||||
int m_has_deployed_ranged_num = 0;
|
||||
int m_last_cooling_count = 0;
|
||||
bool m_has_finished_deploy_ranged = false;
|
||||
size_t m_cur_home_index = 0;
|
||||
cv::Mat m_dice_image;
|
||||
|
||||
std::array<BattleRole, 9> m_role_order;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info;
|
||||
std::vector<ReplacementHome> m_homes;
|
||||
std::vector<bool> m_wait_melee;
|
||||
std::vector<bool> m_wait_medic;
|
||||
std::vector<bool> m_indeed_no_medic;
|
||||
std::unordered_map<Point, size_t> m_melee_for_home_index;
|
||||
std::unordered_map<Point, std::vector<size_t>> m_medic_for_home_index;
|
||||
std::stack<size_t> m_next_urgent_index;
|
||||
std::unordered_set<Point> m_blacklist_location;
|
||||
std::set<std::string> m_retreated_opers;
|
||||
std::queue<int> m_key_kills;
|
||||
size_t m_cur_home_index = 0;
|
||||
std::unordered_map<Point, std::string> m_used_tiles;
|
||||
std::unordered_map<Point, std::pair<std::string, BattleRole>> m_used_tiles;
|
||||
std::unordered_map<std::string, Point> m_opers_in_field;
|
||||
std::unordered_map<std::string, int64_t> m_restore_status;
|
||||
std::priority_queue<DroneTile> m_need_clear_tiles;
|
||||
|
||||
std::string m_stage_name;
|
||||
};
|
||||
}
|
||||
} // namespace asst
|
||||
|
||||
@@ -131,6 +131,6 @@ bool asst::RoguelikeCustomStartTaskPlugin::hijack_core_char()
|
||||
|
||||
sleep(Task.get("Roguelike1Custom-HijackCoChar")->pre_delay);
|
||||
|
||||
m_status->set_str("RoguelikeCoreChar", char_name);
|
||||
m_status->set_str(RuntimeStatus::RoguelikeCoreChar, char_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
recruited = true;
|
||||
};
|
||||
|
||||
bool team_full_without_rookie = m_status->get_number(RuntimeStatus::RoguelikeTeamFullWithoutRookie).value_or(0);
|
||||
Log.info("team_full_without_rookie", team_full_without_rookie);
|
||||
|
||||
// 编队信息 (已有角色)
|
||||
std::string str_chars_info =
|
||||
m_status->get_str(RuntimeStatus::RoguelikeCharOverview).value_or(json::value().to_string());
|
||||
@@ -112,22 +115,26 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
// 干员已在编队中,又出现在招募列表,只有待晋升和预备干员两种情况
|
||||
if (recruit_info.is_alternate) {
|
||||
// 预备干员可以重复招募
|
||||
priority = recruit_info.recruit_priority;
|
||||
priority = team_full_without_rookie ? recruit_info.recruit_priority_when_team_full
|
||||
: recruit_info.recruit_priority;
|
||||
}
|
||||
else {
|
||||
// 干员待晋升
|
||||
priority = recruit_info.promote_priority;
|
||||
priority = team_full_without_rookie ? recruit_info.promote_priority_when_team_full
|
||||
: recruit_info.promote_priority;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 干员待招募,检查练度
|
||||
if (oper_info.elite == 2) {
|
||||
// TODO: 招募时已精二 (随机提升或对应分队) => promoted_priority
|
||||
priority = recruit_info.recruit_priority;
|
||||
priority = team_full_without_rookie ? recruit_info.recruit_priority_when_team_full
|
||||
: recruit_info.recruit_priority;
|
||||
}
|
||||
else if (oper_info.elite == 1 && oper_info.level >= 50) {
|
||||
// 精一50级以上
|
||||
priority = recruit_info.recruit_priority;
|
||||
priority = team_full_without_rookie ? recruit_info.recruit_priority_when_team_full
|
||||
: recruit_info.recruit_priority;
|
||||
}
|
||||
else {
|
||||
// 精一50级以下,默认不招募
|
||||
@@ -137,7 +144,7 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
}
|
||||
|
||||
// 优先级为0,可能练度不够被忽略
|
||||
if (priority == 0) {
|
||||
if (priority <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -311,11 +318,11 @@ bool asst::RoguelikeRecruitTaskPlugin::check_core_char()
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
auto core_opt = m_status->get_str("RoguelikeCoreChar");
|
||||
auto core_opt = m_status->get_str(RuntimeStatus::RoguelikeCoreChar);
|
||||
if (!core_opt || core_opt->empty()) {
|
||||
return false;
|
||||
}
|
||||
m_status->set_str("RoguelikeCoreChar", "");
|
||||
m_status->set_str(RuntimeStatus::RoguelikeCoreChar, "");
|
||||
return check_char(core_opt.value());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_rookie = false;
|
||||
for (const auto& [name, skill_vec] : analyzer.get_result()) {
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
@@ -47,8 +48,20 @@ bool asst::RoguelikeSkillSelectionTaskPlugin::_run()
|
||||
Log.info(__FUNCTION__, name, " select main skill:", oper_info.skill);
|
||||
m_ctrler->click(skill_vec.at(oper_info.skill - 1));
|
||||
}
|
||||
m_status->set_number("RoguelikeSkillUsage-" + name, static_cast<int>(oper_info.skill_usage));
|
||||
constexpr int RookieStd = 200;
|
||||
if (oper_info.promote_priority < RookieStd) {
|
||||
has_rookie = true;
|
||||
}
|
||||
m_status->set_number(RuntimeStatus::RoguelikeSkillUsagePrefix + name, static_cast<int>(oper_info.skill_usage));
|
||||
}
|
||||
|
||||
if (analyzer.get_team_full() && !has_rookie) {
|
||||
Log.info("Team full and no rookie");
|
||||
m_status->set_number(RuntimeStatus::RoguelikeTeamFullWithoutRookie, 1);
|
||||
}
|
||||
else {
|
||||
Log.info("Team not full or has rookie");
|
||||
m_status->set_number(RuntimeStatus::RoguelikeTeamFullWithoutRookie, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ namespace asst
|
||||
std::vector<ReplacementHome> replacement_home;
|
||||
std::unordered_set<Point> blacklist_location;
|
||||
std::vector<int> key_kills;
|
||||
std::array<BattleRole, 9> role_order;
|
||||
bool use_dice_stage = true;
|
||||
int stop_melee_deploy_num = INT_MAX;
|
||||
int deploy_ranged_num = 0;
|
||||
};
|
||||
|
||||
struct BattleCharData
|
||||
|
||||
Reference in New Issue
Block a user