feat.肉鸽新协议画饼,完成了读取配置部分的内容

This commit is contained in:
MistEO
2022-04-03 22:56:19 +08:00
parent 660135f93f
commit 57b21b06fc
15 changed files with 396 additions and 9 deletions

View File

@@ -0,0 +1,65 @@
# 肉鸽战斗流程协议
`resource/roguelike/*.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": "左"
}
]
}
```

View File

@@ -0,0 +1,54 @@
{
"minimum_required": "v4.0",
"desc": {
"title": "《摆完挂机 简单好抄》",
"details": "作业抄自 https://www.bilibili.com/video/BV12r4y1W7s4?p=10",
"strategy_author": "萧然Q",
"copilot_author": "Evil Deamo",
"version": "v1.0"
},
"stage_name": "兽群战术",
"opers": [
{
"name": "煌",
"skill": 2
}
],
"groups": [
{
"name": "普通单奶",
"opers": [
{
"name": "闪灵",
"skill": 2
},
{
"name": "华法琳",
"skill": 1
},
{
"name": "赫默2",
"skill": 1
}
]
}
],
"actions": [
{
"name": "煌",
"location": [
7,
3
],
"direction": "左"
},
{
"name": "普通单奶",
"location": [
9,
4
],
"direction": "左"
}
]
}

View File

@@ -0,0 +1,28 @@
{
"stage_name": "意外",
"actions": [
{
"location": [
5,
3
],
"roles": [
"近卫",
"先锋",
"重装",
"召唤物"
],
"direction": "左"
},
{
"location": [
5,
2
],
"roles": [
"辅助"
],
"direction": "左"
}
]
}

View File

@@ -3923,9 +3923,7 @@
100,
255
],
"templThreshold": 0.95,
"preDelay": 3000,
"rearDelay": 3000
"templThreshold": 0.95
},
"BattleSkillReady": {
"roi": [

View File

@@ -9,11 +9,18 @@ bool asst::AbstractConfiger::load(const std::string& filename)
{
LogTraceFunction;
if (!std::filesystem::exists(filename)) {
#ifdef WIN32
std::string cvt_filename = utils::utf8_to_gbk(filename);
#else
std::string cvt_filename = filename;
#endif
Log.info("Load:", cvt_filename);
if (!std::filesystem::exists(cvt_filename)) {
return false;
}
std::string content = utils::load_file_without_bom(filename);
std::string content = utils::load_file_without_bom(cvt_filename);
auto&& ret = json::parser::parse(content);
if (!ret) {

View File

@@ -47,7 +47,8 @@ namespace asst
Retreat, // 撤退干员
SkillUsage, // 技能用法
SwitchSpeed, // 切换二倍速
BulletTime // 使用 1/5 的速度(点击任意干员),会在下一个任意操作后恢复原速度
BulletTime, // 使用 1/5 的速度(点击任意干员),会在下一个任意操作后恢复原速度
UseAllSkill // 使用所有技能,仅肉鸽模式
};
struct BattleAction // 操作
@@ -98,4 +99,17 @@ namespace asst
return name == oper_name;
}
};
struct RoguelikeBattleAction
{
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;
};
}

View File

@@ -79,7 +79,12 @@ bool asst::BattleProcessTask::analyze_opers_preview()
std::string text = "( " + std::to_string(loc.x) + ", " + std::to_string(loc.y) + " )";
cv::putText(draw, text, cv::Point(info.pos.x - 30, info.pos.y), 1, 1.2, cv::Scalar(0, 0, 255), 2);
}
cv::imwrite(utils::utf8_to_gbk(m_stage_name) + ".png", draw);
#ifdef WIN32
std::string output_filename = utils::utf8_to_gbk(m_stage_name);
#else
std::string output_filename = m_stage_name;
#endif
cv::imwrite(output_filename + ".png", draw);
//#endif
// 干员头像出来之后,还要过 2 秒左右才可以点击,这里要加个延时

View File

@@ -19,7 +19,7 @@ namespace asst
protected:
virtual bool _run() override;
bool get_stage_info();
virtual bool get_stage_info();
bool battle_pause();
bool battle_speedup();
bool cancel_selection(); // 取消选择干员

View File

