mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 02:10:21 +08:00
feat: 完成肉鸽 copilot
This commit is contained in:
@@ -1,65 +1,26 @@
|
||||
# 肉鸽战斗流程协议
|
||||
|
||||
`resource/roguelike/*.json` 的使用方法及各字段说明
|
||||
`resource/roguelike_copilot.json` 的使用方法及各字段说明
|
||||
|
||||
## 完整字段一览
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"stage_name": "驯兽小屋", // 关卡名,必选
|
||||
"actions": [ // 战斗中的操作。有序,执行完前一个才会去执行下一个。必选
|
||||
{
|
||||
"type": "部署", // 操作类型,可选,默认 "Deploy"
|
||||
// "Deploy" | "Skill" | "Retreat" | "AllSkill"
|
||||
// "部署" | "技能" | "撤退" | "全部技能"
|
||||
// 中英文皆可,效果相同
|
||||
// 若为 "部署", 当费用不够时,会一直等待到费用够
|
||||
// 若为 "技能", 当技能 cd 没转好时,会直接跳过当前项。
|
||||
// 考虑不同干员技能效果及 cd 差异巨大,谨慎使用(例如 山 2 技能 这种,假如已经开了,你又开一次,就给关了)
|
||||
// "全部技能", 会一次性使用所有 skill_usgae == 3 的干员的技能
|
||||
|
||||
"kills": 0, // 击杀数条件,如果没达到就一直等待。可选,默认为 0。直接执行
|
||||
// TODO: 其他条件
|
||||
// TODO: "condition_type": 0, // 执行条件间的关系,可选,默认 0
|
||||
// // 0 - 且; 1 - 或
|
||||
|
||||
"roles": [ // 该位置使用的职业。可选,默认为空,任何职业都可以
|
||||
// 有序,会先使用靠前的职业
|
||||
// 会根据位置类型自动区分是高台还是地面。若设置了不可放置的职业,则忽略该职业
|
||||
// 如果这里面的职业一个都没有,则会跳过该位置转而执行下一条
|
||||
"近卫",
|
||||
"先锋",
|
||||
"重装",
|
||||
"召唤物"
|
||||
],
|
||||
|
||||
"waiting_cost": false, // 如果有 roles 中有靠前的职业,但费用不够,是否等待。可选,默认 false
|
||||
// 为 true 时,会使用 roles 中,当前有的、最靠前的职业(一直等他费用够)
|
||||
// 为 false 时,会使用 roles 中,当前有的、费用够的中最靠前的职业
|
||||
|
||||
"location": [ 5, 5 ], // 操作的干员的位置。必选
|
||||
|
||||
"direction": "左", // 部署干员的干员朝向。 type 为 "部署" 时必选
|
||||
// "Left" | "Right" | "Up" | "Down" | "None"
|
||||
// "左" | "右" | "上" | "下" | "无"
|
||||
// 中英文皆可,效果相同
|
||||
|
||||
"pre_delay": 0, // 前置延时。可选,默认 0, 单位毫秒
|
||||
"rear_delay": 0, // 后置延时。可选,默认 0, 单位毫秒
|
||||
|
||||
"timeout": 999999999 // 超时时间。当 type 为 "部署" 时可选。默认 INT_MAX, 单位毫秒
|
||||
// 等待超时则放弃当前动作, 转而执行下一个动作
|
||||
},
|
||||
{ // 简单举例
|
||||
"location": [6, 6],
|
||||
"roles": [
|
||||
"近卫",
|
||||
"先锋",
|
||||
"重装",
|
||||
"召唤物"
|
||||
],
|
||||
"direction": "左"
|
||||
}
|
||||
]
|
||||
}
|
||||
[
|
||||
{
|
||||
"stage_name": "驯兽小屋", // 关卡名,必选
|
||||
"replacement_home": [ // 替换掉家门的位置,可选,默认为空
|
||||
[ 2, 8 ] // 会使用这里面的坐标来代替蓝门的坐标,然后堵门
|
||||
],
|
||||
"key_kills": [ // 关键击杀数,可选,默认为空
|
||||
15, // 若不为空,干员技能会攒着,不是好了就放,而是等到击杀数达到时再释放
|
||||
... // 当跑完数组里所有的元素之后,再把干员技能变成好了就放
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage_name": "巡逻队",
|
||||
"replacement_home": [
|
||||
[ 2, 8 ]
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"stage_name": "意外",
|
||||
"actions": [
|
||||
{
|
||||
"location": [
|
||||
5,
|
||||
3
|
||||
],
|
||||
"roles": [
|
||||
"近卫",
|
||||
"先锋",
|
||||
"重装",
|
||||
"召唤物"
|
||||
],
|
||||
"direction": "左"
|
||||
},
|
||||
{
|
||||
"location": [
|
||||
5,
|
||||
2
|
||||
],
|
||||
"roles": [
|
||||
"辅助"
|
||||
],
|
||||
"direction": "左"
|
||||
}
|
||||
]
|
||||
}
|
||||
20
resource/roguelike_copilot.json
Normal file
20
resource/roguelike_copilot.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"stage_name": "驯兽小屋",
|
||||
"replacement_home": [
|
||||
[
|
||||
8,
|
||||
3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage_name": "巡逻队",
|
||||
"replacement_home": [
|
||||
[
|
||||
8,
|
||||
3
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -37,11 +37,32 @@
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "陈",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "拉普兰德",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "阿米娅",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "史尔特尔",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "银灰",
|
||||
"level": 6,
|
||||
"skill": 3,
|
||||
"alternate_skill": 1
|
||||
},
|
||||
{
|
||||
"name": "赫拉格",
|
||||
"level": 6,
|
||||
@@ -53,7 +74,7 @@
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "拉普兰德",
|
||||
"name": "幽灵鲨",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
@@ -62,27 +83,11 @@
|
||||
"level": 4,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "陈",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "斯卡蒂",
|
||||
"level": 6,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "阿米娅",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "银灰",
|
||||
"level": 6,
|
||||
"skill": 3,
|
||||
"alternate_skill": 1
|
||||
},
|
||||
{
|
||||
"name": "Sharp",
|
||||
"level": 5,
|
||||
@@ -94,7 +99,7 @@
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "幽灵鲨",
|
||||
"name": "星极",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
@@ -105,9 +110,9 @@
|
||||
"alternate_skill": 2
|
||||
},
|
||||
{
|
||||
"name": "断罪者",
|
||||
"level": 4,
|
||||
"skill": 1
|
||||
"name": "柏喙",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "芳汀",
|
||||
@@ -115,9 +120,9 @@
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "星极",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
"name": "断罪者",
|
||||
"level": 4,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "战车",
|
||||
@@ -204,11 +209,6 @@
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "柏喙",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "布洛卡",
|
||||
"level": 5,
|
||||
@@ -246,11 +246,6 @@
|
||||
}
|
||||
],
|
||||
"Pioneer": [
|
||||
{
|
||||
"name": "芬",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "风笛",
|
||||
"level": 6,
|
||||
@@ -287,6 +282,11 @@
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "芬",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "清道夫",
|
||||
"level": 4,
|
||||
@@ -307,6 +307,21 @@
|
||||
"level": 4,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "红豆",
|
||||
"level": 4,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "苇草",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "格拉尼",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "琴柳",
|
||||
"level": 6,
|
||||
@@ -341,21 +356,6 @@
|
||||
"name": "翎羽",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "红豆",
|
||||
"level": 4,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "苇草",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "格拉尼",
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
}
|
||||
],
|
||||
"Tank": [
|
||||
@@ -367,8 +367,17 @@
|
||||
{
|
||||
"name": "塞雷娅",
|
||||
"level": 6,
|
||||
"skill": 2,
|
||||
"alternate_skill": 1
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "暮落",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "星熊",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "斑点",
|
||||
@@ -401,11 +410,6 @@
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "星熊",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "雷蛇",
|
||||
"level": 5,
|
||||
@@ -461,11 +465,6 @@
|
||||
"level": 4,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "暮落",
|
||||
"level": 5,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "极光",
|
||||
"level": 5,
|
||||
@@ -713,11 +712,6 @@
|
||||
"level": 6,
|
||||
"skill": 3
|
||||
},
|
||||
{
|
||||
"name": "伊芙利特",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "阿米娅",
|
||||
"level": 5,
|
||||
@@ -733,6 +727,11 @@
|
||||
"level": 6,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "伊芙利特",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "天火",
|
||||
"level": 5,
|
||||
@@ -998,11 +997,6 @@
|
||||
}
|
||||
],
|
||||
"Medic": [
|
||||
{
|
||||
"name": "安赛尔",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "凯尔希",
|
||||
"level": 6,
|
||||
@@ -1090,6 +1084,11 @@
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "安赛尔",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "闪灵",
|
||||
"level": 6,
|
||||
@@ -1122,22 +1121,12 @@
|
||||
}
|
||||
],
|
||||
"Support": [
|
||||
{
|
||||
"name": "浊心斯卡蒂",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "令",
|
||||
"level": 6,
|
||||
"skill": 3,
|
||||
"alternate_skill": 2
|
||||
},
|
||||
{
|
||||
"name": "梓兰",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "波登可",
|
||||
"level": 4,
|
||||
@@ -1148,16 +1137,17 @@
|
||||
"level": 6,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "安洁莉娜",
|
||||
"level": 6,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "铃兰",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "安洁莉娜",
|
||||
"level": 6,
|
||||
"skill": 3,
|
||||
"alternate_skill": 2
|
||||
},
|
||||
{
|
||||
"name": "巫恋",
|
||||
"level": 5,
|
||||
@@ -1178,6 +1168,16 @@
|
||||
"level": 5,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "梓兰",
|
||||
"level": 3,
|
||||
"skill": 1
|
||||
},
|
||||
{
|
||||
"name": "浊心斯卡蒂",
|
||||
"level": 6,
|
||||
"skill": 2
|
||||
},
|
||||
{
|
||||
"name": "月禾",
|
||||
"level": 5,
|
||||
|
||||
@@ -110,16 +110,10 @@ namespace asst
|
||||
}
|
||||
};
|
||||
|
||||
struct RoguelikeBattleAction
|
||||
struct RoguelikeBattleData
|
||||
{
|
||||
int kills = 0;
|
||||
BattleActionType type = BattleActionType::Deploy;
|
||||
std::vector<BattleRole> roles;
|
||||
bool waiting_cost = false;
|
||||
Point location;
|
||||
BattleDeployDirection direction = BattleDeployDirection::Right;
|
||||
int pre_delay = 0;
|
||||
int rear_delay = 0;
|
||||
int time_out = INT_MAX;
|
||||
std::string stage_name;
|
||||
std::vector<Point> replacement_home;
|
||||
std::vector<int> key_kills;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ bool asst::Resource::load(const std::string& dir)
|
||||
constexpr static auto InfrastCfgFilename = "infrast.json";
|
||||
constexpr static auto InfrastTempls = "template/infrast";
|
||||
//constexpr static const char* CopilotCfgDirname = "copilot";
|
||||
constexpr static auto RoguelikeCfgDirname = "roguelike";
|
||||
constexpr static auto RoguelikeCfgDirname = "roguelike_copilot.json";
|
||||
constexpr static auto OcrResourceFilename = "PaddleOCR";
|
||||
constexpr static auto TilesCalcResourceFilename = "Arknights-Tile-Pos";
|
||||
constexpr static auto StageDropsCfgFilename = "stages.json";
|
||||
@@ -91,17 +91,15 @@ bool asst::Resource::load(const std::string& dir)
|
||||
// }
|
||||
//}
|
||||
|
||||
if (std::filesystem::exists(dir + RoguelikeCfgDirname)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(dir + RoguelikeCfgDirname)) {
|
||||
if (entry.path().extension() != ".json") {
|
||||
continue;
|
||||
}
|
||||
if (!m_roguelike_cfg_unique_ins.load(entry.path().string())) {
|
||||
m_last_error = entry.path().string() + " Load failed";
|
||||
return false;
|
||||
}
|
||||
if (!m_roguelike_cfg_unique_ins.load(dir + RoguelikeCfgDirname)) {
|
||||
if (!m_loaded) {
|
||||
m_last_error = std::string(RoguelikeCfgDirname) + ": " + m_roguelike_cfg_unique_ins.get_last_error();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
overload = true;
|
||||
}
|
||||
|
||||
if (!m_infrast_cfg_unique_ins.load(dir + InfrastCfgFilename)) {
|
||||
if (!m_loaded) {
|
||||
|
||||
@@ -117,6 +117,40 @@ bool asst::RoguelikeBattleTaskPlugin::get_stage_info()
|
||||
calced = true;
|
||||
}
|
||||
|
||||
auto opt = Resrc.roguelike().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.find(home) == m_normal_tile_info.end()) {
|
||||
Log.error("No replacement home point", home.x, home.y);
|
||||
}
|
||||
log_str += "( " + std::to_string(home.x) + ", " + std::to_string(home.y) + " ), ";
|
||||
}
|
||||
log_str += "]";
|
||||
Log.info("replacement home:", log_str);
|
||||
}
|
||||
else {
|
||||
for (const auto& [loc, side] : m_normal_tile_info) {
|
||||
if (side.key == TilePack::TileKey::Home) {
|
||||
m_homes.emplace_back(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt && !opt->key_kills.empty()) {
|
||||
std::string log_str = "[ ";
|
||||
for (const auto& kills : opt->key_kills) {
|
||||
m_key_kills.emplace(kills);
|
||||
log_str += std::to_string(kills) + ", ";
|
||||
}
|
||||
log_str += "]";
|
||||
Log.info("key kills:", log_str);
|
||||
}
|
||||
|
||||
if (m_homes.empty()) {
|
||||
Log.error("Unknown home pos");
|
||||
}
|
||||
|
||||
if (calced) {
|
||||
auto cb_info = basic_info_with_what("StageInfo");
|
||||
auto& details = cb_info["details"];
|
||||
@@ -307,10 +341,14 @@ void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
m_opers_used = false;
|
||||
m_pre_hp = 0;
|
||||
m_homes.clear();
|
||||
decltype(m_key_kills) empty;
|
||||
m_key_kills.swap(empty);
|
||||
m_cur_home_index = 0;
|
||||
m_stage_name.clear();
|
||||
m_side_tile_info.clear();
|
||||
m_used_tiles.clear();
|
||||
m_kills = 0;
|
||||
m_total_kills = 0;
|
||||
|
||||
for (auto& [key, status] : m_restore_status) {
|
||||
m_status->set_number(key, status);
|
||||
@@ -320,6 +358,10 @@ void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
|
||||
{
|
||||
if (!check_key_kills(image)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto task_ptr = Task.get("BattleAutoSkillFlag");
|
||||
const Rect& skill_roi_move = task_ptr->rect_move;
|
||||
|
||||
@@ -356,6 +398,29 @@ bool asst::RoguelikeBattleTaskPlugin::try_possible_skill(const cv::Mat& image)
|
||||
return used;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::check_key_kills(const cv::Mat& image)
|
||||
{
|
||||
if (m_key_kills.empty()) {
|
||||
return true;
|
||||
}
|
||||
int need_kills = m_key_kills.front();
|
||||
|
||||
BattleImageAnalyzer analyzer(image);
|
||||
if (m_total_kills) {
|
||||
analyzer.set_pre_total_kills(m_total_kills);
|
||||
}
|
||||
analyzer.set_target(BattleImageAnalyzer::Target::Kills);
|
||||
if (analyzer.analyze()) {
|
||||
m_kills = analyzer.get_kills();
|
||||
m_total_kills = analyzer.get_total_kills();
|
||||
if (m_kills >= need_kills) {
|
||||
m_key_kills.pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::wait_start()
|
||||
{
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
@@ -392,6 +457,15 @@ bool asst::RoguelikeBattleTaskPlugin::wait_start()
|
||||
std::string text = "( " + std::to_string(loc.x) + ", " + std::to_string(loc.y) + " )";
|
||||
cv::putText(image, text, cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2);
|
||||
}
|
||||
|
||||
// 识别一帧总击杀数
|
||||
BattleImageAnalyzer kills_analyzer(image);
|
||||
kills_analyzer.set_target(BattleImageAnalyzer::Target::Kills);
|
||||
if (kills_analyzer.analyze()) {
|
||||
m_kills = kills_analyzer.get_kills();
|
||||
m_total_kills = kills_analyzer.get_total_kills();
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
cv::imwrite("map/" + utils::utf8_to_ansi(m_stage_name) + ".png", image);
|
||||
#else
|
||||
@@ -449,16 +523,6 @@ asst::RoguelikeBattleTaskPlugin::DeployInfo asst::RoguelikeBattleTaskPlugin::cal
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_homes.empty()) {
|
||||
for (const auto& [loc, side] : m_side_tile_info) {
|
||||
if (side.key == TilePack::TileKey::Home) {
|
||||
m_homes.emplace_back(loc);
|
||||
}
|
||||
}
|
||||
if (m_homes.empty()) {
|
||||
Log.error("Unknown home pos");
|
||||
}
|
||||
}
|
||||
if (m_cur_home_index >= m_homes.size()) {
|
||||
m_cur_home_index = 0;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace asst
|
||||
bool retreat(const Point& point);
|
||||
void clear();
|
||||
bool try_possible_skill(const cv::Mat& image);
|
||||
bool check_key_kills(const cv::Mat& image);
|
||||
bool wait_start();
|
||||
|
||||
struct DeployInfo
|
||||
@@ -46,10 +47,13 @@ namespace asst
|
||||
|
||||
bool m_opers_used = false;
|
||||
int m_pre_hp = 0;
|
||||
int m_kills = 0;
|
||||
int m_total_kills = 0;
|
||||
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info;
|
||||
std::vector<Point> m_homes;
|
||||
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<std::string, int64_t> m_restore_status;
|
||||
|
||||
@@ -4,158 +4,34 @@
|
||||
|
||||
#include "Logger.hpp"
|
||||
|
||||
std::optional<asst::RoguelikeBattleData> asst::RoguelikeCopilotConfiger::get_stage_data(const std::string& stage_name) const
|
||||
{
|
||||
auto it = m_stage_data.find(stage_name);
|
||||
if (it == m_stage_data.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeCopilotConfiger::parse(const json::value& json)
|
||||
{
|
||||
std::string stage_name = json.at("stage_name").as_string();
|
||||
|
||||
std::vector<RoguelikeBattleAction> actions_vec;
|
||||
|
||||
for (const auto& action_info : json.at("actions").as_array()) {
|
||||
RoguelikeBattleAction action;
|
||||
|
||||
static const std::unordered_map<std::string, BattleActionType> ActionTypeMapping = {
|
||||
{ "Deploy", BattleActionType::Deploy },
|
||||
{ "DEPLOY", BattleActionType::Deploy },
|
||||
{ "deploy", BattleActionType::Deploy },
|
||||
{ "部署", BattleActionType::Deploy },
|
||||
|
||||
{ "Skill", BattleActionType::UseSkill },
|
||||
{ "SKILL", BattleActionType::UseSkill },
|
||||
{ "skill", BattleActionType::UseSkill },
|
||||
{ "技能", BattleActionType::UseSkill },
|
||||
|
||||
{ "Retreat", BattleActionType::Retreat },
|
||||
{ "RETREAT", BattleActionType::Retreat },
|
||||
{ "retreat", BattleActionType::Retreat },
|
||||
{ "撤退", BattleActionType::Retreat },
|
||||
|
||||
{ "AllSkill", BattleActionType::UseAllSkill },
|
||||
{ "allskill", BattleActionType::UseAllSkill },
|
||||
{ "ALLSKILL", BattleActionType::UseAllSkill },
|
||||
{ "Allskill", BattleActionType::UseAllSkill },
|
||||
{ "全部技能", BattleActionType::UseAllSkill },
|
||||
};
|
||||
|
||||
std::string type_str = action_info.get("type", "Deploy");
|
||||
|
||||
if (auto iter = ActionTypeMapping.find(type_str);
|
||||
iter != ActionTypeMapping.end()) {
|
||||
action.type = iter->second;
|
||||
}
|
||||
else {
|
||||
action.type = BattleActionType::Deploy;
|
||||
}
|
||||
action.kills = action_info.get("kills", 0);
|
||||
|
||||
if (action_info.contains("roles")) {
|
||||
for (const auto& role_info : action_info.at("roles").as_array()) {
|
||||
static const std::unordered_map<std::string, BattleRole> RoleMapping = {
|
||||
{ "MEDIC", BattleRole::Medic },
|
||||
{ "Medic", BattleRole::Medic },
|
||||
{ "medic", BattleRole::Medic },
|
||||
{ "医疗", BattleRole::Medic },
|
||||
|
||||
{ "WARRIOR", BattleRole::Warrior },
|
||||
{ "Warrior", BattleRole::Warrior },
|
||||
{ "warrior", BattleRole::Warrior },
|
||||
{ "近卫", BattleRole::Warrior },
|
||||
|
||||
{ "SPECIAL", BattleRole::Special },
|
||||
{ "Special", BattleRole::Special },
|
||||
{ "special", BattleRole::Special },
|
||||
{ "特种", BattleRole::Special },
|
||||
|
||||
{ "SNIPER", BattleRole::Sniper },
|
||||
{ "Sniper", BattleRole::Sniper },
|
||||
{ "sniper", BattleRole::Sniper },
|
||||
{ "狙击", BattleRole::Sniper },
|
||||
|
||||
{ "PIONEER", BattleRole::Pioneer },
|
||||
{ "Pioneer", BattleRole::Pioneer },
|
||||
{ "pioneer", BattleRole::Pioneer },
|
||||
{ "先锋", BattleRole::Pioneer },
|
||||
|
||||
{ "TANK", BattleRole::Tank },
|
||||
{ "Tank", BattleRole::Tank },
|
||||
{ "tank", BattleRole::Tank },
|
||||
{ "重装", BattleRole::Tank },
|
||||
|
||||
{ "SUPPORT", BattleRole::Support },
|
||||
{ "Support", BattleRole::Support },
|
||||
{ "support", BattleRole::Support },
|
||||
{ "辅助", BattleRole::Support },
|
||||
|
||||
{ "CASTER", BattleRole::Caster },
|
||||
{ "Caster", BattleRole::Caster },
|
||||
{ "caster", BattleRole::Caster },
|
||||
{ "术师", BattleRole::Caster },
|
||||
|
||||
{ "Drone", BattleRole::Drone },
|
||||
{ "DRONE", BattleRole::Drone },
|
||||
{ "drone", BattleRole::Drone },
|
||||
{ "召唤物", BattleRole::Drone },
|
||||
{ "无人机", BattleRole::Drone }
|
||||
};
|
||||
|
||||
std::string role_name = role_info.as_string();
|
||||
if (auto iter = RoleMapping.find(role_name);
|
||||
iter != RoleMapping.end()) {
|
||||
action.roles.emplace_back(iter->second);
|
||||
}
|
||||
else {
|
||||
Log.error("Unknown role", role_name);
|
||||
}
|
||||
for (const auto& stage_info : json.as_array()) {
|
||||
std::string stage_name = stage_info.at("stage_name").as_string();
|
||||
RoguelikeBattleData data;
|
||||
data.stage_name = stage_name;
|
||||
if (stage_info.contains("replacement_home")) {
|
||||
for (const auto& points : stage_info.at("replacement_home").as_array()) {
|
||||
const auto& points_array = points.as_array();
|
||||
Point point(points_array.at(0).as_integer(), points_array.at(1).as_integer());
|
||||
data.replacement_home.push_back(point);
|
||||
}
|
||||
}
|
||||
|
||||
action.location.x = action_info.get("location", 0, 0);
|
||||
action.location.y = action_info.get("location", 1, 0);
|
||||
|
||||
static const std::unordered_map<std::string, BattleDeployDirection> DeployDirectionMapping = {
|
||||
{ "Right", BattleDeployDirection::Right },
|
||||
{ "RIGHT", BattleDeployDirection::Right },
|
||||
{ "right", BattleDeployDirection::Right },
|
||||
{ "右", BattleDeployDirection::Right },
|
||||
|
||||
{ "Left", BattleDeployDirection::Left },
|
||||
{ "LEFT", BattleDeployDirection::Left },
|
||||
{ "left", BattleDeployDirection::Left },
|
||||
{ "左", BattleDeployDirection::Left },
|
||||
|
||||
{ "Up", BattleDeployDirection::Up },
|
||||
{ "UP", BattleDeployDirection::Up },
|
||||
{ "up", BattleDeployDirection::Up },
|
||||
{ "上", BattleDeployDirection::Up },
|
||||
|
||||
{ "Down", BattleDeployDirection::Down },
|
||||
{ "DOWN", BattleDeployDirection::Down },
|
||||
{ "down", BattleDeployDirection::Down },
|
||||
{ "下", BattleDeployDirection::Down },
|
||||
|
||||
{ "None", BattleDeployDirection::None },
|
||||
{ "NONE", BattleDeployDirection::None },
|
||||
{ "none", BattleDeployDirection::None },
|
||||
{ "无", BattleDeployDirection::None },
|
||||
};
|
||||
|
||||
std::string direction_str = action_info.get("direction", "Right");
|
||||
|
||||
if (auto iter = DeployDirectionMapping.find(direction_str);
|
||||
iter != DeployDirectionMapping.end()) {
|
||||
action.direction = iter->second;
|
||||
if (stage_info.contains("key_kills")) {
|
||||
for (const auto& key_kill : stage_info.at("key_kills").as_array()) {
|
||||
data.key_kills.emplace_back(key_kill.as_integer());
|
||||
}
|
||||
}
|
||||
else {
|
||||
action.direction = BattleDeployDirection::Right;
|
||||
}
|
||||
action.waiting_cost = action_info.get("waiting_cost", false);
|
||||
action.pre_delay = action_info.get("pre_delay", 0);
|
||||
action.rear_delay = action_info.get("rear_delay", 0);
|
||||
action.time_out = action_info.get("timeout", INT_MAX);
|
||||
|
||||
actions_vec.emplace_back(std::move(action));
|
||||
m_stage_data.emplace(std::move(stage_name), std::move(data));
|
||||
}
|
||||
|
||||
m_battle_actions[std::move(stage_name)] = std::move(actions_vec);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "AbstractConfiger.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "AsstBattleDef.h"
|
||||
|
||||
namespace asst
|
||||
@@ -10,17 +12,10 @@ namespace asst
|
||||
public:
|
||||
virtual ~RoguelikeCopilotConfiger() = default;
|
||||
|
||||
bool contains_actions(const std::string& stage_name) const noexcept
|
||||
{
|
||||
return m_battle_actions.find(stage_name) != m_battle_actions.cend();
|
||||
}
|
||||
std::optional<RoguelikeBattleData> get_stage_data(const std::string& stage_name) const;
|
||||
|
||||
auto get_actions(const std::string& stage_name) const noexcept
|
||||
{
|
||||
return m_battle_actions.at(stage_name);
|
||||
}
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
std::unordered_map<std::string, std::vector<RoguelikeBattleAction>> m_battle_actions;
|
||||
std::unordered_map<std::string, RoguelikeBattleData> m_stage_data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,14 +40,26 @@ bool asst::RoguelikeRecruitTaskPlugin::_run()
|
||||
};
|
||||
|
||||
for (const auto& info : analyzer.get_result()) {
|
||||
if (info.elite == 0 ||
|
||||
(info.elite == 1 && info.level < 50)) {
|
||||
// 先看看有没有精二的
|
||||
if (info.elite != 2) {
|
||||
continue;
|
||||
}
|
||||
recruit_oper(info);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!recruited) {
|
||||
for (const auto& info : analyzer.get_result()) {
|
||||
// 拿个精一 50 以上的
|
||||
if (info.elite == 0 ||
|
||||
(info.elite == 1 && info.level < 50)) {
|
||||
continue;
|
||||
}
|
||||
recruit_oper(info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!recruited) {
|
||||
Log.info("All are lower");
|
||||
// 随便招个精一的
|
||||
|
||||
Reference in New Issue
Block a user