mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-20 02:55:38 +08:00
feat.修改战斗流程协议,并完成 parser
This commit is contained in:
137
docs/战斗流程协议.md
137
docs/战斗流程协议.md
@@ -1,80 +1,83 @@
|
||||
# 战斗流程协议
|
||||
|
||||
`resource/battle.json` 的使用方法及各字段说明
|
||||
`resource/battle/*.json` 的使用方法及各字段说明
|
||||
|
||||
## 完整字段一览
|
||||
|
||||
```jsonc
|
||||
[
|
||||
{
|
||||
"stage_name": "暴君", // 关卡名
|
||||
"stage_code": "1-7", // 关卡号,与关卡名二选一必选
|
||||
"opers": [ // 指定干员
|
||||
{
|
||||
"name": "棘刺", // 干员名
|
||||
{
|
||||
"stage_name": "暴君", // 关卡名,必选
|
||||
"opers": [ // 指定干员
|
||||
{
|
||||
"name": "棘刺", // 干员名
|
||||
|
||||
"skill": 3, // 技能序号。可选,默认 1,取值范围 [1, 3]
|
||||
"skill": 3, // 技能序号。可选,默认 1,取值范围 [1, 3]
|
||||
|
||||
"skill_usage": 0 // 技能用法。可选,默认 0
|
||||
// 0 - 好了就用,有多少次用多少次(例如干员 棘刺 3 技能、桃金娘 1 技能等)
|
||||
// 1 - 好了就用,仅使用一次(例如干员 山 2 技能)
|
||||
// 2 - 指定时刻使用(依赖“波数依赖”字段)
|
||||
// 如果是全自动的技能,可以不填写该字段
|
||||
}
|
||||
],
|
||||
"opers_group": [
|
||||
{
|
||||
"name": "任意正常群奶", // 群组名,必选
|
||||
// 自己随便取名字,与下面 deploy 中的 name 对应起来就行
|
||||
"skill_usage": 0 // 技能用法。可选,默认 0
|
||||
// 0 - 好了就用,有多少次用多少次(例如干员 棘刺 3 技能、桃金娘 1 技能等)
|
||||
// 1 - 好了就用,仅使用一次(例如干员 山 2 技能)
|
||||
// 2 - 自动判断使用时机(画饼.jpg)
|
||||
// 3 - 不自动使用(依赖 "kills_actions" 字段)
|
||||
// 如果是全自动的技能,随便填哪个都一样,也可以不填
|
||||
},
|
||||
],
|
||||
"opers_groups": [
|
||||
{
|
||||
"name": "任意正常群奶", // 群组名,必选
|
||||
// 自己随便取名字,与下面 deploy 中的 name 对应起来就行
|
||||
|
||||
"opers": [ // 干员任选其一,无序,会优先选练度高的,用法同上述 opers 字段
|
||||
{
|
||||
"name": "夜莺",
|
||||
"skill": 3,
|
||||
"skill_usage": 2 // 若是指定时刻,不同的干员技能 cd 可能不同,请留意
|
||||
},
|
||||
{
|
||||
"name": "白面鸮",
|
||||
"skill": 2,
|
||||
"skill_usage": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"auto_deploy": [ // 自动部署干员,有费就下。有序,可选
|
||||
{
|
||||
"name": "棘刺", // 干员名 或 群组名。必选
|
||||
"location": [ 5, 5 ], // 放置干员的位置。必选
|
||||
"opers": [ // 干员任选其一,无序,会优先选练度高的,用法同上述 opers 字段
|
||||
{
|
||||
"name": "夜莺",
|
||||
"skill": 3,
|
||||
"skill_usage": 2 // 若是指定时刻,不同的干员技能 cd 可能不同,请留意
|
||||
},
|
||||
{
|
||||
"name": "白面鸮",
|
||||
"skill": 2,
|
||||
"skill_usage": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [ // 战斗中的操作。有序,执行完前一个才会去执行下一个。必选
|
||||
{
|
||||
"type": 0, // 操作类型,可选,默认 0
|
||||
// 0 - 部署; 1 - 撤退; 2 - 开技能;
|
||||
// 100 - 切换二倍速开关;101 - 切换 1/5 速度(点击任意干员)
|
||||
// 若为 0, 当费用不够时,会一直等待到费用够(除非 timeout)
|
||||
// 若为 2, 当技能 cd 没转好时,一直等待到技能 cd 好(除非 timeout)
|
||||
|
||||
"direction": 1, // 干员朝向。必选
|
||||
// 0 - 无朝向(适用于召唤物)
|
||||
// 1 - 右; 2 - 下; 3 - 左; 4 - 上
|
||||
"kills_condition": 0, // 击杀数条件,如果没达到就一直等待,可选,默认为空。直接执行
|
||||
// TODO: 其他条件
|
||||
// TODO: "condition_type": 0, // 执行条件间的关系,可选,默认 0
|
||||
// // 0 - 且; 1 - 或
|
||||
|
||||
"preDelay": 0, // 轮到该干员时,等待多长时间再放。可选,默认 0
|
||||
"rearDelay": 0 // 放完该干员后,等待多少时间再到下一个干员。可选,默认 0
|
||||
},
|
||||
{
|
||||
"name": "任意正常群奶",
|
||||
"location": [ 5, 6 ],
|
||||
"direction": 1,
|
||||
"preDelay": 0,
|
||||
"rearDelay": 0
|
||||
}
|
||||
],
|
||||
"kills_deploy": [ // 依赖击杀数的操作。有序,可选
|
||||
{
|
||||
"kills": 0, // 击杀数 >= N 的时候,进行该操作,必选
|
||||
"action": 0, // 操作类型,必选。
|
||||
// 0 - 开技能;1 - 部署;2 - 撤退;
|
||||
// 100 - 切换二倍速开关;101 - 切换 1/5 速度(点击任意干员)
|
||||
"name": "棘刺", // 干员名 或 群组名,action < 100 时必选
|
||||
|
||||
"name": "史尔特尔", // 干员名,action ∈ [0, 100) 时必选。
|
||||
"preDelay": 0 // 击杀数达到后,等待多长时间再进行操作。可选,默认 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{ // 其他关卡...
|
||||
"stage_name": "机密情报押运"
|
||||
}
|
||||
]
|
||||
"location": [ 5, 5 ], // 部署干员的位置。action == 0 时必选
|
||||
|
||||
"direction": 0, // 部署干员的干员朝向。action == 0 时必选
|
||||
// 0 - 右; 1 - 下; 2 - 左; 3 - 上
|
||||
// 4 - 无朝向(适用于召唤物)
|
||||
|
||||
"pre_delay": 0, // 前置延时。可选,默认 0, 单位毫秒
|
||||
"rear_delay": 0, // 后置延时。可选,默认 0, 单位毫秒
|
||||
|
||||
"timeout": 999999999 // 超时时间。可选,默认 INT_MAX, 单位毫秒
|
||||
// 当 type == 0 或 type == 1 且等待超时
|
||||
// 则放弃当前动作, 转而执行下一个动作
|
||||
},
|
||||
// 举例 1
|
||||
{
|
||||
"name": "任意正常群奶",
|
||||
"location": [ 5, 6 ],
|
||||
"direction": 1
|
||||
},
|
||||
// 举例 2
|
||||
{
|
||||
"type": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
0
resource/battle.json
Normal file
0
resource/battle.json
Normal file
61
src/MeoAssistant/AsstBattleDef.h
Normal file
61
src/MeoAssistant/AsstBattleDef.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AsstTypes.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
enum class BattleSkillUsage // 干员技能使用方法
|
||||
{
|
||||
Possibly = 0, // 有就用,例如干员 棘刺 3 技能
|
||||
Once = 1, // 只用一次,例如干员 银灰 2技能
|
||||
InTime = 2, // 关键时刻使用,自动判断使用时机,例如干员 假日威龙陈 3 技能
|
||||
NotUse = 3, // 不自动使用
|
||||
OnceUsed
|
||||
};
|
||||
struct BattleOper // 干员
|
||||
{
|
||||
std::string name;
|
||||
int skill = 1; // 技能序号,取值范围 [1, 3]
|
||||
BattleSkillUsage skill_usage = BattleSkillUsage::Possibly;
|
||||
};
|
||||
|
||||
enum class DeployDirection
|
||||
{
|
||||
Right = 0,
|
||||
Down = 1,
|
||||
Left = 2,
|
||||
Up = 3,
|
||||
No = 4 // 没有方向,通常是无人机之类的
|
||||
};
|
||||
|
||||
enum class BattleActionType // 操作类型
|
||||
{
|
||||
Deploy = 0, // 部署干员
|
||||
Retreat = 1, // 撤退干员
|
||||
UseSkill = 2, // 开技能
|
||||
SwitchSpeed = 100, // 切换二倍速
|
||||
SlowMode = 101 // 使用 1/5 的速度(点击任意干员),会在下一个任意操作后恢复原速度
|
||||
};
|
||||
|
||||
struct BattleAction // 操作
|
||||
{
|
||||
int kills_condition = 0;
|
||||
BattleActionType type = BattleActionType::Deploy;
|
||||
std::string group_name; // 目标名,若 type >= SwitchSpeed, group_name 为空
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
int pre_delay = 0;
|
||||
int rear_delay = 0;
|
||||
int time_out = INT_MAX;
|
||||
};
|
||||
|
||||
struct BattleActions
|
||||
{
|
||||
std::unordered_map<std::string, std::vector<BattleOper>> opers_groups;
|
||||
std::vector<BattleAction> actions;
|
||||
};
|
||||
}
|
||||
62
src/MeoAssistant/BattleConfiger.cpp
Normal file
62
src/MeoAssistant/BattleConfiger.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "BattleConfiger.h"
|
||||
|
||||
#include <meojson/json.hpp>
|
||||
|
||||
#include "Logger.hpp"
|
||||
|
||||
bool asst::BattleConfiger::parse(const json::value& json)
|
||||
{
|
||||
std::string stage_name = json.at("stage_name").as_string();
|
||||
|
||||
BattleActions battle_actions;
|
||||
|
||||
if (json.contains("opers_groups")) {
|
||||
for (const auto& group_info : json.at("opers_groups").as_array()) {
|
||||
std::string group_name = group_info.at("name").as_string();
|
||||
std::vector<BattleOper> oper_vec;
|
||||
for (const auto& oper_info : group_info.at("opers").as_array()) {
|
||||
BattleOper oper;
|
||||
oper.name = oper_info.at("name").as_string();
|
||||
oper.skill = oper_info.get("skill", 1);
|
||||
oper.skill_usage = static_cast<BattleSkillUsage>(oper_info.get("skill_usage", 0));
|
||||
oper_vec.emplace_back(std::move(oper));
|
||||
}
|
||||
battle_actions.opers_groups.emplace(std::move(group_name), std::move(oper_vec));
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("opers")) {
|
||||
for (const auto& oper_info : json.at("opers").as_array()) {
|
||||
BattleOper oper;
|
||||
oper.name = oper_info.at("name").as_string();
|
||||
oper.skill = oper_info.get("skill", 1);
|
||||
oper.skill_usage = static_cast<BattleSkillUsage>(oper_info.get("skill_usage", 0));
|
||||
|
||||
// 单个干员的,干员名直接作为组名
|
||||
std::string group_name = oper.name;
|
||||
|
||||
battle_actions.opers_groups.emplace(std::move(group_name), std::vector{ std::move(oper) });
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& action_info : json.at("actions").as_array()) {
|
||||
BattleAction action;
|
||||
action.type = static_cast<BattleActionType>(action_info.get("type", 0));
|
||||
action.kills_condition = action_info.get("kills_condition", 0);
|
||||
action.group_name = action_info.get("name", std::string());
|
||||
|
||||
action.location.x = action_info.get("location", 0, 0);
|
||||
action.location.y = action_info.get("location", 1, 0);
|
||||
|
||||
action.direction = static_cast<DeployDirection>(action_info.get("direction", 0));
|
||||
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);
|
||||
|
||||
battle_actions.actions.emplace_back(std::move(action));
|
||||
}
|
||||
|
||||
m_battle_actions[std::move(stage_name)] = std::move(battle_actions);
|
||||
|
||||
return true;
|
||||
}
|
||||
15
src/MeoAssistant/BattleConfiger.h
Normal file
15
src/MeoAssistant/BattleConfiger.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "AbstractConfiger.h"
|
||||
#include "AsstBattleDef.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class BattleConfiger : public AbstractConfiger
|
||||
{
|
||||
public:
|
||||
virtual ~BattleConfiger() = default;
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
std::unordered_map<std::string, BattleActions> m_battle_actions;
|
||||
};
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
<ClInclude Include="AsstMsg.h" />
|
||||
<ClInclude Include="AutoRecruitTask.h" />
|
||||
<ClInclude Include="AwardTask.h" />
|
||||
<ClInclude Include="BattleConfiger.h" />
|
||||
<ClInclude Include="BattleImageAnalyzer.h" />
|
||||
<ClInclude Include="BattlePerspectiveImageAnalyzer.h" />
|
||||
<ClInclude Include="DebugTask.h" />
|
||||
@@ -94,6 +95,7 @@
|
||||
<ClCompile Include="AbstractTaskPlugin.cpp" />
|
||||
<ClCompile Include="AutoRecruitTask.cpp" />
|
||||
<ClCompile Include="AwardTask.cpp" />
|
||||
<ClCompile Include="BattleConfiger.cpp" />
|
||||
<ClCompile Include="BattleImageAnalyzer.cpp" />
|
||||
<ClCompile Include="BattlePerspectiveImageAnalyzer.cpp" />
|
||||
<ClCompile Include="DebugTask.cpp" />
|
||||
|
||||
@@ -306,6 +306,9 @@
|
||||
<ClInclude Include="ReplenishOriginiumShardTaskPlugin.h">
|
||||
<Filter>头文件\Task\Plugin</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BattleConfiger.h">
|
||||
<Filter>头文件\Resource</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Controller.cpp">
|
||||
@@ -509,6 +512,9 @@
|
||||
<ClCompile Include="ReplenishOriginiumShardTaskPlugin.cpp">
|
||||
<Filter>源文件\Task\Plugin</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BattleConfiger.cpp">
|
||||
<Filter>源文件\Resource</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\resource\config.json">
|
||||
|
||||
@@ -11,336 +11,336 @@
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::verify(AsstMsg msg, const json::value& details) const
|
||||
{
|
||||
if (msg != AsstMsg::SubTaskCompleted
|
||||
|| details.get("subtask", std::string()) != "ProcessTask") {
|
||||
return false;
|
||||
}
|
||||
if (msg != AsstMsg::SubTaskCompleted
|
||||
|| details.get("subtask", std::string()) != "ProcessTask") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (details.at("details").at("task").as_string() == "Roguelike1StartAction") {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
if (details.at("details").at("task").as_string() == "Roguelike1StartAction") {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void asst::RoguelikeBattleTaskPlugin::set_skill_usage(SkillUsageMap usage_map)
|
||||
{
|
||||
m_skill_usage = std::move(usage_map);
|
||||
m_skill_usage = std::move(usage_map);
|
||||
}
|
||||
|
||||
void asst::RoguelikeBattleTaskPlugin::set_stage_name(std::string stage)
|
||||
{
|
||||
m_stage_name = std::move(stage);
|
||||
m_stage_name = std::move(stage);
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::_run()
|
||||
{
|
||||
bool getted_info = get_stage_info();
|
||||
bool getted_info = get_stage_info();
|
||||
|
||||
speed_up();
|
||||
speed_up();
|
||||
|
||||
if (!getted_info) {
|
||||
return true;
|
||||
}
|
||||
if (!getted_info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (!need_exit()) {
|
||||
// 不在战斗场景,且已使用过了干员,说明已经打完了,就结束循环
|
||||
if (!auto_battle() && m_opers_used) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!need_exit()) {
|
||||
// 不在战斗场景,且已使用过了干员,说明已经打完了,就结束循环
|
||||
if (!auto_battle() && m_opers_used) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
clear();
|
||||
clear();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::get_stage_info()
|
||||
{
|
||||
LogTraceFunction;
|
||||
LogTraceFunction;
|
||||
|
||||
const auto& tile = Resrc.tile();
|
||||
bool calced = false;
|
||||
const auto& tile = Resrc.tile();
|
||||
bool calced = false;
|
||||
|
||||
if (m_stage_name.empty()) {
|
||||
const auto stage_name_task_ptr = Task.get("BattleStageName");
|
||||
sleep(stage_name_task_ptr->pre_delay);
|
||||
if (m_stage_name.empty()) {
|
||||
const auto stage_name_task_ptr = Task.get("BattleStageName");
|
||||
sleep(stage_name_task_ptr->pre_delay);
|
||||
|
||||
constexpr int StageNameRetryTimes = 50;
|
||||
for (int i = 0; i != StageNameRetryTimes; ++i) {
|
||||
cv::Mat image = m_ctrler->get_image();
|
||||
OcrImageAnalyzer name_analyzer(image);
|
||||
constexpr int StageNameRetryTimes = 50;
|
||||
for (int i = 0; i != StageNameRetryTimes; ++i) {
|
||||
cv::Mat image = m_ctrler->get_image();
|
||||
OcrImageAnalyzer name_analyzer(image);
|
||||
|
||||
name_analyzer.set_task_info(stage_name_task_ptr);
|
||||
if (!name_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
name_analyzer.set_task_info(stage_name_task_ptr);
|
||||
if (!name_analyzer.analyze()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& tr : name_analyzer.get_result()) {
|
||||
auto side_info = tile.calc(tr.text, true);
|
||||
if (side_info.empty()) {
|
||||
continue;
|
||||
}
|
||||
m_side_tile_info = std::move(side_info);
|
||||
m_normal_tile_info = tile.calc(tr.text, false);
|
||||
m_stage_name = tr.text;
|
||||
calced = true;
|
||||
break;
|
||||
}
|
||||
if (calced) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_side_tile_info = tile.calc(m_stage_name, true);
|
||||
calced = true;
|
||||
}
|
||||
for (const auto& tr : name_analyzer.get_result()) {
|
||||
auto side_info = tile.calc(tr.text, true);
|
||||
if (side_info.empty()) {
|
||||
continue;
|
||||
}
|
||||
m_side_tile_info = std::move(side_info);
|
||||
m_normal_tile_info = tile.calc(tr.text, false);
|
||||
m_stage_name = tr.text;
|
||||
calced = true;
|
||||
break;
|
||||
}
|
||||
if (calced) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_side_tile_info = tile.calc(m_stage_name, true);
|
||||
calced = true;
|
||||
}
|
||||
|
||||
if (calced) {
|
||||
if (calced) {
|
||||
#ifdef ASST_DEBUG
|
||||
auto normal_tiles = tile.calc(m_stage_name, false);
|
||||
cv::Mat draw = m_ctrler->get_image();
|
||||
for (const auto& [point, info] : normal_tiles) {
|
||||
using TileKey = TilePack::TileKey;
|
||||
static const std::unordered_map<TileKey, std::string> TileKeyMapping = {
|
||||
{ TileKey::Invalid, "invalid" },
|
||||
{ TileKey::Forbidden, "forbidden" },
|
||||
{ TileKey::Wall, "wall" },
|
||||
{ TileKey::Road, "road" },
|
||||
{ TileKey::Home, "end" },
|
||||
{ TileKey::EnemyHome, "start" },
|
||||
{ TileKey::Floor, "floor" },
|
||||
{ TileKey::Hole, "hole" },
|
||||
{ TileKey::Telin, "telin" },
|
||||
{ TileKey::Telout, "telout" }
|
||||
};
|
||||
auto normal_tiles = tile.calc(m_stage_name, false);
|
||||
cv::Mat draw = m_ctrler->get_image();
|
||||
for (const auto& [point, info] : normal_tiles) {
|
||||
using TileKey = TilePack::TileKey;
|
||||
static const std::unordered_map<TileKey, std::string> TileKeyMapping = {
|
||||
{ TileKey::Invalid, "invalid" },
|
||||
{ TileKey::Forbidden, "forbidden" },
|
||||
{ TileKey::Wall, "wall" },
|
||||
{ TileKey::Road, "road" },
|
||||
{ TileKey::Home, "end" },
|
||||
{ TileKey::EnemyHome, "start" },
|
||||
{ TileKey::Floor, "floor" },
|
||||
{ TileKey::Hole, "hole" },
|
||||
{ TileKey::Telin, "telin" },
|
||||
{ TileKey::Telout, "telout" }
|
||||
};
|
||||
|
||||
cv::putText(draw, TileKeyMapping.at(info.key), cv::Point(info.pos.x, info.pos.y), 1, 1, cv::Scalar(0, 0, 255));
|
||||
}
|
||||
cv::putText(draw, TileKeyMapping.at(info.key), cv::Point(info.pos.x, info.pos.y), 1, 1, cv::Scalar(0, 0, 255));
|
||||
}
|
||||
#endif
|
||||
|
||||
auto cb_info = basic_info_with_what("StageInfo");
|
||||
auto& details = cb_info["details"];
|
||||
details["name"] = m_stage_name;
|
||||
details["size"] = m_side_tile_info.size();
|
||||
callback(AsstMsg::SubTaskExtraInfo, cb_info);
|
||||
}
|
||||
else {
|
||||
callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("StageInfoError"));
|
||||
}
|
||||
auto cb_info = basic_info_with_what("StageInfo");
|
||||
auto& details = cb_info["details"];
|
||||
details["name"] = m_stage_name;
|
||||
details["size"] = m_side_tile_info.size();
|
||||
callback(AsstMsg::SubTaskExtraInfo, cb_info);
|
||||
}
|
||||
else {
|
||||
callback(AsstMsg::SubTaskExtraInfo, basic_info_with_what("StageInfoError"));
|
||||
}
|
||||
|
||||
return calced;
|
||||
return calced;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::auto_battle()
|
||||
{
|
||||
LogTraceFunction;
|
||||
LogTraceFunction;
|
||||
|
||||
using Oper = asst::BattleImageAnalyzer::Oper;
|
||||
using Oper = asst::BattleImageAnalyzer::Oper;
|
||||
|
||||
BattleImageAnalyzer battle_analyzer(m_ctrler->get_image());
|
||||
BattleImageAnalyzer battle_analyzer(m_ctrler->get_image());
|
||||
|
||||
if (!battle_analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
if (!battle_analyzer.analyze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (int hp = battle_analyzer.get_hp();
|
||||
// hp != 0) {
|
||||
// bool used_skills = false;
|
||||
// if (hp < m_pre_hp) { // 说明漏怪了,漏怪就开技能(
|
||||
// for (const Rect& rect : battle_analyzer.get_ready_skills()) {
|
||||
// used_skills = true;
|
||||
// if (!use_skill(rect)) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// m_pre_hp = hp;
|
||||
// if (used_skills) {
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
//if (int hp = battle_analyzer.get_hp();
|
||||
// hp != 0) {
|
||||
// bool used_skills = false;
|
||||
// if (hp < m_pre_hp) { // 说明漏怪了,漏怪就开技能(
|
||||
// for (const Rect& rect : battle_analyzer.get_ready_skills()) {
|
||||
// used_skills = true;
|
||||
// if (!use_skill(rect)) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// m_pre_hp = hp;
|
||||
// if (used_skills) {
|
||||
// return true;
|
||||
// }
|
||||
//}
|
||||
|
||||
for (const Rect& rect : battle_analyzer.get_ready_skills()) {
|
||||
// 找出这个可以使用的技能是哪个干员的(根据之前放干员的位置)
|
||||
std::string name = "NotFound";
|
||||
for (const auto& [loc, oper_name] : m_used_tiles) {
|
||||
auto point = m_normal_tile_info[loc].pos;
|
||||
if (rect.include(point)) {
|
||||
name = oper_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (const Rect& rect : battle_analyzer.get_ready_skills()) {
|
||||
// 找出这个可以使用的技能是哪个干员的(根据之前放干员的位置)
|
||||
std::string name = "NotFound";
|
||||
for (const auto& [loc, oper_name] : m_used_tiles) {
|
||||
auto point = m_normal_tile_info[loc].pos;
|
||||
if (rect.include(point)) {
|
||||
name = oper_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto& usage = m_skill_usage[name];
|
||||
Log.info("Oper", name, ", skill usage", static_cast<int>(usage));
|
||||
switch (usage) {
|
||||
case SkillUsage::Once:
|
||||
use_skill(rect);
|
||||
usage = SkillUsage::OnceUsed;
|
||||
return true;
|
||||
break;
|
||||
case SkillUsage::Possibly:
|
||||
use_skill(rect);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto& usage = m_skill_usage[name];
|
||||
Log.info("Oper", name, ", skill usage", static_cast<int>(usage));
|
||||
switch (usage) {
|
||||
case BattleSkillUsage::Once:
|
||||
use_skill(rect);
|
||||
usage = BattleSkillUsage::OnceUsed;
|
||||
return true;
|
||||
break;
|
||||
case BattleSkillUsage::Possibly:
|
||||
use_skill(rect);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& opers = battle_analyzer.get_opers();
|
||||
if (opers.empty()) {
|
||||
return true;
|
||||
}
|
||||
const auto& opers = battle_analyzer.get_opers();
|
||||
if (opers.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static const std::array<Role, 9> RoleOrder = {
|
||||
Role::Pioneer,
|
||||
Role::Sniper,
|
||||
Role::Warrior,
|
||||
Role::Support,
|
||||
Role::Medic,
|
||||
Role::Caster,
|
||||
Role::Special,
|
||||
Role::Tank,
|
||||
Role::Drone
|
||||
};
|
||||
const auto use_oper_task_ptr = Task.get("BattleUseOper");
|
||||
const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper");
|
||||
static const std::array<Role, 9> RoleOrder = {
|
||||
Role::Pioneer,
|
||||
Role::Sniper,
|
||||
Role::Warrior,
|
||||
Role::Support,
|
||||
Role::Medic,
|
||||
Role::Caster,
|
||||
Role::Special,
|
||||
Role::Tank,
|
||||
Role::Drone
|
||||
};
|
||||
const auto use_oper_task_ptr = Task.get("BattleUseOper");
|
||||
const auto swipe_oper_task_ptr = Task.get("BattleSwipeOper");
|
||||
|
||||
// 点击当前最合适的干员
|
||||
Oper opt_oper;
|
||||
bool oper_found = false;
|
||||
for (auto role : RoleOrder) {
|
||||
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;
|
||||
}
|
||||
m_ctrler->click(opt_oper.rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
// 点击当前最合适的干员
|
||||
Oper opt_oper;
|
||||
bool oper_found = false;
|
||||
for (auto role : RoleOrder) {
|
||||
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;
|
||||
}
|
||||
m_ctrler->click(opt_oper.rect);
|
||||
sleep(use_oper_task_ptr->pre_delay);
|
||||
|
||||
OcrImageAnalyzer oper_name_analyzer(m_ctrler->get_image());
|
||||
oper_name_analyzer.set_task_info(Task.get("BattleOperName"));
|
||||
oper_name_analyzer.set_replace(
|
||||
std::dynamic_pointer_cast<OcrTaskInfo>(
|
||||
Task.get("Roguelike1RecruitData"))
|
||||
->replace_map);
|
||||
OcrImageAnalyzer oper_name_analyzer(m_ctrler->get_image());
|
||||
oper_name_analyzer.set_task_info(Task.get("BattleOperName"));
|
||||
oper_name_analyzer.set_replace(
|
||||
std::dynamic_pointer_cast<OcrTaskInfo>(
|
||||
Task.get("Roguelike1RecruitData"))
|
||||
->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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 将干员拖动到场上
|
||||
Loc loc = Loc::All;
|
||||
switch (opt_oper.role) {
|
||||
case Role::Medic:
|
||||
case Role::Support:
|
||||
case Role::Sniper:
|
||||
case Role::Caster:
|
||||
loc = Loc::Ranged;
|
||||
break;
|
||||
case Role::Pioneer:
|
||||
case Role::Warrior:
|
||||
case Role::Tank:
|
||||
loc = Loc::Melee;
|
||||
break;
|
||||
case Role::Special:
|
||||
case Role::Drone:
|
||||
default:
|
||||
// 特种和无人机,有的只能放地面,有的又只能放高台,不好判断
|
||||
// 笨办法,都试试,总有一次能成的
|
||||
//{
|
||||
// static Loc static_loc = Loc::Melee;
|
||||
// loc = static_loc;
|
||||
// if (static_loc == Loc::Melee) {
|
||||
// static_loc = Loc::Ranged;
|
||||
// }
|
||||
// else {
|
||||
// static_loc = Loc::Melee;
|
||||
// }
|
||||
//}
|
||||
loc = Loc::Melee;
|
||||
break;
|
||||
}
|
||||
// 将干员拖动到场上
|
||||
Loc loc = Loc::All;
|
||||
switch (opt_oper.role) {
|
||||
case Role::Medic:
|
||||
case Role::Support:
|
||||
case Role::Sniper:
|
||||
case Role::Caster:
|
||||
loc = Loc::Ranged;
|
||||
break;
|
||||
case Role::Pioneer:
|
||||
case Role::Warrior:
|
||||
case Role::Tank:
|
||||
loc = Loc::Melee;
|
||||
break;
|
||||
case Role::Special:
|
||||
case Role::Drone:
|
||||
default:
|
||||
// 特种和无人机,有的只能放地面,有的又只能放高台,不好判断
|
||||
// 笨办法,都试试,总有一次能成的
|
||||
//{
|
||||
// static Loc static_loc = Loc::Melee;
|
||||
// loc = static_loc;
|
||||
// if (static_loc == Loc::Melee) {
|
||||
// static_loc = Loc::Ranged;
|
||||
// }
|
||||
// else {
|
||||
// static_loc = Loc::Melee;
|
||||
// }
|
||||
//}
|
||||
loc = Loc::Melee;
|
||||
break;
|
||||
}
|
||||
|
||||
Point placed_loc = get_placed(loc);
|
||||
Point placed_point = m_side_tile_info.at(placed_loc).pos;
|
||||
Point placed_loc = get_placed(loc);
|
||||
Point placed_point = m_side_tile_info.at(placed_loc).pos;
|
||||
#ifdef ASST_DEBUG
|
||||
auto image = m_ctrler->get_image();
|
||||
cv::circle(image, cv::Point(placed_point.x, placed_point.y), 10, cv::Scalar(0, 0, 255), -1);
|
||||
auto image = m_ctrler->get_image();
|
||||
cv::circle(image, cv::Point(placed_point.x, placed_point.y), 10, cv::Scalar(0, 0, 255), -1);
|
||||
#endif
|
||||
Rect placed_rect(placed_point.x, placed_point.y, 1, 1);
|
||||
m_ctrler->swipe(opt_oper.rect, placed_rect, swipe_oper_task_ptr->pre_delay);
|
||||
sleep(use_oper_task_ptr->rear_delay);
|
||||
Rect placed_rect(placed_point.x, placed_point.y, 1, 1);
|
||||
m_ctrler->swipe(opt_oper.rect, placed_rect, swipe_oper_task_ptr->pre_delay);
|
||||
sleep(use_oper_task_ptr->rear_delay);
|
||||
|
||||
// 计算往哪边拖动(干员朝向)
|
||||
Point direction = calc_direction(placed_loc, opt_oper.role);
|
||||
// 计算往哪边拖动(干员朝向)
|
||||
Point direction = calc_direction(placed_loc, opt_oper.role);
|
||||
|
||||
// 将方向转换为实际的 swipe end 坐标点
|
||||
Point end_point = placed_point;
|
||||
constexpr int coeff = 500;
|
||||
end_point.x += direction.x * coeff;
|
||||
end_point.y += direction.y * coeff;
|
||||
// 将方向转换为实际的 swipe end 坐标点
|
||||
Point end_point = placed_point;
|
||||
constexpr int coeff = 500;
|
||||
end_point.x += direction.x * coeff;
|
||||
end_point.y += direction.y * coeff;
|
||||
|
||||
end_point.x = std::max(0, end_point.x);
|
||||
end_point.x = std::min(end_point.x, WindowWidthDefault);
|
||||
end_point.y = std::max(0, end_point.y);
|
||||
end_point.y = std::min(end_point.y, WindowHeightDefault);
|
||||
end_point.x = std::max(0, end_point.x);
|
||||
end_point.x = std::min(end_point.x, WindowWidthDefault);
|
||||
end_point.y = std::max(0, end_point.y);
|
||||
end_point.y = std::min(end_point.y, WindowHeightDefault);
|
||||
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay);
|
||||
m_ctrler->swipe(placed_point, end_point, swipe_oper_task_ptr->rear_delay);
|
||||
|
||||
m_used_tiles.emplace(placed_loc, oper_name);
|
||||
m_opers_used = true;
|
||||
++m_cur_home_index;
|
||||
m_used_tiles.emplace(placed_loc, oper_name);
|
||||
m_opers_used = true;
|
||||
++m_cur_home_index;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::speed_up()
|
||||
{
|
||||
ProcessTask task(*this, { "BattleSpeedUp" });
|
||||
return task.run();
|
||||
ProcessTask task(*this, { "BattleSpeedUp" });
|
||||
return task.run();
|
||||
}
|
||||
|
||||
bool asst::RoguelikeBattleTaskPlugin::use_skill(const asst::Rect& rect)
|
||||
{
|
||||
m_ctrler->click(rect);
|
||||
m_ctrler->click(rect);
|
||||
|
||||
ProcessTask task(*this, { "BattleUseSkillBegin" });
|
||||
task.set_retry_times(0);
|
||||
return task.run();
|
||||
ProcessTask task(*this, { "BattleUseSkillBegin" });
|
||||
task.set_retry_times(0);
|
||||
return task.run();
|
||||
}
|
||||
|
||||
void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
{
|
||||
m_opers_used = false;
|
||||
m_pre_hp = 0;
|
||||
m_homes.clear();
|
||||
m_cur_home_index = 0;
|
||||
m_stage_name.clear();
|
||||
m_side_tile_info.clear();
|
||||
m_used_tiles.clear();
|
||||
m_opers_used = false;
|
||||
m_pre_hp = 0;
|
||||
m_homes.clear();
|
||||
m_cur_home_index = 0;
|
||||
m_stage_name.clear();
|
||||
m_side_tile_info.clear();
|
||||
m_used_tiles.clear();
|
||||
|
||||
for (auto& [_, usage] : m_skill_usage) {
|
||||
if (usage == SkillUsage::OnceUsed) {
|
||||
usage = SkillUsage::Once;
|
||||
}
|
||||
}
|
||||
for (auto& [_, usage] : m_skill_usage) {
|
||||
if (usage == BattleSkillUsage::OnceUsed) {
|
||||
usage = BattleSkillUsage::Once;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//asst::Rect asst::RoguelikeBattleTaskPlugin::get_placed_by_cv()
|
||||
@@ -358,177 +358,177 @@ void asst::RoguelikeBattleTaskPlugin::clear()
|
||||
|
||||
asst::Point asst::RoguelikeBattleTaskPlugin::get_placed(Loc buildable_type)
|
||||
{
|
||||
LogTraceFunction;
|
||||
LogTraceFunction;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Point nearest;
|
||||
int min_dist = INT_MAX;
|
||||
Point nearest;
|
||||
int min_dist = INT_MAX;
|
||||
|
||||
Point home(5, 5); // 默认值,一般是地图的中间
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
home = m_homes.at(m_cur_home_index);
|
||||
}
|
||||
Point home(5, 5); // 默认值,一般是地图的中间
|
||||
if (m_cur_home_index < m_homes.size()) {
|
||||
home = m_homes.at(m_cur_home_index);
|
||||
}
|
||||
|
||||
for (const auto& [loc, tile] : m_side_tile_info) {
|
||||
if (tile.buildable == buildable_type
|
||||
|| tile.buildable == Loc::All) {
|
||||
if (m_used_tiles.find(loc) != m_used_tiles.cend()) {
|
||||
continue;
|
||||
}
|
||||
int dx = std::abs(home.x - loc.x);
|
||||
int dy = std::abs(home.y - loc.y);
|
||||
int dist = dx * dx + dy * dy;
|
||||
if (dist <= min_dist) {
|
||||
min_dist = dist;
|
||||
nearest = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.info(__FUNCTION__, nearest.to_string());
|
||||
for (const auto& [loc, tile] : m_side_tile_info) {
|
||||
if (tile.buildable == buildable_type
|
||||
|| tile.buildable == Loc::All) {
|
||||
if (m_used_tiles.find(loc) != m_used_tiles.cend()) {
|
||||
continue;
|
||||
}
|
||||
int dx = std::abs(home.x - loc.x);
|
||||
int dy = std::abs(home.y - loc.y);
|
||||
int dist = dx * dx + dy * dy;
|
||||
if (dist <= min_dist) {
|
||||
min_dist = dist;
|
||||
nearest = loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.info(__FUNCTION__, nearest.to_string());
|
||||
|
||||
return nearest;
|
||||
return nearest;
|
||||
}
|
||||
|
||||
asst::Point asst::RoguelikeBattleTaskPlugin::calc_direction(Point loc, Role role)
|
||||
{
|
||||
LogTraceFunction;
|
||||
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);
|
||||
}
|
||||
Point home_point = m_side_tile_info.at(home_loc).pos;
|
||||
Rect home_rect(home_point.x, home_point.y, 1, 1);
|
||||
// 根据家门的方向计算一下大概的朝向
|
||||
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);
|
||||
}
|
||||
Point home_point = m_side_tile_info.at(home_loc).pos;
|
||||
Rect home_rect(home_point.x, home_point.y, 1, 1);
|
||||
|
||||
int dx = 0;
|
||||
if (loc.x > home_loc.x) dx = 1;
|
||||
else if (loc.x < home_loc.x) dx = -1;
|
||||
else dx = 0;
|
||||
int dx = 0;
|
||||
if (loc.x > home_loc.x) dx = 1;
|
||||
else if (loc.x < home_loc.x) dx = -1;
|
||||
else dx = 0;
|
||||
|
||||
int dy = 0;
|
||||
if (loc.y > home_loc.y) dy = 1;
|
||||
else if (loc.y < home_loc.y) dy = -1;
|
||||
else dy = 0;
|
||||
int dy = 0;
|
||||
if (loc.y > home_loc.y) dy = 1;
|
||||
else if (loc.y < home_loc.y) dy = -1;
|
||||
else dy = 0;
|
||||
|
||||
Point base_direction(0, 0);
|
||||
switch (role) {
|
||||
case Role::Medic:
|
||||
{
|
||||
if (std::abs(dx) < std::abs(dy)) {
|
||||
base_direction.y = -dy;
|
||||
}
|
||||
else {
|
||||
base_direction.x = -dx;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Role::Support:
|
||||
case Role::Pioneer:
|
||||
case Role::Warrior:
|
||||
case Role::Sniper:
|
||||
case Role::Special:
|
||||
case Role::Tank:
|
||||
case Role::Caster:
|
||||
case Role::Drone:
|
||||
default:
|
||||
{
|
||||
if (std::abs(dx) < std::abs(dy)) {
|
||||
base_direction.y = dy;
|
||||
}
|
||||
else {
|
||||
base_direction.x = dx;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Point base_direction(0, 0);
|
||||
switch (role) {
|
||||
case Role::Medic:
|
||||
{
|
||||
if (std::abs(dx) < std::abs(dy)) {
|
||||
base_direction.y = -dy;
|
||||
}
|
||||
else {
|
||||
base_direction.x = -dx;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Role::Support:
|
||||
case Role::Pioneer:
|
||||
case Role::Warrior:
|
||||
case Role::Sniper:
|
||||
case Role::Special:
|
||||
case Role::Tank:
|
||||
case Role::Caster:
|
||||
case Role::Drone:
|
||||
default:
|
||||
{
|
||||
if (std::abs(dx) < std::abs(dy)) {
|
||||
base_direction.y = dy;
|
||||
}
|
||||
else {
|
||||
base_direction.x = dx;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
using TileKey = TilePack::TileKey;
|
||||
using TileKey = TilePack::TileKey;
|
||||
|
||||
// 战斗干员朝向的权重
|
||||
static const std::unordered_map<TileKey, int> TileKeyFightWeights = {
|
||||
{ TileKey::Invalid, 0 },
|
||||
{ TileKey::Forbidden, 0 },
|
||||
{ TileKey::Wall, 500 },
|
||||
{ TileKey::Road, 1000 },
|
||||
{ TileKey::Home, 1000 },
|
||||
{ TileKey::EnemyHome, 1000 },
|
||||
{ TileKey::Floor, 1000 },
|
||||
{ TileKey::Hole, 0 },
|
||||
{ TileKey::Telin, 0 },
|
||||
{ TileKey::Telout, 0 }
|
||||
};
|
||||
// 战斗干员朝向的权重
|
||||
static const std::unordered_map<TileKey, int> TileKeyFightWeights = {
|
||||
{ TileKey::Invalid, 0 },
|
||||
{ TileKey::Forbidden, 0 },
|
||||
{ TileKey::Wall, 500 },
|
||||
{ TileKey::Road, 1000 },
|
||||
{ TileKey::Home, 1000 },
|
||||
{ TileKey::EnemyHome, 1000 },
|
||||
{ TileKey::Floor, 1000 },
|
||||
{ TileKey::Hole, 0 },
|
||||
{ TileKey::Telin, 0 },
|
||||
{ TileKey::Telout, 0 }
|
||||
};
|
||||
|
||||
static const std::unordered_map<Point, Point> DirectionStartingPoint = {
|
||||
{ Point(1, 0), Point(0, -1) }, // 朝右
|
||||
{ Point(0, 1), Point(-1, 0) }, // 朝下
|
||||
{ Point(-1, 0), Point(-2, -1) }, // 朝左
|
||||
{ Point(0, -1), Point(-1, -2) }, // 朝上
|
||||
};
|
||||
static const std::unordered_map<Point, Point> DirectionStartingPoint = {
|
||||
{ Point(1, 0), Point(0, -1) }, // 朝右
|
||||
{ Point(0, 1), Point(-1, 0) }, // 朝下
|
||||
{ Point(-1, 0), Point(-2, -1) }, // 朝左
|
||||
{ Point(0, -1), Point(-1, -2) }, // 朝上
|
||||
};
|
||||
|
||||
int max_score = 0;
|
||||
Point opt_direction;
|
||||
int max_score = 0;
|
||||
Point opt_direction;
|
||||
|
||||
// 计算每个方向上的得分
|
||||
for (const auto& [direction, point_move] : DirectionStartingPoint) {
|
||||
Point start_point = loc;
|
||||
start_point.x += point_move.x;
|
||||
start_point.y += point_move.y;
|
||||
int score = 0;
|
||||
// 计算每个方向上的得分
|
||||
for (const auto& [direction, point_move] : DirectionStartingPoint) {
|
||||
Point start_point = loc;
|
||||
start_point.x += point_move.x;
|
||||
start_point.y += point_move.y;
|
||||
int score = 0;
|
||||
|
||||
constexpr int AttackRangeSize = 3;
|
||||
// 这个方向上 3x3 的格子,计算总的得分
|
||||
for (int i = 0; i != AttackRangeSize; ++i) {
|
||||
for (int j = 0; j != AttackRangeSize; ++j) {
|
||||
Point cur_point = start_point;
|
||||
cur_point.x += i;
|
||||
cur_point.y += j;
|
||||
constexpr int AttackRangeSize = 3;
|
||||
// 这个方向上 3x3 的格子,计算总的得分
|
||||
for (int i = 0; i != AttackRangeSize; ++i) {
|
||||
for (int j = 0; j != AttackRangeSize; ++j) {
|
||||
Point cur_point = start_point;
|
||||
cur_point.x += i;
|
||||
cur_point.y += j;
|
||||
|
||||
switch (role) {
|
||||
// 医疗干员根据哪个方向上人多决定朝向哪
|
||||
case Role::Medic:
|
||||
if (m_used_tiles.find(cur_point) != m_used_tiles.cend()) {
|
||||
score += 1000;
|
||||
}
|
||||
break;
|
||||
// 其他干员(战斗干员)根据哪个方向上权重高决定朝向哪
|
||||
default:
|
||||
if (auto iter = m_side_tile_info.find(cur_point);
|
||||
iter == m_side_tile_info.cend()) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
score += TileKeyFightWeights.at(iter->second.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (role) {
|
||||
// 医疗干员根据哪个方向上人多决定朝向哪
|
||||
case Role::Medic:
|
||||
if (m_used_tiles.find(cur_point) != m_used_tiles.cend()) {
|
||||
score += 1000;
|
||||
}
|
||||
break;
|
||||
// 其他干员(战斗干员)根据哪个方向上权重高决定朝向哪
|
||||
default:
|
||||
if (auto iter = m_side_tile_info.find(cur_point);
|
||||
iter == m_side_tile_info.cend()) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
score += TileKeyFightWeights.at(iter->second.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (direction == base_direction) {
|
||||
score += 50;
|
||||
}
|
||||
if (direction == base_direction) {
|
||||
score += 50;
|
||||
}
|
||||
|
||||
if (score > max_score) {
|
||||
max_score = score;
|
||||
opt_direction = direction;
|
||||
}
|
||||
}
|
||||
if (score > max_score) {
|
||||
max_score = score;
|
||||
opt_direction = direction;
|
||||
}
|
||||
}
|
||||
|
||||
return opt_direction;
|
||||
}
|
||||
return opt_direction;
|
||||
}
|
||||
@@ -4,22 +4,16 @@
|
||||
#include "AsstTypes.h"
|
||||
#include "TilePack.h"
|
||||
#include "BattleImageAnalyzer.h"
|
||||
#include "AsstBattleDef.h"
|
||||
|
||||
namespace asst
|
||||
{
|
||||
enum class SkillUsage
|
||||
{
|
||||
Possibly = 0, // 有就用,例如干员 棘刺 3 技能
|
||||
Once = 1, // 只用一次,例如干员 银灰 2技能
|
||||
InTime = 2, // 关键时刻使用,例如干员 假日威龙陈 3 技能
|
||||
OnceUsed
|
||||
};
|
||||
class RoguelikeBattleTaskPlugin : public AbstractTaskPlugin
|
||||
{
|
||||
using Loc = asst::TilePack::BuildableType;
|
||||
using Role = asst::BattleImageAnalyzer::Role;
|
||||
public:
|
||||
using SkillUsageMap = std::unordered_map<std::string, SkillUsage>;
|
||||
using SkillUsageMap = std::unordered_map<std::string, BattleSkillUsage>;
|
||||
public:
|
||||
using AbstractTaskPlugin::AbstractTaskPlugin;
|
||||
virtual ~RoguelikeBattleTaskPlugin() = default;
|
||||
|
||||
@@ -10,78 +10,78 @@
|
||||
#include "Logger.hpp"
|
||||
|
||||
asst::RoguelikeTask::RoguelikeTask(AsstCallback callback, void* callback_arg)
|
||||
: PackageTask(callback, callback_arg, TaskType),
|
||||
m_roguelike_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType))
|
||||
: PackageTask(callback, callback_arg, TaskType),
|
||||
m_roguelike_task_ptr(std::make_shared<ProcessTask>(callback, callback_arg, TaskType))
|
||||
{
|
||||
m_roguelike_task_ptr->set_tasks({ "Roguelike1Begin" })
|
||||
.set_retry_times(50);
|
||||
m_roguelike_task_ptr->set_tasks({ "Roguelike1Begin" })
|
||||
.set_retry_times(50);
|
||||
|
||||
m_roguelike_task_ptr->regiseter_plugin<RoguelikeFormationTaskPlugin>();
|
||||
m_roguelike_task_ptr->regiseter_plugin<RoguelikeFormationTaskPlugin>();
|
||||
|
||||
m_battle_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeBattleTaskPlugin>();
|
||||
m_battle_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeBattleTaskPlugin>();
|
||||
|
||||
m_recruit_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeRecruitTaskPlugin>();
|
||||
m_recruit_task_ptr->set_retry_times(2);
|
||||
m_recruit_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeRecruitTaskPlugin>();
|
||||
m_recruit_task_ptr->set_retry_times(2);
|
||||
|
||||
m_skill_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeSkillSelectionTaskPlugin>();
|
||||
m_skill_task_ptr->set_retry_times(0);
|
||||
m_skill_task_ptr = m_roguelike_task_ptr->regiseter_plugin<RoguelikeSkillSelectionTaskPlugin>();
|
||||
m_skill_task_ptr->set_retry_times(0);
|
||||
|
||||
// 这个任务如果卡住会放弃当前的肉鸽并重新开始,所以多添加一点。先这样凑合用
|
||||
for (int i = 0; i != 10000; ++i) {
|
||||
m_subtasks.emplace_back(m_roguelike_task_ptr);
|
||||
}
|
||||
// 这个任务如果卡住会放弃当前的肉鸽并重新开始,所以多添加一点。先这样凑合用
|
||||
for (int i = 0; i != 10000; ++i) {
|
||||
m_subtasks.emplace_back(m_roguelike_task_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool asst::RoguelikeTask::set_params(const json::value& params)
|
||||
{
|
||||
// 0 - 尽可能一直往后打
|
||||
// 1 - 第一层投资完源石锭就退出
|
||||
// 2 - 投资过后再退出,没有投资就继续往后打
|
||||
// 0 - 尽可能一直往后打
|
||||
// 1 - 第一层投资完源石锭就退出
|
||||
// 2 - 投资过后再退出,没有投资就继续往后打
|
||||
|
||||
if (!params.contains("opers") || !params.at("opers").is_array()) {
|
||||
return false;
|
||||
}
|
||||
if (!params.contains("opers") || !params.at("opers").is_array()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> opers_vec;
|
||||
RoguelikeSkillSelectionTaskPlugin::SkillMap skill_map;
|
||||
RoguelikeBattleTaskPlugin::SkillUsageMap usage_map;
|
||||
std::vector<std::string> opers_vec;
|
||||
RoguelikeSkillSelectionTaskPlugin::SkillMap skill_map;
|
||||
RoguelikeBattleTaskPlugin::SkillUsageMap usage_map;
|
||||
|
||||
for (auto& oper : params.at("opers").as_array()) {
|
||||
if (!oper.contains("name") || !oper.at("name").is_string()) {
|
||||
return false;
|
||||
}
|
||||
int skill = oper.get("skill", 1);
|
||||
if (skill < 1 || 3 < skill) {
|
||||
return false;
|
||||
}
|
||||
std::string name = oper.at("name").as_string();
|
||||
opers_vec.emplace_back(name);
|
||||
skill_map.emplace(name, skill);
|
||||
for (auto& oper : params.at("opers").as_array()) {
|
||||
if (!oper.contains("name") || !oper.at("name").is_string()) {
|
||||
return false;
|
||||
}
|
||||
int skill = oper.get("skill", 1);
|
||||
if (skill < 1 || 3 < skill) {
|
||||
return false;
|
||||
}
|
||||
std::string name = oper.at("name").as_string();
|
||||
opers_vec.emplace_back(name);
|
||||
skill_map.emplace(name, skill);
|
||||
|
||||
auto usage = static_cast<SkillUsage>(oper.get("skill_usage", 0));
|
||||
usage_map.emplace(name, usage);
|
||||
}
|
||||
skill_map.emplace("Unknown", 3);
|
||||
auto usage = static_cast<BattleSkillUsage>(oper.get("skill_usage", 0));
|
||||
usage_map.emplace(name, usage);
|
||||
}
|
||||
skill_map.emplace("Unknown", 3);
|
||||
|
||||
int mode = params.get("mode", 0);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
m_roguelike_task_ptr->set_times_limit("Roguelike1StageTraderLeave", 0);
|
||||
break;
|
||||
case 2:
|
||||
m_roguelike_task_ptr->set_times_limit("Roguelike1StageTraderInvestCancel", 0);
|
||||
break;
|
||||
default:
|
||||
Log.error(__FUNCTION__, "| Unknown mode", mode);
|
||||
return false;
|
||||
}
|
||||
int mode = params.get("mode", 0);
|
||||
switch (mode) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
m_roguelike_task_ptr->set_times_limit("Roguelike1StageTraderLeave", 0);
|
||||
break;
|
||||
case 2:
|
||||
m_roguelike_task_ptr->set_times_limit("Roguelike1StageTraderInvestCancel", 0);
|
||||
break;
|
||||
default:
|
||||
Log.error(__FUNCTION__, "| Unknown mode", mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_recruit_task_ptr->set_opers(std::move(opers_vec));
|
||||
m_skill_task_ptr->set_skill_map(std::move(skill_map));
|
||||
m_recruit_task_ptr->set_opers(std::move(opers_vec));
|
||||
m_skill_task_ptr->set_skill_map(std::move(skill_map));
|
||||
|
||||
m_battle_task_ptr->set_skill_usage(std::move(usage_map));
|
||||
m_battle_task_ptr->set_skill_usage(std::move(usage_map));
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user