@@ -73,6 +73,7 @@
<ClInclude Include="ProcessTask.h" />
<ClInclude Include="ProcessTaskImageAnalyzer.h" />
<ClInclude Include="RecruitConfiger.h" />
<ClInclude Include="RoguelikeCopilotConfiger.h" />
<ClInclude Include="RoguelikeFormationTaskPlugin.h" />
<ClInclude Include="RoguelikeFormationImageAnalyzer.h" />
<ClInclude Include="RoguelikeRecruitTaskPlugin.h" />
@@ -147,6 +148,7 @@
<ClCompile Include="ProcessTask.cpp" />
<ClCompile Include="ProcessTaskImageAnalyzer.cpp" />
<ClCompile Include="RecruitConfiger.cpp" />
<ClCompile Include="RoguelikeCopilotConfiger.cpp" />
<ClCompile Include="RoguelikeFormationTaskPlugin.cpp" />
<ClCompile Include="RoguelikeFormationImageAnalyzer.cpp" />
<ClCompile Include="RoguelikeRecruitTaskPlugin.cpp" />

View File

@@ -318,6 +318,9 @@
<ClInclude Include="CombatRecruitConfiger.h">
<Filter>头文件\Resource</Filter>
</ClInclude>
<ClInclude Include="RoguelikeCopilotConfiger.h">
<Filter>头文件\Resource</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Controller.cpp">
@@ -533,6 +536,9 @@
<ClCompile Include="CombatRecruitConfiger.cpp">
<Filter>源文件\Resource</Filter>
</ClCompile>
<ClCompile Include="RoguelikeCopilotConfiger.cpp">
<Filter>源文件\Resource</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\resource\config.json">

View File

@@ -21,6 +21,7 @@ bool asst::Resource::load(const std::string& dir)
constexpr static const char* ItemCfgFilename = "item_index.json";
constexpr static const char* InfrastCfgFilename = "infrast.json";
constexpr static const char* CopilotCfgDirname = "copilot";
constexpr static const char* RoguelikeCfgDirname = "roguelike";
constexpr static const char* OcrResourceFilename = "PaddleOCR";
constexpr static const char* PenguinResourceFilename = "penguin-stats-recognize";
constexpr static const char* TilesCalcResourceFilename = "Arknights-Tile-Pos";
@@ -88,6 +89,16 @@ bool asst::Resource::load(const std::string& dir)
}
}
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().u8string())) {
m_last_error = entry.path().u8string() + " Load failed";
return false;
}
}
if (!m_infrast_cfg_unique_ins.load(dir + InfrastCfgFilename)) {
if (!m_loaded) {
m_last_error = std::string(InfrastCfgFilename) + ": " + m_infrast_cfg_unique_ins.get_last_error();

View File

@@ -16,6 +16,7 @@
#include "TemplResource.h"
#include "TilePack.h"
#include "CopilotConfiger.h"
#include "RoguelikeCopilotConfiger.h"
namespace asst
{
@@ -60,6 +61,10 @@ namespace asst
{
return m_copilot_cfg_unique_ins;
}
RoguelikeCopilotConfiger& roguelike() noexcept
{
return m_roguelike_cfg_unique_ins;
}
OcrPack& ocr() noexcept
{
return m_ocr_pack_unique_ins;
@@ -101,6 +106,10 @@ namespace asst
{
return m_copilot_cfg_unique_ins;
}
const RoguelikeCopilotConfiger& roguelike() const noexcept
{
return m_roguelike_cfg_unique_ins;
}
const OcrPack& ocr() const noexcept
{
return m_ocr_pack_unique_ins;
@@ -125,6 +134,7 @@ namespace asst
RecruitConfiger m_recruit_cfg_unique_ins;
CombatRecruitConfiger m_combatrecruit_cfg_unique_ins;
CopilotConfiger m_copilot_cfg_unique_ins;
RoguelikeCopilotConfiger m_roguelike_cfg_unique_ins;
ItemConfiger m_item_cfg_unique_ins;
InfrastConfiger m_infrast_cfg_unique_ins;
OcrPack m_ocr_pack_unique_ins;

View File

@@ -37,7 +37,7 @@ bool asst::RoguelikeBattleTaskPlugin::_run()
{
bool getted_info = get_stage_info();
speed_up();
//speed_up();
if (!getted_info) {
return true;

View File

@@ -0,0 +1,161 @@
#include "RoguelikeCopilotConfiger.h"
#include <meojson/json.hpp>
#include "Logger.hpp"
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);
}
}
}
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;
}
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_battle_actions[std::move(stage_name)] = std::move(actions_vec);
return true;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "AbstractConfiger.h"
#include "AsstBattleDef.h"
namespace asst
{
class RoguelikeCopilotConfiger : public AbstractConfiger
{
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();
}
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;
};
}