mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-15 17:30:27 +08:00
feat: 作业命令块增强,支持命令触发条件的策略变更,支持循环命令,条件命令,派发命令以及无序命令组
This commit is contained in:
committed by
ValenciaFly
parent
3d73780c01
commit
a930afe949
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "AsstTypes.h"
|
||||
@@ -9,330 +12,500 @@
|
||||
|
||||
namespace asst::battle
|
||||
{
|
||||
// 统一变量名:
|
||||
// loc, location, 表示格子坐标,例如 [1, 1], [5, 5]
|
||||
// pos, position, 表示像素坐标,例如 [1280, 720], [500, 300]
|
||||
// 统一变量名:
|
||||
// loc, location, 表示格子坐标,例如 [1, 1], [5, 5]
|
||||
// pos, position, 表示像素坐标,例如 [1280, 720], [500, 300]
|
||||
|
||||
enum class SkillUsage // 技能用法
|
||||
enum class SkillUsage // 技能用法
|
||||
{
|
||||
NotUse = 0, // 不自动使用
|
||||
Possibly = 1, // 有就用,例如干员 棘刺 3 技能
|
||||
Times = 2, // 用 X 次,例如干员 山 2 技能用 1 次、重岳 3 技能用 5 次,由 "skill_times" 字段控制
|
||||
InTime = 3, // 自动判断使用时机,画饼.jpg
|
||||
TimesUsed // 已经使用了 X 次
|
||||
};
|
||||
|
||||
struct OperUsage // 干员用法
|
||||
{
|
||||
std::string name;
|
||||
int skill = 0; // 技能序号,取值范围 [0, 3],0时使用默认技能 或 上次编队时使用的技能
|
||||
SkillUsage skill_usage = SkillUsage::NotUse;
|
||||
int skill_times = 1; // 使用技能的次数,默认为 1,兼容曾经的作业
|
||||
};
|
||||
|
||||
enum class DeployDirection
|
||||
{
|
||||
Right = 0,
|
||||
Down = 1,
|
||||
Left = 2,
|
||||
Up = 3,
|
||||
None = 4 // 没有方向,通常是无人机之类的
|
||||
};
|
||||
|
||||
enum class Role
|
||||
{
|
||||
Unknown,
|
||||
Caster,
|
||||
Medic,
|
||||
Pioneer,
|
||||
Sniper,
|
||||
Special,
|
||||
Support,
|
||||
Tank,
|
||||
Warrior,
|
||||
Drone
|
||||
};
|
||||
|
||||
inline static Role get_role_type(const std::string& role_name)
|
||||
{
|
||||
static const std::unordered_map<std::string, Role> NameToRole = {
|
||||
{ "warrior", Role::Warrior }, { "WARRIOR", Role::Warrior }, { "Warrior", Role::Warrior },
|
||||
{ "近卫", Role::Warrior }, { "GUARD", Role::Warrior }, { "guard", Role::Warrior },
|
||||
{ "Guard", Role::Warrior },
|
||||
|
||||
{ "pioneer", Role::Pioneer }, { "PIONEER", Role::Pioneer }, { "Pioneer", Role::Pioneer },
|
||||
{ "先锋", Role::Pioneer }, { "VANGUARD", Role::Pioneer }, { "vanguard", Role::Pioneer },
|
||||
{ "Vanguard", Role::Pioneer },
|
||||
|
||||
{ "medic", Role::Medic }, { "MEDIC", Role::Medic }, { "Medic", Role::Medic },
|
||||
{ "医疗", Role::Medic },
|
||||
|
||||
{ "tank", Role::Tank }, { "TANK", Role::Tank }, { "Tank", Role::Tank },
|
||||
{ "重装", Role::Tank }, { "DEFENDER", Role::Tank }, { "defender", Role::Tank },
|
||||
{ "Defender", Role::Tank }, { "坦克", Role::Tank },
|
||||
|
||||
{ "sniper", Role::Sniper }, { "SNIPER", Role::Sniper }, { "Sniper", Role::Sniper },
|
||||
{ "狙击", Role::Sniper },
|
||||
|
||||
{ "caster", Role::Caster }, { "CASTER", Role::Caster },
|
||||
|
||||
{ "Caster", Role::Caster }, { "术师", Role::Caster }, { "术士", Role::Caster },
|
||||
{ "法师", Role::Caster },
|
||||
|
||||
{ "support", Role::Support }, { "SUPPORT", Role::Support }, { "Support", Role::Support },
|
||||
{ "supporter", Role::Support }, { "SUPPORTER", Role::Support }, { "Supporter", Role::Support },
|
||||
{ "辅助", Role::Support }, { "支援", Role::Support },
|
||||
|
||||
{ "special", Role::Special }, { "SPECIAL", Role::Special }, { "Special", Role::Special },
|
||||
{ "特种", Role::Special }, { "SPECIALIST", Role::Special }, { "specialist", Role::Special },
|
||||
{ "Specialist", Role::Special },
|
||||
|
||||
{ "drone", Role::Drone }, { "DRONE", Role::Drone }, { "Drone", Role::Drone },
|
||||
{ "无人机", Role::Drone }, { "SUMMON", Role::Drone }, { "summon", Role::Drone },
|
||||
{ "Summon", Role::Drone }, { "召唤物", Role::Drone },
|
||||
};
|
||||
if (auto iter = NameToRole.find(role_name); iter != NameToRole.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return Role::Unknown;
|
||||
}
|
||||
|
||||
enum class OperPosition
|
||||
{
|
||||
None,
|
||||
Blocking, // 阻挡单位
|
||||
AirDefense, // 对空单位
|
||||
};
|
||||
|
||||
enum class LocationType
|
||||
{
|
||||
Invalid = -1,
|
||||
None = 0,
|
||||
Melee = 1,
|
||||
Ranged = 2,
|
||||
All = 3
|
||||
};
|
||||
|
||||
inline static LocationType get_role_usual_location(const Role& role)
|
||||
{
|
||||
switch (role) {
|
||||
case Role::Warrior:
|
||||
case Role::Pioneer:
|
||||
case Role::Tank:
|
||||
case Role::Special:
|
||||
case Role::Drone:
|
||||
return LocationType::Melee;
|
||||
case Role::Medic:
|
||||
case Role::Sniper:
|
||||
case Role::Caster:
|
||||
case Role::Support:
|
||||
return LocationType::Ranged;
|
||||
default:
|
||||
return LocationType::None;
|
||||
}
|
||||
}
|
||||
|
||||
struct DeploymentOper
|
||||
{
|
||||
size_t index = 0;
|
||||
Role role = Role::Unknown;
|
||||
int cost = 0;
|
||||
bool available = false;
|
||||
bool cooling = false;
|
||||
Rect rect;
|
||||
cv::Mat avatar;
|
||||
std::string name;
|
||||
LocationType location_type = LocationType::None;
|
||||
bool is_unusual_location = false; // 地面辅助,高台先锋等
|
||||
};
|
||||
|
||||
struct OperProps
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string name_en;
|
||||
std::string name_jp;
|
||||
std::string name_kr;
|
||||
std::string name_tw;
|
||||
Role role = Role::Unknown;
|
||||
std::array<std::string, 3> ranges;
|
||||
int rarity = 0;
|
||||
LocationType location_type = LocationType::None;
|
||||
std::vector<std::string> tokens; // 召唤物名字
|
||||
};
|
||||
|
||||
using AttackRange = std::vector<Point>;
|
||||
using RoleCounts = std::unordered_map<Role, int>;
|
||||
|
||||
namespace copilot
|
||||
{
|
||||
using OperUsageGroups = std::unordered_map<std::string, std::vector<OperUsage>>;
|
||||
|
||||
enum class ActionType
|
||||
{
|
||||
Deploy, // 部署干员
|
||||
UseSkill, // 开技能
|
||||
Retreat, // 撤退干员
|
||||
SkillUsage, // 技能用法
|
||||
SwitchSpeed, // 切换二倍速
|
||||
BulletTime, // 使用 1/5 的速度
|
||||
Output, // 仅输出,什么都不操作,界面上也不显示
|
||||
SkillDaemon, // 什么都不做,有技能开技能,直到战斗结束
|
||||
|
||||
/* for TRN */
|
||||
MoveCamera, // 引航者试炼,移动镜头
|
||||
|
||||
/* for SSS */
|
||||
DrawCard, // “调配干员”
|
||||
CheckIfStartOver, // 检查如果没有某干员则退出重开
|
||||
|
||||
/* for Enhance */
|
||||
Loop, // 创建一个循环流程
|
||||
Case, // 为干员组进行分化操作
|
||||
Check, // 根据触发条件执行不同的分支
|
||||
Until, // 内部动作可无序,知道里边的动作全部执行完毕才能结束
|
||||
SavePoint, // 保存一个锚点
|
||||
SyncPoint, // 同步锚点
|
||||
CheckPoint, // 检查锚点
|
||||
};
|
||||
|
||||
struct Action;
|
||||
using ActionPtr = std::shared_ptr<Action>;
|
||||
|
||||
struct TriggerInfo
|
||||
{
|
||||
static constexpr int DEACTIVE_KILLS = -1; // 击杀数未被设置
|
||||
static constexpr int DEACTIVE_COST = -1; // 费用数未被设置
|
||||
static constexpr int DEACTIVE_COST_CHANGES = 0; // 费用变更未设置
|
||||
static constexpr int DEACTIVE_COOLING = -1; // 干员冷却数未设置
|
||||
static constexpr int DEACTIVE_COUNT = -1; // 循环计数
|
||||
|
||||
enum class Category
|
||||
{
|
||||
NotUse = 0, // 不自动使用
|
||||
Possibly = 1, // 有就用,例如干员 棘刺 3 技能
|
||||
Times = 2, // 用 X 次,例如干员 山 2 技能用 1 次、重岳 3 技能用 5 次,由 "skill_times" 字段控制
|
||||
InTime = 3, // 自动判断使用时机,画饼.jpg
|
||||
TimesUsed // 已经使用了 X 次
|
||||
None, // 未设置, 未指定策略
|
||||
Succ, // 默认满足, 跳过条件判断
|
||||
All, // 所有被设置的条件都应该满足
|
||||
Any, // 所有被设置的条件只要有一个满足
|
||||
Not // 所有被设置的条件一个都不满足
|
||||
};
|
||||
|
||||
struct OperUsage // 干员用法
|
||||
Category category = Category::None;
|
||||
int kills = DEACTIVE_KILLS; // 击杀数条件
|
||||
int costs = DEACTIVE_COST; // 费用条件
|
||||
int cost_changes = DEACTIVE_COST_CHANGES; // 费用变化条件
|
||||
int cooling = DEACTIVE_COOLING; // 冷却中的干员条件
|
||||
int count = DEACTIVE_COUNT; // 计数条件,不做变化,记录初始值
|
||||
mutable int counter = 0; // 计数器
|
||||
|
||||
// 触发器是否被激活
|
||||
bool active() const noexcept { return category != Category::None; }
|
||||
|
||||
// 重置计数器
|
||||
void resetCounter() const noexcept { counter = 0; }
|
||||
|
||||
// 激活一次计数器
|
||||
void activeCounter() const noexcept { ++counter; }
|
||||
|
||||
static auto loadCategoryFrom(std::string const& _Str) -> Category
|
||||
{
|
||||
std::string name;
|
||||
int skill = 0; // 技能序号,取值范围 [0, 3],0时使用默认技能 或 上次编队时使用的技能
|
||||
SkillUsage skill_usage = SkillUsage::NotUse;
|
||||
int skill_times = 1; // 使用技能的次数,默认为 1,兼容曾经的作业
|
||||
};
|
||||
|
||||
enum class DeployDirection
|
||||
{
|
||||
Right = 0,
|
||||
Down = 1,
|
||||
Left = 2,
|
||||
Up = 3,
|
||||
None = 4 // 没有方向,通常是无人机之类的
|
||||
};
|
||||
|
||||
enum class Role
|
||||
{
|
||||
Unknown,
|
||||
Caster,
|
||||
Medic,
|
||||
Pioneer,
|
||||
Sniper,
|
||||
Special,
|
||||
Support,
|
||||
Tank,
|
||||
Warrior,
|
||||
Drone
|
||||
};
|
||||
|
||||
inline static Role get_role_type(const std::string& role_name)
|
||||
{
|
||||
static const std::unordered_map<std::string, Role> NameToRole = {
|
||||
{ "warrior", Role::Warrior }, { "WARRIOR", Role::Warrior }, { "Warrior", Role::Warrior },
|
||||
{ "近卫", Role::Warrior }, { "GUARD", Role::Warrior }, { "guard", Role::Warrior },
|
||||
{ "Guard", Role::Warrior },
|
||||
|
||||
{ "pioneer", Role::Pioneer }, { "PIONEER", Role::Pioneer }, { "Pioneer", Role::Pioneer },
|
||||
{ "先锋", Role::Pioneer }, { "VANGUARD", Role::Pioneer }, { "vanguard", Role::Pioneer },
|
||||
{ "Vanguard", Role::Pioneer },
|
||||
|
||||
{ "medic", Role::Medic }, { "MEDIC", Role::Medic }, { "Medic", Role::Medic },
|
||||
{ "医疗", Role::Medic },
|
||||
|
||||
{ "tank", Role::Tank }, { "TANK", Role::Tank }, { "Tank", Role::Tank },
|
||||
{ "重装", Role::Tank }, { "DEFENDER", Role::Tank }, { "defender", Role::Tank },
|
||||
{ "Defender", Role::Tank }, { "坦克", Role::Tank },
|
||||
|
||||
{ "sniper", Role::Sniper }, { "SNIPER", Role::Sniper }, { "Sniper", Role::Sniper },
|
||||
{ "狙击", Role::Sniper },
|
||||
|
||||
{ "caster", Role::Caster }, { "CASTER", Role::Caster },
|
||||
|
||||
{ "Caster", Role::Caster }, { "术师", Role::Caster }, { "术士", Role::Caster },
|
||||
{ "法师", Role::Caster },
|
||||
|
||||
{ "support", Role::Support }, { "SUPPORT", Role::Support }, { "Support", Role::Support },
|
||||
{ "supporter", Role::Support }, { "SUPPORTER", Role::Support }, { "Supporter", Role::Support },
|
||||
{ "辅助", Role::Support }, { "支援", Role::Support },
|
||||
|
||||
{ "special", Role::Special }, { "SPECIAL", Role::Special }, { "Special", Role::Special },
|
||||
{ "特种", Role::Special }, { "SPECIALIST", Role::Special }, { "specialist", Role::Special },
|
||||
{ "Specialist", Role::Special },
|
||||
|
||||
{ "drone", Role::Drone }, { "DRONE", Role::Drone }, { "Drone", Role::Drone },
|
||||
{ "无人机", Role::Drone }, { "SUMMON", Role::Drone }, { "summon", Role::Drone },
|
||||
{ "Summon", Role::Drone }, { "召唤物", Role::Drone },
|
||||
};
|
||||
if (auto iter = NameToRole.find(role_name); iter != NameToRole.end()) {
|
||||
return iter->second;
|
||||
if (_Str == "Succ") {
|
||||
return Category::Succ;
|
||||
}
|
||||
return Role::Unknown;
|
||||
}
|
||||
|
||||
enum class OperPosition
|
||||
{
|
||||
None,
|
||||
Blocking, // 阻挡单位
|
||||
AirDefense, // 对空单位
|
||||
};
|
||||
|
||||
enum class LocationType
|
||||
{
|
||||
Invalid = -1,
|
||||
None = 0,
|
||||
Melee = 1,
|
||||
Ranged = 2,
|
||||
All = 3
|
||||
};
|
||||
|
||||
inline static LocationType get_role_usual_location(const Role& role)
|
||||
{
|
||||
switch (role) {
|
||||
case Role::Warrior:
|
||||
case Role::Pioneer:
|
||||
case Role::Tank:
|
||||
case Role::Special:
|
||||
case Role::Drone:
|
||||
return LocationType::Melee;
|
||||
case Role::Medic:
|
||||
case Role::Sniper:
|
||||
case Role::Caster:
|
||||
case Role::Support:
|
||||
return LocationType::Ranged;
|
||||
default:
|
||||
return LocationType::None;
|
||||
if (_Str == "All") {
|
||||
return Category::All;
|
||||
}
|
||||
|
||||
if (_Str == "Any") {
|
||||
return Category::Any;
|
||||
}
|
||||
|
||||
if (_Str == "Not") {
|
||||
return Category::Not;
|
||||
}
|
||||
|
||||
return Category::None;
|
||||
}
|
||||
};
|
||||
|
||||
// 用于定义延时信息,每个命令都有
|
||||
struct DelayInfo
|
||||
{
|
||||
int pre_delay = 0; // 执行动作前的延时
|
||||
int post_delay = 0; // 执行动作之后的延时
|
||||
int time_out = INT_MAX; // TODO
|
||||
};
|
||||
|
||||
// 定义干员操作信息
|
||||
struct AvatarInfo
|
||||
{
|
||||
std::string name; // 目标名,若 type >= SwitchSpeed, name 为空
|
||||
Point location; // 目标定位
|
||||
DeployDirection direction = DeployDirection::Right; // 目标朝向
|
||||
SkillUsage modify_usage = SkillUsage::NotUse; // 目标的技能使用策略
|
||||
int modify_times = 1; // 更改使用技能的次数,默认为 1,兼容曾经的作业
|
||||
};
|
||||
|
||||
// 定义文本说明信息
|
||||
struct TextInfo
|
||||
{
|
||||
std::string doc; // 文本
|
||||
std::string doc_color; // 文本颜色
|
||||
};
|
||||
|
||||
// 定义case操作需要的信息
|
||||
struct CaseInfo
|
||||
{
|
||||
std::string group_select; // 选定的干员组
|
||||
std::map<std::string, std::vector<ActionPtr>> dispatch_actions; // 对应干员的动作
|
||||
std::vector<ActionPtr> default_action; // 默认的动作
|
||||
};
|
||||
|
||||
// 定义loop操作需要的信息
|
||||
struct LoopInfo
|
||||
{
|
||||
TriggerInfo end_info;
|
||||
TriggerInfo continue_info;
|
||||
TriggerInfo break_info;
|
||||
|
||||
int counter = 0; // 计数器
|
||||
|
||||
std::vector<ActionPtr> loop_actions;
|
||||
};
|
||||
|
||||
// 定义check操作需要的信息
|
||||
struct CheckInfo
|
||||
{
|
||||
TriggerInfo condition_info;
|
||||
std::vector<ActionPtr> then_actions; // 当条件满足时执行
|
||||
std::vector<ActionPtr> else_actions; // 当条件不满足时执行
|
||||
};
|
||||
|
||||
// 定义until操作需要的信息
|
||||
struct UntilInfo
|
||||
{
|
||||
TriggerInfo::Category category; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束
|
||||
std::vector<ActionPtr> candidate; // 备用的命令序列
|
||||
};
|
||||
|
||||
struct CheckIfStartOverInfo
|
||||
{
|
||||
std::string name;
|
||||
RoleCounts role_counts; // 角色统计
|
||||
|
||||
CheckIfStartOverInfo() = default;
|
||||
|
||||
explicit CheckIfStartOverInfo(RoleCounts&& _Counts) :
|
||||
role_counts(std::move(_Counts))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct MoveCameraInfo
|
||||
{
|
||||
std::pair<double, double> distance;
|
||||
|
||||
explicit MoveCameraInfo(decltype(distance)&& _Dist) :
|
||||
distance(std::move(_Dist))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct Action
|
||||
{
|
||||
ActionType type = ActionType::Deploy;
|
||||
std::string point_code; // 锚点编码
|
||||
TriggerInfo trigger; // 必须拥有,表示动作触发时的条件信息
|
||||
DelayInfo delay; // 延时信息
|
||||
TextInfo text; // 表示文本输出,用于命令提示亦或是调试输出
|
||||
|
||||
// 为了便于内存管理,使用variant管理额外的action信息,通过type来进行还原
|
||||
// AvatarInfo 表示干员或辅助装置的部署、撤退、技能策略更改等
|
||||
// CaseInfo 表示对干员组内选择对不同干员进行分化动作
|
||||
// LoopInfo 表示循环命令中对动作信息的相关设置
|
||||
// CheckInfo 表示根据条件的成立于否来确认执行的命令序列
|
||||
// UntilInfo 表示根据条件的成立于否来无序执行命令序列
|
||||
std::variant<
|
||||
std::monostate,
|
||||
CheckIfStartOverInfo,
|
||||
MoveCameraInfo,
|
||||
AvatarInfo,
|
||||
CaseInfo,
|
||||
LoopInfo,
|
||||
CheckInfo,
|
||||
UntilInfo>
|
||||
payload; // 信息载荷
|
||||
|
||||
// 是否携带干员信息
|
||||
bool hasAvatarInfo() const noexcept { return std::holds_alternative<AvatarInfo>(payload); }
|
||||
|
||||
// 读取载荷
|
||||
template <typename PayLoad_T>
|
||||
auto getPayload() const noexcept -> PayLoad_T const&
|
||||
{
|
||||
return std::get<PayLoad_T>(payload);
|
||||
}
|
||||
|
||||
struct DeploymentOper
|
||||
{
|
||||
size_t index = 0;
|
||||
Role role = Role::Unknown;
|
||||
int cost = 0;
|
||||
bool available = false;
|
||||
bool cooling = false;
|
||||
Rect rect;
|
||||
cv::Mat avatar;
|
||||
std::string name;
|
||||
LocationType location_type = LocationType::None;
|
||||
bool is_unusual_location = false; // 地面辅助,高台先锋等
|
||||
};
|
||||
struct OperProps
|
||||
{
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string name_en;
|
||||
std::string name_jp;
|
||||
std::string name_kr;
|
||||
std::string name_tw;
|
||||
Role role = Role::Unknown;
|
||||
std::array<std::string, 3> ranges;
|
||||
int rarity = 0;
|
||||
LocationType location_type = LocationType::None;
|
||||
std::vector<std::string> tokens; // 召唤物名字
|
||||
};
|
||||
// 创建堆对象,由智能指针管理内存
|
||||
static auto create() -> ActionPtr { return std::make_shared<Action>(); }
|
||||
};
|
||||
|
||||
using AttackRange = std::vector<Point>;
|
||||
using RoleCounts = std::unordered_map<Role, int>;
|
||||
struct BasicInfo
|
||||
{
|
||||
std::string stage_name;
|
||||
std::string minimum_required;
|
||||
std::string title;
|
||||
std::string title_color;
|
||||
std::string details;
|
||||
std::string details_color;
|
||||
};
|
||||
|
||||
namespace copilot
|
||||
{
|
||||
using OperUsageGroups = std::unordered_map<std::string, std::vector<OperUsage>>;
|
||||
struct CombatData // 作业 JSON 数据
|
||||
{
|
||||
BasicInfo info;
|
||||
OperUsageGroups groups;
|
||||
std::vector<Action> actions;
|
||||
};
|
||||
} // namespace copilot
|
||||
|
||||
enum class ActionType
|
||||
{
|
||||
Deploy, // 部署干员
|
||||
UseSkill, // 开技能
|
||||
Retreat, // 撤退干员
|
||||
SkillUsage, // 技能用法
|
||||
SwitchSpeed, // 切换二倍速
|
||||
BulletTime, // 使用 1/5 的速度
|
||||
Output, // 仅输出,什么都不操作,界面上也不显示
|
||||
SkillDaemon, // 什么都不做,有技能开技能,直到战斗结束
|
||||
namespace sss // 保全派驻
|
||||
{
|
||||
struct Strategy
|
||||
{
|
||||
std::string core;
|
||||
RoleCounts tool_men;
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::None;
|
||||
};
|
||||
|
||||
/* for TRN */
|
||||
MoveCamera, // 引航者试炼,移动镜头
|
||||
struct CombatData : public copilot::CombatData
|
||||
{
|
||||
std::vector<Strategy> strategies;
|
||||
bool draw_as_possible = false;
|
||||
int retry_times = 0;
|
||||
std::vector<std::string> order_of_drops;
|
||||
};
|
||||
|
||||
/* for SSS */
|
||||
DrawCard, // “调配干员”
|
||||
CheckIfStartOver, // 检查如果没有某干员则退出重开
|
||||
};
|
||||
enum class EquipmentType
|
||||
{
|
||||
NotChoose,
|
||||
A,
|
||||
B,
|
||||
};
|
||||
|
||||
struct Action
|
||||
{
|
||||
int kills = 0;
|
||||
int costs = 0;
|
||||
int cost_changes = 0;
|
||||
int cooling = 0;
|
||||
ActionType type = ActionType::Deploy;
|
||||
std::string name; // 目标名,若 type >= SwitchSpeed, name 为空
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
SkillUsage modify_usage = SkillUsage::NotUse;
|
||||
int modify_times = 1; // 更改使用技能的次数,默认为 1,兼容曾经的作业
|
||||
int pre_delay = 0;
|
||||
int post_delay = 0;
|
||||
int time_out = INT_MAX; // TODO
|
||||
std::string doc;
|
||||
std::string doc_color;
|
||||
RoleCounts role_counts;
|
||||
std::pair<double, double> distance;
|
||||
};
|
||||
struct CompleteData
|
||||
{
|
||||
copilot::BasicInfo info;
|
||||
|
||||
struct BasicInfo
|
||||
{
|
||||
std::string stage_name;
|
||||
std::string minimum_required;
|
||||
std::string title;
|
||||
std::string title_color;
|
||||
std::string details;
|
||||
std::string details_color;
|
||||
};
|
||||
std::string buff;
|
||||
std::vector<EquipmentType> equipment;
|
||||
std::string strategy;
|
||||
|
||||
struct CombatData // 作业 JSON 数据
|
||||
{
|
||||
BasicInfo info;
|
||||
OperUsageGroups groups;
|
||||
std::vector<Action> actions;
|
||||
};
|
||||
} // namespace copilot
|
||||
copilot::OperUsageGroups groups;
|
||||
RoleCounts tool_men;
|
||||
std::vector<std::string> order_of_drops;
|
||||
std::unordered_set<std::string> blacklist;
|
||||
|
||||
namespace sss // 保全派驻
|
||||
{
|
||||
struct Strategy
|
||||
{
|
||||
std::string core;
|
||||
RoleCounts tool_men;
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::None;
|
||||
};
|
||||
std::unordered_map<std::string, CombatData> stages_data;
|
||||
};
|
||||
}
|
||||
|
||||
struct CombatData : public copilot::CombatData
|
||||
{
|
||||
std::vector<Strategy> strategies;
|
||||
bool draw_as_possible = false;
|
||||
int retry_times = 0;
|
||||
std::vector<std::string> order_of_drops;
|
||||
};
|
||||
namespace roguelike
|
||||
{
|
||||
struct ReplacementHome
|
||||
{
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
};
|
||||
|
||||
enum class EquipmentType
|
||||
{
|
||||
NotChoose,
|
||||
A,
|
||||
B,
|
||||
};
|
||||
struct DeployInfoWithRank
|
||||
{
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::None;
|
||||
int rank = 0;
|
||||
int kill_lower_bound = 0;
|
||||
int kill_upper_bound = 9999;
|
||||
};
|
||||
|
||||
struct CompleteData
|
||||
{
|
||||
copilot::BasicInfo info;
|
||||
struct ForceDeployDirection
|
||||
{
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
std::unordered_set<Role> role = {};
|
||||
};
|
||||
|
||||
std::string buff;
|
||||
std::vector<EquipmentType> equipment;
|
||||
std::string strategy;
|
||||
struct CombatData
|
||||
{
|
||||
std::string stage_name;
|
||||
std::vector<ReplacementHome> replacement_home;
|
||||
std::unordered_set<Point> blacklist_location;
|
||||
std::unordered_map<Point, ForceDeployDirection> force_deploy_direction;
|
||||
std::array<Role, 9> role_order = {};
|
||||
bool use_dice_stage = true;
|
||||
int stop_deploy_blocking_num = INT_MAX;
|
||||
int force_deploy_air_defense_num = 0;
|
||||
bool force_ban_medic = false;
|
||||
std::unordered_map<std::string, std::vector<DeployInfoWithRank>> deploy_plan;
|
||||
std::vector<DeployInfoWithRank> retreat_plan;
|
||||
};
|
||||
|
||||
copilot::OperUsageGroups groups;
|
||||
RoleCounts tool_men;
|
||||
std::vector<std::string> order_of_drops;
|
||||
std::unordered_set<std::string> blacklist;
|
||||
struct Recruitment
|
||||
{
|
||||
std::string name;
|
||||
Rect rect;
|
||||
int elite = 0;
|
||||
int level = 0;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, CombatData> stages_data;
|
||||
};
|
||||
}
|
||||
enum class SupportAnalyzeMode
|
||||
{
|
||||
ChooseSupportBtn,
|
||||
AnalyzeChars,
|
||||
RefreshSupportBtn
|
||||
};
|
||||
|
||||
namespace roguelike
|
||||
{
|
||||
struct ReplacementHome
|
||||
{
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
};
|
||||
struct RecruitSupportCharInfo
|
||||
{
|
||||
Recruitment oper_info;
|
||||
bool is_friend = false; // 是否为好友助战
|
||||
int max_elite = 0; // 两次招募后的实际精英化与等级
|
||||
int max_level = 0;
|
||||
};
|
||||
|
||||
struct DeployInfoWithRank
|
||||
{
|
||||
Point location;
|
||||
DeployDirection direction = DeployDirection::None;
|
||||
int rank = 0;
|
||||
int kill_lower_bound = 0;
|
||||
int kill_upper_bound = 9999;
|
||||
};
|
||||
|
||||
struct ForceDeployDirection
|
||||
{
|
||||
DeployDirection direction = DeployDirection::Right;
|
||||
std::unordered_set<Role> role = {};
|
||||
};
|
||||
|
||||
struct CombatData
|
||||
{
|
||||
std::string stage_name;
|
||||
std::vector<ReplacementHome> replacement_home;
|
||||
std::unordered_set<Point> blacklist_location;
|
||||
std::unordered_map<Point, ForceDeployDirection> force_deploy_direction;
|
||||
std::array<Role, 9> role_order = {};
|
||||
bool use_dice_stage = true;
|
||||
int stop_deploy_blocking_num = INT_MAX;
|
||||
int force_deploy_air_defense_num = 0;
|
||||
bool force_ban_medic = false;
|
||||
std::unordered_map<std::string, std::vector<DeployInfoWithRank>> deploy_plan;
|
||||
std::vector<DeployInfoWithRank> retreat_plan;
|
||||
};
|
||||
|
||||
struct Recruitment
|
||||
{
|
||||
std::string name;
|
||||
Rect rect;
|
||||
int elite = 0;
|
||||
int level = 0;
|
||||
};
|
||||
|
||||
enum class SupportAnalyzeMode
|
||||
{
|
||||
ChooseSupportBtn,
|
||||
AnalyzeChars,
|
||||
RefreshSupportBtn
|
||||
};
|
||||
|
||||
struct RecruitSupportCharInfo
|
||||
{
|
||||
Recruitment oper_info;
|
||||
bool is_friend = false; // 是否为好友助战
|
||||
int max_elite = 0; // 两次招募后的实际精英化与等级
|
||||
int max_level = 0;
|
||||
};
|
||||
|
||||
struct RefreshSupportInfo
|
||||
{
|
||||
Rect rect;
|
||||
bool in_cooldown = false;
|
||||
int remain_secs = 0; // 刷新冷却时间
|
||||
};
|
||||
} // namespace roguelike
|
||||
struct RefreshSupportInfo
|
||||
{
|
||||
Rect rect;
|
||||
bool in_cooldown = false;
|
||||
int remain_secs = 0; // 刷新冷却时间
|
||||
};
|
||||
} // namespace roguelike
|
||||
} // namespace asst::battle
|
||||
|
||||
@@ -113,6 +113,288 @@ asst::battle::copilot::OperUsageGroups asst::CopilotConfig::parse_groups(const j
|
||||
return groups;
|
||||
}
|
||||
|
||||
TriggerInfo asst::CopilotConfig::parse_trigger(const json::value& json)
|
||||
{
|
||||
TriggerInfo trigger;
|
||||
|
||||
trigger.kills = json.get("kills", TriggerInfo::DEACTIVE_KILLS);
|
||||
trigger.costs = json.get("costs", TriggerInfo::DEACTIVE_COST);
|
||||
trigger.cost_changes = json.get("cost_changes", TriggerInfo::DEACTIVE_COUNT);
|
||||
trigger.cooling = json.get("cooling", TriggerInfo::DEACTIVE_COOLING);
|
||||
trigger.count = json.get("count", TriggerInfo::DEACTIVE_COUNT);
|
||||
|
||||
if (auto category = json.find("category")) {
|
||||
trigger.category = TriggerInfo::loadCategoryFrom(category.value().as_string());
|
||||
}
|
||||
|
||||
return trigger;
|
||||
}
|
||||
|
||||
bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::battle::copilot::Action* _Out)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
static const std::unordered_map<std::string, ActionType> ActionTypeMapping = {
|
||||
{ "Deploy", ActionType::Deploy },
|
||||
{ "DEPLOY", ActionType::Deploy },
|
||||
{ "deploy", ActionType::Deploy },
|
||||
{ "部署", ActionType::Deploy },
|
||||
|
||||
{ "Skill", ActionType::UseSkill },
|
||||
{ "SKILL", ActionType::UseSkill },
|
||||
{ "skill", ActionType::UseSkill },
|
||||
{ "技能", ActionType::UseSkill },
|
||||
|
||||
{ "Retreat", ActionType::Retreat },
|
||||
{ "RETREAT", ActionType::Retreat },
|
||||
{ "retreat", ActionType::Retreat },
|
||||
{ "撤退", ActionType::Retreat },
|
||||
|
||||
{ "SkillUsage", ActionType::SkillUsage },
|
||||
{ "SKILLUSAGE", ActionType::SkillUsage },
|
||||
{ "Skillusage", ActionType::SkillUsage },
|
||||
{ "skillusage", ActionType::SkillUsage },
|
||||
{ "技能用法", ActionType::SkillUsage },
|
||||
|
||||
{ "SpeedUp", ActionType::SwitchSpeed },
|
||||
{ "SPEEDUP", ActionType::SwitchSpeed },
|
||||
{ "Speedup", ActionType::SwitchSpeed },
|
||||
{ "speedup", ActionType::SwitchSpeed },
|
||||
{ "二倍速", ActionType::SwitchSpeed },
|
||||
|
||||
{ "BulletTime", ActionType::BulletTime },
|
||||
{ "BULLETTIME", ActionType::BulletTime },
|
||||
{ "Bullettime", ActionType::BulletTime },
|
||||
{ "bullettime", ActionType::BulletTime },
|
||||
{ "子弹时间", ActionType::BulletTime },
|
||||
|
||||
{ "Output", ActionType::Output },
|
||||
{ "OUTPUT", ActionType::Output },
|
||||
{ "output", ActionType::Output },
|
||||
{ "输出", ActionType::Output },
|
||||
{ "打印", ActionType::Output },
|
||||
|
||||
{ "SkillDaemon", ActionType::SkillDaemon },
|
||||
{ "skilldaemon", ActionType::SkillDaemon },
|
||||
{ "SKILLDAEMON", ActionType::SkillDaemon },
|
||||
{ "Skilldaemon", ActionType::SkillDaemon },
|
||||
{ "DoNothing", ActionType::SkillDaemon },
|
||||
{ "摆完挂机", ActionType::SkillDaemon },
|
||||
{ "开摆", ActionType::SkillDaemon },
|
||||
|
||||
{ "MoveCamera", ActionType::MoveCamera },
|
||||
{ "movecamera", ActionType::MoveCamera },
|
||||
{ "MOVECAMERA", ActionType::MoveCamera },
|
||||
{ "Movecamera", ActionType::MoveCamera },
|
||||
{ "移动镜头", ActionType::MoveCamera },
|
||||
|
||||
{ "DrawCard", ActionType::DrawCard },
|
||||
{ "drawcard", ActionType::DrawCard },
|
||||
{ "DRAWCARD", ActionType::DrawCard },
|
||||
{ "Drawcard", ActionType::DrawCard },
|
||||
{ "抽卡", ActionType::DrawCard },
|
||||
{ "抽牌", ActionType::DrawCard },
|
||||
{ "调配", ActionType::DrawCard },
|
||||
{ "调配干员", ActionType::DrawCard },
|
||||
|
||||
{ "CheckIfStartOver", ActionType::CheckIfStartOver },
|
||||
{ "Checkifstartover", ActionType::CheckIfStartOver },
|
||||
{ "CHECKIFSTARTOVER", ActionType::CheckIfStartOver },
|
||||
{ "checkifstartover", ActionType::CheckIfStartOver },
|
||||
{ "检查重开", ActionType::CheckIfStartOver },
|
||||
|
||||
{ "Loop", ActionType::Loop },
|
||||
{ "loop", ActionType::Loop },
|
||||
{ "LOOP", ActionType::Loop },
|
||||
{ "循环", ActionType::Loop },
|
||||
|
||||
{ "Case", ActionType::Case },
|
||||
{ "case", ActionType::Case },
|
||||
{ "CASE", ActionType::Case },
|
||||
{ "派发", ActionType::Case },
|
||||
{ "干员派发", ActionType::Case },
|
||||
|
||||
{ "Check", ActionType::Check },
|
||||
{ "check", ActionType::Check },
|
||||
{ "CHECK", ActionType::Check },
|
||||
{ "检查", ActionType::Check },
|
||||
{ "分支", ActionType::Check },
|
||||
|
||||
{ "Until", ActionType::Until },
|
||||
{ "until", ActionType::Until },
|
||||
{ "UNTIL", ActionType::Until },
|
||||
{ "直到", ActionType::Until },
|
||||
};
|
||||
|
||||
auto& action = (*_Out);
|
||||
|
||||
std::string type_str = action_info.get("type", "Deploy");
|
||||
|
||||
if (auto iter = ActionTypeMapping.find(type_str); iter != ActionTypeMapping.end()) {
|
||||
action.type = iter->second;
|
||||
}
|
||||
else {
|
||||
Log.warn("Unknown action type:", type_str);
|
||||
return false;
|
||||
}
|
||||
// 解析锚点编码,可选
|
||||
action.point_code = action_info.get("point_code", std::string());
|
||||
|
||||
// 解析动作触发信息
|
||||
action.trigger = parse_trigger(action_info);
|
||||
|
||||
// 解析前置条件满足之后的前后时延
|
||||
action.delay.pre_delay = action_info.get("pre_delay", 0);
|
||||
auto post_delay_opt = action_info.find<int>("post_delay");
|
||||
action.delay.post_delay = post_delay_opt ? *post_delay_opt : action_info.get("rear_delay", 0);
|
||||
// 历史遗留字段,兼容一下
|
||||
action.delay.time_out = action_info.get("timeout", INT_MAX);
|
||||
|
||||
// 解析行动的相关附加文本
|
||||
action.text.doc = action_info.get("doc", std::string());
|
||||
action.text.doc_color = action_info.get("doc_color", std::string());
|
||||
|
||||
// 根据动作的类型解析载荷
|
||||
switch (action.type) {
|
||||
case ActionType::Deploy:
|
||||
case ActionType::UseSkill:
|
||||
case ActionType::Retreat:
|
||||
case ActionType::BulletTime:
|
||||
case ActionType::SkillUsage: {
|
||||
auto& avatar = action.payload.emplace<AvatarInfo>();
|
||||
avatar.name = action_info.get("name", std::string());
|
||||
avatar.location.x = action_info.get("location", 0, 0);
|
||||
avatar.location.y = action_info.get("location", 1, 0);
|
||||
avatar.direction = string_to_direction(action_info.get("direction", "Right"));
|
||||
|
||||
avatar.modify_usage = static_cast<battle::SkillUsage>(action_info.get("skill_usage", 0));
|
||||
avatar.modify_times = action_info.get("skill_times", 1);
|
||||
} break;
|
||||
case ActionType::CheckIfStartOver: {
|
||||
auto& info = action.payload.emplace<CheckIfStartOverInfo>();
|
||||
|
||||
info.name = action_info.get("name", std::string());
|
||||
if (auto tool_men = action_info.find("tool_men")) {
|
||||
info.role_counts = parse_role_counts(*tool_men);
|
||||
}
|
||||
} break;
|
||||
case ActionType::MoveCamera: {
|
||||
auto dist_arr = action_info.at("distance").as_array();
|
||||
action.payload.emplace<MoveCameraInfo>(std::make_pair(dist_arr[0].as_double(), dist_arr[1].as_double()));
|
||||
} break;
|
||||
case ActionType::Loop: {
|
||||
auto& loop = action.payload.emplace<LoopInfo>();
|
||||
|
||||
// 必选字段
|
||||
loop.end_info = parse_trigger(action_info.at("end"));
|
||||
if (loop.end_info.category == TriggerInfo::Category::None) {
|
||||
// 默认使用all策略,表示只有全部满足才算生效
|
||||
loop.end_info.category = TriggerInfo::Category::All;
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("continue")) {
|
||||
loop.continue_info = parse_trigger(t.value());
|
||||
|
||||
if (loop.continue_info.category == TriggerInfo::Category::None) {
|
||||
// 默认使用all策略,表示只有全部满足才算生效
|
||||
loop.continue_info.category = TriggerInfo::Category::All;
|
||||
}
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("break")) {
|
||||
loop.break_info = parse_trigger(t.value());
|
||||
|
||||
if (loop.break_info.category == TriggerInfo::Category::None) {
|
||||
// 默认使用all策略,表示只有全部满足才算生效
|
||||
loop.break_info.category = TriggerInfo::Category::All;
|
||||
}
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("loop_actions")) {
|
||||
loop.loop_actions = parse_actions_ptr(t.value());
|
||||
}
|
||||
|
||||
} break;
|
||||
case ActionType::Case: {
|
||||
auto& case_info = action.payload.emplace<CaseInfo>();
|
||||
|
||||
// 必选字段
|
||||
case_info.group_select = action_info.at("select").as_string();
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("dispatch_actions")) {
|
||||
for (auto const& [name, batch] : t.value().as_object()) {
|
||||
case_info.dispatch_actions.emplace(name, parse_actions_ptr(batch));
|
||||
}
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("default_action")) {
|
||||
case_info.default_action = parse_actions_ptr(t.value());
|
||||
}
|
||||
} break;
|
||||
case ActionType::Check: {
|
||||
auto& check = action.payload.emplace<CheckInfo>();
|
||||
|
||||
// 必选字段
|
||||
check.condition_info = parse_trigger(action_info.at("condition"));
|
||||
|
||||
if (check.condition_info.category == TriggerInfo::Category::None) {
|
||||
// 默认使用all策略,表示只有全部满足才算生效
|
||||
check.condition_info.category = TriggerInfo::Category::All;
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("then_actions")) {
|
||||
check.then_actions = parse_actions_ptr(t.value());
|
||||
}
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("else_actions")) {
|
||||
check.else_actions = parse_actions_ptr(t.value());
|
||||
}
|
||||
} break;
|
||||
case ActionType::Until: {
|
||||
auto& until = action.payload.emplace<UntilInfo>();
|
||||
|
||||
// 必选字段
|
||||
until.category = TriggerInfo::loadCategoryFrom(action_info.at("category").as_string());
|
||||
switch (until.category) {
|
||||
case TriggerInfo::Category::Any:
|
||||
case TriggerInfo::Category::All:
|
||||
case TriggerInfo::Category::Succ:
|
||||
break;
|
||||
default:
|
||||
until.category = TriggerInfo::Category::All;
|
||||
break;
|
||||
}
|
||||
|
||||
// 必选字段,缺省值为0
|
||||
action.trigger.count = action_info.get("limit", 0);
|
||||
|
||||
// 可选字段
|
||||
if (auto t = action_info.find("candidate_actions")) {
|
||||
until.candidate = parse_actions_ptr(t.value());
|
||||
}
|
||||
} break;
|
||||
case ActionType::SwitchSpeed:
|
||||
case ActionType::Output:
|
||||
case ActionType::SkillDaemon:
|
||||
case ActionType::DrawCard:
|
||||
case ActionType::SavePoint:
|
||||
case ActionType::SyncPoint:
|
||||
case ActionType::CheckPoint:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<asst::battle::copilot::Action> asst::CopilotConfig::parse_actions(const json::value& json)
|
||||
{
|
||||
LogTraceFunction;
|
||||
@@ -120,115 +402,10 @@ std::vector<asst::battle::copilot::Action> asst::CopilotConfig::parse_actions(co
|
||||
std::vector<battle::copilot::Action> actions_list;
|
||||
|
||||
for (const auto& action_info : json.at("actions").as_array()) {
|
||||
Action action;
|
||||
static const std::unordered_map<std::string, ActionType> ActionTypeMapping = {
|
||||
{ "Deploy", ActionType::Deploy },
|
||||
{ "DEPLOY", ActionType::Deploy },
|
||||
{ "deploy", ActionType::Deploy },
|
||||
{ "部署", ActionType::Deploy },
|
||||
|
||||
{ "Skill", ActionType::UseSkill },
|
||||
{ "SKILL", ActionType::UseSkill },
|
||||
{ "skill", ActionType::UseSkill },
|
||||
{ "技能", ActionType::UseSkill },
|
||||
|
||||
{ "Retreat", ActionType::Retreat },
|
||||
{ "RETREAT", ActionType::Retreat },
|
||||
{ "retreat", ActionType::Retreat },
|
||||
{ "撤退", ActionType::Retreat },
|
||||
|
||||
{ "SpeedUp", ActionType::SwitchSpeed },
|
||||
{ "SPEEDUP", ActionType::SwitchSpeed },
|
||||
{ "Speedup", ActionType::SwitchSpeed },
|
||||
{ "speedup", ActionType::SwitchSpeed },
|
||||
{ "二倍速", ActionType::SwitchSpeed },
|
||||
|
||||
{ "BulletTime", ActionType::BulletTime },
|
||||
{ "BULLETTIME", ActionType::BulletTime },
|
||||
{ "Bullettime", ActionType::BulletTime },
|
||||
{ "bullettime", ActionType::BulletTime },
|
||||
{ "子弹时间", ActionType::BulletTime },
|
||||
|
||||
{ "SkillUsage", ActionType::SkillUsage },
|
||||
{ "SKILLUSAGE", ActionType::SkillUsage },
|
||||
{ "Skillusage", ActionType::SkillUsage },
|
||||
{ "skillusage", ActionType::SkillUsage },
|
||||
{ "技能用法", ActionType::SkillUsage },
|
||||
|
||||
{ "Output", ActionType::Output },
|
||||
{ "OUTPUT", ActionType::Output },
|
||||
{ "output", ActionType::Output },
|
||||
{ "输出", ActionType::Output },
|
||||
{ "打印", ActionType::Output },
|
||||
|
||||
{ "SkillDaemon", ActionType::SkillDaemon },
|
||||
{ "skilldaemon", ActionType::SkillDaemon },
|
||||
{ "SKILLDAEMON", ActionType::SkillDaemon },
|
||||
{ "Skilldaemon", ActionType::SkillDaemon },
|
||||
{ "DoNothing", ActionType::SkillDaemon },
|
||||
{ "摆完挂机", ActionType::SkillDaemon },
|
||||
{ "开摆", ActionType::SkillDaemon },
|
||||
|
||||
{ "MoveCamera", ActionType::MoveCamera },
|
||||
{ "movecamera", ActionType::MoveCamera },
|
||||
{ "MOVECAMERA", ActionType::MoveCamera },
|
||||
{ "Movecamera", ActionType::MoveCamera },
|
||||
{ "移动镜头", ActionType::MoveCamera },
|
||||
|
||||
{ "DrawCard", ActionType::DrawCard },
|
||||
{ "drawcard", ActionType::DrawCard },
|
||||
{ "DRAWCARD", ActionType::DrawCard },
|
||||
{ "Drawcard", ActionType::DrawCard },
|
||||
{ "抽卡", ActionType::DrawCard },
|
||||
{ "抽牌", ActionType::DrawCard },
|
||||
{ "调配", ActionType::DrawCard },
|
||||
{ "调配干员", ActionType::DrawCard },
|
||||
|
||||
{ "CheckIfStartOver", ActionType::CheckIfStartOver },
|
||||
{ "Checkifstartover", ActionType::CheckIfStartOver },
|
||||
{ "CHECKIFSTARTOVER", ActionType::CheckIfStartOver },
|
||||
{ "checkifstartover", ActionType::CheckIfStartOver },
|
||||
{ "检查重开", ActionType::CheckIfStartOver },
|
||||
};
|
||||
|
||||
std::string type_str = action_info.get("type", "Deploy");
|
||||
|
||||
if (auto iter = ActionTypeMapping.find(type_str); iter != ActionTypeMapping.end()) {
|
||||
action.type = iter->second;
|
||||
}
|
||||
else {
|
||||
Log.warn("Unknown action type:", type_str);
|
||||
battle::copilot::Action action;
|
||||
if (!parse_action(action_info, &action)) {
|
||||
continue;
|
||||
}
|
||||
action.kills = action_info.get("kills", 0);
|
||||
action.cost_changes = action_info.get("cost_changes", 0);
|
||||
action.costs = action_info.get("costs", 0);
|
||||
action.cooling = action_info.get("cooling", -1);
|
||||
action.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 = string_to_direction(action_info.get("direction", "Right"));
|
||||
|
||||
action.modify_usage = static_cast<battle::SkillUsage>(action_info.get("skill_usage", 0));
|
||||
action.modify_times = action_info.get("skill_times", 1);
|
||||
action.pre_delay = action_info.get("pre_delay", 0);
|
||||
auto post_delay_opt = action_info.find<int>("post_delay");
|
||||
// 历史遗留字段,兼容一下
|
||||
action.post_delay = post_delay_opt ? *post_delay_opt : action_info.get("rear_delay", 0);
|
||||
action.time_out = action_info.get("timeout", INT_MAX);
|
||||
action.doc = action_info.get("doc", std::string());
|
||||
action.doc_color = action_info.get("doc_color", std::string());
|
||||
|
||||
if (action.type == ActionType::CheckIfStartOver) {
|
||||
if (auto tool_men = action_info.find("tool_men")) {
|
||||
action.role_counts = parse_role_counts(*tool_men);
|
||||
}
|
||||
}
|
||||
else if (action.type == ActionType::MoveCamera) {
|
||||
auto dist_arr = action_info.at("distance").as_array();
|
||||
action.distance = std::make_pair(dist_arr[0].as_double(), dist_arr[1].as_double());
|
||||
}
|
||||
|
||||
actions_list.emplace_back(std::move(action));
|
||||
}
|
||||
@@ -236,6 +413,24 @@ std::vector<asst::battle::copilot::Action> asst::CopilotConfig::parse_actions(co
|
||||
return actions_list;
|
||||
}
|
||||
|
||||
std::vector<asst::battle::copilot::ActionPtr> asst::CopilotConfig::parse_actions_ptr(const json::value& json)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
std::vector<battle::copilot::ActionPtr> actions_list;
|
||||
|
||||
for (const auto& action_info : json.as_array()) {
|
||||
battle::copilot::ActionPtr action = std::make_shared<battle::copilot::Action>();
|
||||
if (!parse_action(action_info, action.get())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
actions_list.emplace_back(action);
|
||||
}
|
||||
|
||||
return actions_list;
|
||||
}
|
||||
|
||||
asst::battle::RoleCounts asst::CopilotConfig::parse_role_counts(const json::value& json)
|
||||
{
|
||||
battle::RoleCounts counts;
|
||||
|
||||
@@ -4,28 +4,33 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class CopilotConfig : public SingletonHolder<CopilotConfig>, public AbstractConfig
|
||||
{
|
||||
public:
|
||||
static battle::copilot::BasicInfo parse_basic_info(const json::value& json);
|
||||
static battle::copilot::OperUsageGroups parse_groups(const json::value& json);
|
||||
static std::vector<battle::copilot::Action> parse_actions(const json::value& json);
|
||||
static battle::RoleCounts parse_role_counts(const json::value& json);
|
||||
static battle::DeployDirection string_to_direction(const std::string& str);
|
||||
class CopilotConfig : public SingletonHolder<CopilotConfig>, public AbstractConfig
|
||||
{
|
||||
public:
|
||||
static battle::copilot::BasicInfo parse_basic_info(const json::value& json);
|
||||
static battle::copilot::OperUsageGroups parse_groups(const json::value& json);
|
||||
static battle::copilot::TriggerInfo parse_trigger(const json::value& json);
|
||||
static bool parse_action(const json::value& json, asst::battle::copilot::Action*);
|
||||
static std::vector<battle::copilot::ActionPtr> parse_actions_ptr(const json::value& json);
|
||||
static std::vector<battle::copilot::Action> parse_actions(const json::value& json);
|
||||
static battle::RoleCounts parse_role_counts(const json::value& json);
|
||||
static battle::DeployDirection string_to_direction(const std::string& str);
|
||||
|
||||
public:
|
||||
virtual ~CopilotConfig() override = default;
|
||||
public:
|
||||
virtual ~CopilotConfig() override = default;
|
||||
|
||||
const battle::copilot::CombatData& get_data() const noexcept { return m_data; }
|
||||
const std::string& get_stage_name() const noexcept { return m_data.info.stage_name; }
|
||||
bool parse_magic_code(const std::string& copilot_magic_code);
|
||||
void clear();
|
||||
const battle::copilot::CombatData& get_data() const noexcept { return m_data; }
|
||||
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
const std::string& get_stage_name() const noexcept { return m_data.info.stage_name; }
|
||||
|
||||
battle::copilot::CombatData m_data;
|
||||
};
|
||||
bool parse_magic_code(const std::string& copilot_magic_code);
|
||||
void clear();
|
||||
|
||||
inline static auto& Copilot = CopilotConfig::get_instance();
|
||||
protected:
|
||||
virtual bool parse(const json::value& json) override;
|
||||
|
||||
battle::copilot::CombatData m_data;
|
||||
};
|
||||
|
||||
inline static auto& Copilot = CopilotConfig::get_instance();
|
||||
}
|
||||
|
||||
@@ -14,88 +14,95 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class BattleHelper
|
||||
{
|
||||
public:
|
||||
~BattleHelper() = default;
|
||||
class BattleHelper
|
||||
{
|
||||
public:
|
||||
~BattleHelper() = default;
|
||||
|
||||
protected:
|
||||
BattleHelper(Assistant* inst);
|
||||
protected:
|
||||
BattleHelper(Assistant* inst);
|
||||
|
||||
virtual AbstractTask& this_task() = 0;
|
||||
virtual AbstractTask& this_task() = 0;
|
||||
|
||||
virtual bool set_stage_name(const std::string& name);
|
||||
virtual void clear();
|
||||
virtual const std::string oper_name_ocr_task_name() const noexcept { return "BattleOperName"; }
|
||||
virtual bool do_strategic_action(const cv::Mat& reusable = cv::Mat());
|
||||
virtual bool set_stage_name(const std::string& name);
|
||||
virtual void clear();
|
||||
|
||||
bool calc_tiles_info(const std::string& stage_name, double shift_x = 0, double shift_y = 0);
|
||||
virtual const std::string oper_name_ocr_task_name() const noexcept { return "BattleOperName"; }
|
||||
|
||||
bool pause();
|
||||
bool speed_up();
|
||||
bool abandon();
|
||||
virtual bool do_strategic_action(const cv::Mat& reusable = cv::Mat());
|
||||
|
||||
bool update_deployment(bool init = false, const cv::Mat& reusable = cv::Mat(), bool need_oper_cost = false);
|
||||
bool update_kills(const cv::Mat& reusable = cv::Mat());
|
||||
bool update_cost(const cv::Mat& reusable = cv::Mat());
|
||||
bool calc_tiles_info(const std::string& stage_name, double shift_x = 0, double shift_y = 0);
|
||||
|
||||
bool deploy_oper(const std::string& name, const Point& loc, battle::DeployDirection direction);
|
||||
bool retreat_oper(const std::string& name);
|
||||
bool retreat_oper(const Point& loc, bool manually = true);
|
||||
bool use_skill(const std::string& name, bool keep_waiting = true);
|
||||
bool use_skill(const Point& loc, bool keep_waiting = true);
|
||||
bool check_pause_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_skip_plot_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_in_speed_up(const cv::Mat& reusable = cv::Mat());
|
||||
virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true);
|
||||
virtual bool wait_until_start(bool weak = true);
|
||||
bool wait_until_end(bool weak = true);
|
||||
bool use_all_ready_skill(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_and_use_skill(const std::string& name, bool& has_error, const cv::Mat& reusable = cv::Mat());
|
||||
bool check_and_use_skill(const Point& loc, bool& has_error, const cv::Mat& reusable = cv::Mat());
|
||||
void save_map(const cv::Mat& image);
|
||||
bool pause();
|
||||
bool speed_up();
|
||||
bool abandon();
|
||||
|
||||
bool click_oper_on_deployment(const std::string& name);
|
||||
bool click_oper_on_deployment(const Rect& rect);
|
||||
bool click_oper_on_battlefield(const std::string& name);
|
||||
bool click_oper_on_battlefield(const Point& loc);
|
||||
bool click_retreat(); // 这个是不带识别的,直接点
|
||||
bool click_skill(bool keep_waiting = true); // 这个是带识别的,转好了才点
|
||||
bool cancel_oper_selection();
|
||||
// 修正终点超出范围的滑动,纠正时是否需要顺时针旋转
|
||||
void fix_swipe_out_of_limit(Point& p1, Point& p2, int width, int height, int max_distance = INT_MAX,
|
||||
double radian = 0);
|
||||
bool move_camera(const std::pair<double, double>& delta);
|
||||
bool update_deployment(bool init = false, const cv::Mat& reusable = cv::Mat(), bool need_oper_cost = false);
|
||||
bool update_kills(const cv::Mat& reusable = cv::Mat());
|
||||
bool update_cost(const cv::Mat& reusable = cv::Mat());
|
||||
|
||||
std::string analyze_detail_page_oper_name(const cv::Mat& image);
|
||||
bool deploy_oper(const std::string& name, const Point& loc, battle::DeployDirection direction);
|
||||
bool retreat_oper(const std::string& name);
|
||||
bool retreat_oper(const Point& loc, bool manually = true);
|
||||
bool use_skill(const std::string& name, bool keep_waiting = true);
|
||||
bool use_skill(const Point& loc, bool keep_waiting = true);
|
||||
bool check_pause_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_skip_plot_button(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_in_speed_up(const cv::Mat& reusable = cv::Mat());
|
||||
virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true);
|
||||
virtual bool wait_until_start(bool weak = true);
|
||||
bool wait_until_end(bool weak = true);
|
||||
bool use_all_ready_skill(const cv::Mat& reusable = cv::Mat());
|
||||
bool check_and_use_skill(const std::string& name, bool& has_error, const cv::Mat& reusable = cv::Mat());
|
||||
bool check_and_use_skill(const Point& loc, bool& has_error, const cv::Mat& reusable = cv::Mat());
|
||||
void save_map(const cv::Mat& image);
|
||||
|
||||
std::optional<Rect> get_oper_rect_on_deployment(const std::string& name) const;
|
||||
bool click_oper_on_deployment(const std::string& name);
|
||||
bool click_oper_on_deployment(const Rect& rect);
|
||||
bool click_oper_on_battlefield(const std::string& name);
|
||||
bool click_oper_on_battlefield(const Point& loc);
|
||||
bool click_retreat(); // 这个是不带识别的,直接点
|
||||
bool click_skill(bool keep_waiting = true); // 这个是带识别的,转好了才点
|
||||
bool cancel_oper_selection();
|
||||
// 修正终点超出范围的滑动,纠正时是否需要顺时针旋转
|
||||
void fix_swipe_out_of_limit(
|
||||
Point& p1,
|
||||
Point& p2,
|
||||
int width,
|
||||
int height,
|
||||
int max_distance = INT_MAX,
|
||||
double radian = 0);
|
||||
bool move_camera(const std::pair<double, double>& delta);
|
||||
|
||||
std::string m_stage_name;
|
||||
Map::Level m_map_data;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info; // 子弹时间的坐标映射
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info; // 正常的坐标映射
|
||||
Point m_skill_button_pos;
|
||||
Point m_retreat_button_pos;
|
||||
std::unordered_map<std::string, battle::SkillUsage> m_skill_usage;
|
||||
std::unordered_map<std::string, int> m_skill_times;
|
||||
std::unordered_map<std::string, int> m_skill_error_count;
|
||||
std::unordered_map<std::string, std::chrono::steady_clock::time_point> m_last_use_skill_time;
|
||||
int m_camera_count = 0;
|
||||
std::pair<double, double> m_camera_shift = { 0., 0. };
|
||||
std::string analyze_detail_page_oper_name(const cv::Mat& image);
|
||||
|
||||
/* 实时更新的数据 */
|
||||
bool m_in_battle = false;
|
||||
int m_kills = 0;
|
||||
int m_total_kills = 0;
|
||||
int m_cost = 0;
|
||||
std::optional<Rect> get_oper_rect_on_deployment(const std::string& name) const;
|
||||
|
||||
std::vector<battle::DeploymentOper> m_cur_deployment_opers;
|
||||
std::string m_stage_name;
|
||||
Map::Level m_map_data;
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_side_tile_info; // 子弹时间的坐标映射
|
||||
std::unordered_map<Point, TilePack::TileInfo> m_normal_tile_info; // 正常的坐标映射
|
||||
Point m_skill_button_pos;
|
||||
Point m_retreat_button_pos;
|
||||
std::unordered_map<std::string, battle::SkillUsage> m_skill_usage;
|
||||
std::unordered_map<std::string, int> m_skill_times;
|
||||
std::unordered_map<std::string, int> m_skill_error_count;
|
||||
std::unordered_map<std::string, std::chrono::steady_clock::time_point> m_last_use_skill_time;
|
||||
int m_camera_count = 0;
|
||||
std::pair<double, double> m_camera_shift = { 0., 0. };
|
||||
|
||||
std::map<std::string, Point> m_battlefield_opers;
|
||||
std::map<Point, std::string> m_used_tiles;
|
||||
/* 实时更新的数据 */
|
||||
bool m_in_battle = false;
|
||||
int m_total_kills = 0;
|
||||
int m_kills = 0;
|
||||
int m_cost = 0;
|
||||
|
||||
private:
|
||||
InstHelper m_inst_helper;
|
||||
};
|
||||
std::vector<battle::DeploymentOper> m_cur_deployment_opers;
|
||||
|
||||
std::map<std::string, Point> m_battlefield_opers;
|
||||
std::map<Point, std::string> m_used_tiles;
|
||||
|
||||
private:
|
||||
InstHelper m_inst_helper;
|
||||
};
|
||||
} // namespace asst
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
using namespace asst::battle;
|
||||
using namespace asst::battle::copilot;
|
||||
|
||||
asst::BattleProcessTask::BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain)
|
||||
: AbstractTask(callback, inst, task_chain), BattleHelper(inst)
|
||||
{}
|
||||
asst::BattleProcessTask::BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain) :
|
||||
AbstractTask(callback, inst, task_chain),
|
||||
BattleHelper(inst)
|
||||
{
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::_run()
|
||||
{
|
||||
@@ -44,7 +46,7 @@ bool asst::BattleProcessTask::_run()
|
||||
size_t action_size = get_combat_data().actions.size();
|
||||
for (size_t i = 0; i < action_size && !need_exit() && m_in_battle; ++i) {
|
||||
const auto& action = get_combat_data().actions.at(i);
|
||||
do_action(action, i);
|
||||
do_action_sync(action, i);
|
||||
}
|
||||
|
||||
if (need_to_wait_until_end()) {
|
||||
@@ -85,7 +87,8 @@ void asst::BattleProcessTask::set_wait_until_end(bool wait_until_end)
|
||||
m_need_to_wait_until_end = wait_until_end;
|
||||
}
|
||||
|
||||
void asst::BattleProcessTask::set_formation_task_ptr(std::shared_ptr<std::unordered_map<std::string, std::string>> value)
|
||||
void asst::BattleProcessTask::set_formation_task_ptr(
|
||||
std::shared_ptr<std::unordered_map<std::string, std::string>> value)
|
||||
{
|
||||
m_formation_ptr = value;
|
||||
}
|
||||
@@ -139,7 +142,12 @@ bool asst::BattleProcessTask::to_group()
|
||||
m_oper_in_group.merge(ungrouped);
|
||||
|
||||
for (const auto& action : m_combat_data.actions) {
|
||||
const std::string& action_name = action.name;
|
||||
if (!action.hasAvatarInfo()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
const std::string& action_name = avatar.name;
|
||||
if (action_name.empty() || m_oper_in_group.contains(action_name)) {
|
||||
continue;
|
||||
}
|
||||
@@ -164,89 +172,339 @@ bool asst::BattleProcessTask::to_group()
|
||||
|
||||
bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, size_t index)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
notify_action(action);
|
||||
|
||||
thread_local auto prev_frame_time = std::chrono::steady_clock::time_point {};
|
||||
static const auto min_frame_interval = std::chrono::milliseconds(Config.get_options().copilot_fight_screencap_interval);
|
||||
|
||||
// prevent our program from consuming too much CPU
|
||||
if (const auto now = std::chrono::steady_clock::now();
|
||||
prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
Log.debug("Sleeping for framerate limit");
|
||||
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
|
||||
}
|
||||
|
||||
if (!wait_condition(action)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
if (action.pre_delay > 0) {
|
||||
sleep_and_do_strategy(action.pre_delay);
|
||||
if (action.delay.pre_delay > 0) {
|
||||
sleep_and_do_strategy(action.delay.pre_delay);
|
||||
// 等待之后画面可能会变化,更新下干员信息
|
||||
update_deployment();
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
const std::string& name = get_name_from_group(action.name);
|
||||
const auto& location = action.location;
|
||||
|
||||
switch (action.type) {
|
||||
case ActionType::Deploy:
|
||||
ret = deploy_oper(name, location, action.direction);
|
||||
if (ret) m_in_bullet_time = false;
|
||||
break;
|
||||
case ActionType::Deploy: {
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
|
||||
const std::string& name = get_name_from_group(avatar.name);
|
||||
const auto& location = avatar.location;
|
||||
|
||||
ret = deploy_oper(name, location, avatar.direction);
|
||||
if (ret) {
|
||||
m_in_bullet_time = false;
|
||||
}
|
||||
} break;
|
||||
case ActionType::Retreat: {
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
|
||||
const std::string& name = get_name_from_group(avatar.name);
|
||||
const auto& location = avatar.location;
|
||||
|
||||
case ActionType::Retreat:
|
||||
ret = m_in_bullet_time ? click_retreat() : (location.empty() ? retreat_oper(name) : retreat_oper(location));
|
||||
if (ret) m_in_bullet_time = false;
|
||||
break;
|
||||
if (ret) {
|
||||
m_in_bullet_time = false;
|
||||
}
|
||||
} break;
|
||||
case ActionType::UseSkill: {
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
|
||||
const std::string& name = get_name_from_group(avatar.name);
|
||||
const auto& location = avatar.location;
|
||||
|
||||
case ActionType::UseSkill:
|
||||
ret = m_in_bullet_time ? click_skill() : (location.empty() ? use_skill(name) : use_skill(location));
|
||||
if (ret) m_in_bullet_time = false;
|
||||
break;
|
||||
|
||||
if (ret) {
|
||||
m_in_bullet_time = false;
|
||||
}
|
||||
} break;
|
||||
case ActionType::SwitchSpeed:
|
||||
ret = speed_up();
|
||||
break;
|
||||
case ActionType::BulletTime: {
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
|
||||
const std::string& name = get_name_from_group(avatar.name);
|
||||
const auto& location = avatar.location;
|
||||
|
||||
case ActionType::BulletTime:
|
||||
ret = enter_bullet_time(name, location);
|
||||
if (ret) m_in_bullet_time = true;
|
||||
break;
|
||||
if (ret) {
|
||||
m_in_bullet_time = true;
|
||||
}
|
||||
} break;
|
||||
case ActionType::SkillUsage: {
|
||||
auto& avatar = action.getPayload<AvatarInfo>();
|
||||
|
||||
case ActionType::SkillUsage:
|
||||
m_skill_usage[name] = action.modify_usage;
|
||||
if (action.modify_usage == SkillUsage::Times) m_skill_times[name] = action.modify_times;
|
||||
const std::string& name = get_name_from_group(avatar.name);
|
||||
|
||||
m_skill_usage[name] = avatar.modify_usage;
|
||||
if (avatar.modify_usage == SkillUsage::Times) {
|
||||
m_skill_times[name] = avatar.modify_times;
|
||||
}
|
||||
ret = true;
|
||||
break;
|
||||
|
||||
} break;
|
||||
case ActionType::Output:
|
||||
// DoNothing
|
||||
ret = true;
|
||||
break;
|
||||
case ActionType::MoveCamera: {
|
||||
auto& info = action.getPayload<MoveCameraInfo>();
|
||||
|
||||
case ActionType::MoveCamera:
|
||||
ret = move_camera(action.distance);
|
||||
break;
|
||||
|
||||
ret = move_camera(info.distance);
|
||||
} break;
|
||||
case ActionType::SkillDaemon:
|
||||
ret = wait_until_end();
|
||||
break;
|
||||
case ActionType::Loop: {
|
||||
auto& info = action.getPayload<LoopInfo>();
|
||||
|
||||
// 假设被设置了自然数才赋值
|
||||
info.end_info.resetCounter();
|
||||
|
||||
while (!check_condition(info.end_info)) {
|
||||
// 需要维护counter
|
||||
info.end_info.activeCounter();
|
||||
|
||||
// 执行循环体
|
||||
for (int i = 0; i < info.loop_actions.size(); ++i) {
|
||||
if (need_exit() || !m_in_battle) {
|
||||
goto END_LOOP;
|
||||
}
|
||||
|
||||
if (info.continue_info.active() && !check_condition(info.continue_info)) {
|
||||
goto NEXT_LOOP;
|
||||
}
|
||||
|
||||
if (info.break_info.active() && !check_condition(info.break_info)) {
|
||||
goto BREAK_LOOP;
|
||||
}
|
||||
|
||||
ret &= do_action_sync(*info.loop_actions[i], i);
|
||||
}
|
||||
|
||||
NEXT_LOOP:
|
||||
continue;
|
||||
|
||||
BREAK_LOOP:
|
||||
break;
|
||||
}
|
||||
|
||||
END_LOOP:;
|
||||
} break;
|
||||
case ActionType::Case: {
|
||||
auto& info = action.getPayload<CaseInfo>();
|
||||
|
||||
if (auto it = m_oper_in_group.find(info.group_select); it != m_oper_in_group.cend()) {
|
||||
// 没找到或者没在CaseInfo 中匹配就使用默认的
|
||||
// 能够找到干员就使用对应的case
|
||||
if (auto itFind = info.dispatch_actions.find(it->second); itFind != info.dispatch_actions.cend()) {
|
||||
for (int i = 0; i < itFind->second.size(); ++i) {
|
||||
ret &= do_action_sync(*itFind->second[i], i);
|
||||
|
||||
if (need_exit() || !m_in_battle) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log.warn("failed to find select group");
|
||||
ret = false;
|
||||
}
|
||||
} break;
|
||||
case ActionType::Until: {
|
||||
auto& info = action.getPayload<UntilInfo>();
|
||||
|
||||
// 循环遍历携带的所有子动作,只要成功一个才退出
|
||||
// 防止死循环,添加loop_limit参数来限制循环
|
||||
action.trigger.resetCounter();
|
||||
switch (info.category) {
|
||||
case TriggerInfo::Category::Any: {
|
||||
int idx = 0;
|
||||
while (ret == false) {
|
||||
// 到达循环极限,退出
|
||||
if (action.trigger.counter == action.trigger.count) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
action.trigger.activeCounter();
|
||||
|
||||
// 只要其中一个命令执行成功就退出
|
||||
size_t i = idx++ % info.candidate.size();
|
||||
if (do_action_async(*info.candidate[i], i)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (need_exit() || !m_in_battle) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case TriggerInfo::Category::All:
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
std::set<ActionPtr> setSucc;
|
||||
int idx = 0;
|
||||
|
||||
// 全部成功则完成循环
|
||||
while (setSucc.size() != info.candidate.size()) {
|
||||
// 到达循环极限,退出
|
||||
if (action.trigger.counter == action.trigger.count) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
action.trigger.activeCounter();
|
||||
|
||||
// 判断是否已经执行成功,如果已经成功就判断下一个
|
||||
size_t i = idx++ % info.candidate.size();
|
||||
if (setSucc.find(info.candidate[i]) != setSucc.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 记录成功完成的动作
|
||||
if (do_action_async(*info.candidate[i], i)) {
|
||||
setSucc.emplace(info.candidate[i]);
|
||||
}
|
||||
|
||||
if (need_exit() || !m_in_battle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
ret = true;
|
||||
|
||||
} break;
|
||||
case ActionType::Check: {
|
||||
auto& info = action.getPayload<CheckInfo>();
|
||||
|
||||
if (check_condition(info.condition_info)) {
|
||||
// 触发器满足条件
|
||||
int i = 0;
|
||||
for (; i < info.then_actions.size(); ++i) {
|
||||
if (!do_action_sync(*info.then_actions[i], i)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (need_exit() || !m_in_battle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = (i == info.then_actions.size());
|
||||
}
|
||||
else { // 触发器不满足条件
|
||||
int i = 0;
|
||||
for (; i < info.else_actions.size(); ++i) {
|
||||
if (!do_action_sync(*info.else_actions[i], i)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (need_exit() || !m_in_battle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = (i == info.else_actions.size());
|
||||
}
|
||||
|
||||
} break;
|
||||
default:
|
||||
ret = do_derived_action(action, index);
|
||||
break;
|
||||
}
|
||||
|
||||
sleep_and_do_strategy(action.post_delay);
|
||||
sleep_and_do_strategy(action.delay.post_delay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::do_action_sync(const battle::copilot::Action& action, size_t index)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
notify_action(action);
|
||||
|
||||
thread_local auto prev_frame_time = std::chrono::steady_clock::time_point {};
|
||||
static const auto min_frame_interval =
|
||||
std::chrono::milliseconds(Config.get_options().copilot_fight_screencap_interval);
|
||||
|
||||
// prevent our program from consuming too much CPU
|
||||
if (const auto now = std::chrono::steady_clock::now(); prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
Log.debug("Sleeping for framerate limit");
|
||||
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
|
||||
}
|
||||
|
||||
// 所有被设置的触发器都满足
|
||||
switch (action.trigger.category) {
|
||||
case TriggerInfo::Category::Succ: // 默认成功,跳过条件阶段,什么都不用做
|
||||
break;
|
||||
case TriggerInfo::Category::Any: {
|
||||
if (!wait_condition_any(action)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case TriggerInfo::Category::Not: {
|
||||
if (!wait_condition_not(action)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case TriggerInfo::Category::All:
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
if (!wait_condition_all(action)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
// 部署干员还要额外等待费用够或 CD 转好
|
||||
if (action.type == ActionType::Deploy) {
|
||||
if (!wait_operator_ready(action)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prev_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
return do_action(action, index);
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::do_action_async(const battle::copilot::Action& action, size_t index)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
notify_action(action);
|
||||
|
||||
thread_local auto prev_frame_time = std::chrono::steady_clock::time_point {};
|
||||
static const auto min_frame_interval =
|
||||
std::chrono::milliseconds(Config.get_options().copilot_fight_screencap_interval);
|
||||
|
||||
// prevent our program from consuming too much CPU
|
||||
if (const auto now = std::chrono::steady_clock::now(); prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
Log.debug("Sleeping for framerate limit");
|
||||
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
|
||||
}
|
||||
|
||||
// 所有被设置的触发器都满足, 不等待
|
||||
if (check_condition(action.trigger)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 部署干员还要额外等待费用够或 CD 转好
|
||||
if (action.type == ActionType::Deploy) {
|
||||
if (!wait_operator_ready(action)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prev_frame_time = std::chrono::steady_clock::now();
|
||||
|
||||
return do_action(action, index);
|
||||
}
|
||||
|
||||
const std::string& asst::BattleProcessTask::get_name_from_group(const std::string& action_name)
|
||||
{
|
||||
auto iter = m_oper_in_group.find(action_name);
|
||||
@@ -271,116 +529,489 @@ void asst::BattleProcessTask::notify_action(const battle::copilot::Action& actio
|
||||
{ ActionType::MoveCamera, "MoveCamera" },
|
||||
{ ActionType::DrawCard, "DrawCard" },
|
||||
{ ActionType::CheckIfStartOver, "CheckIfStartOver" },
|
||||
{ ActionType::Loop, "Loop" },
|
||||
{ ActionType::Case, "Case" },
|
||||
{ ActionType::Check, "Check" },
|
||||
{ ActionType::Until, "Until" },
|
||||
};
|
||||
|
||||
json::value info = basic_info_with_what("CopilotAction");
|
||||
std::string strActionName;
|
||||
if (action.hasAvatarInfo()) {
|
||||
strActionName = action.getPayload<AvatarInfo>().name;
|
||||
}
|
||||
info["details"] |= json::object {
|
||||
{ "action", ActionNames.at(action.type) },
|
||||
{ "target", action.name },
|
||||
{ "doc", action.doc },
|
||||
{ "doc_color", action.doc_color },
|
||||
{ "target", strActionName },
|
||||
{ "doc", action.text.doc },
|
||||
{ "doc_color", action.text.doc_color },
|
||||
};
|
||||
callback(AsstMsg::SubTaskExtraInfo, info);
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::wait_condition(const Action& action)
|
||||
bool asst::BattleProcessTask::wait_operator_ready(const battle::copilot::Action& action)
|
||||
{
|
||||
cv::Mat image;
|
||||
auto update_image_if_empty = [&]() {
|
||||
if (image.empty()) {
|
||||
image = ctrler()->get_image();
|
||||
check_in_battle(image);
|
||||
auto& avatarName = action.getPayload<AvatarInfo>().name;
|
||||
const std::string& name = get_name_from_group(avatarName);
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit()) {
|
||||
if (!update_deployment(false, image)) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
auto do_strategy_and_update_image = [&]() {
|
||||
do_strategic_action(image);
|
||||
image = ctrler()->get_image();
|
||||
};
|
||||
if (auto iter = ranges::find_if(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == name; });
|
||||
iter != m_cur_deployment_opers.end() && iter->available) {
|
||||
break;
|
||||
}
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
|
||||
if (action.cost_changes != 0) {
|
||||
update_image_if_empty();
|
||||
return true;
|
||||
}
|
||||
|
||||
void asst::BattleProcessTask::update_image_if_empty(cv::Mat* _Image)
|
||||
{
|
||||
if (_Image->empty()) {
|
||||
(*_Image) = ctrler()->get_image();
|
||||
check_in_battle(*_Image);
|
||||
}
|
||||
}
|
||||
|
||||
void asst::BattleProcessTask::do_strategy_and_update_image(cv::Mat* _Image)
|
||||
{
|
||||
do_strategic_action(*_Image);
|
||||
(*_Image) = ctrler()->get_image();
|
||||
}
|
||||
|
||||
// 等待至所有被设置的条件不被满足
|
||||
bool asst::BattleProcessTask::wait_condition_not(const Action& action)
|
||||
{
|
||||
cv::Mat image;
|
||||
|
||||
// cost_changes 被指定才进入判断,且等待直至满足
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
update_image_if_empty(&image);
|
||||
update_cost(image);
|
||||
int pre_cost = m_cost;
|
||||
|
||||
while (!need_exit()) {
|
||||
update_cost(image);
|
||||
if (action.cost_changes != 0) {
|
||||
if ((pre_cost + action.cost_changes < 0) ? (m_cost <= pre_cost + action.cost_changes)
|
||||
: (m_cost >= pre_cost + action.cost_changes)) {
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
if ((pre_cost + action.trigger.cost_changes < 0) ? (m_cost <= pre_cost + action.trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + action.trigger.cost_changes)) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_kills < action.kills) {
|
||||
update_image_if_empty();
|
||||
while (!need_exit() && m_kills < action.kills) {
|
||||
if (m_kills < action.trigger.kills) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit() && m_kills < action.trigger.kills) {
|
||||
update_kills(image);
|
||||
if (m_kills >= action.kills) {
|
||||
if (m_kills >= action.trigger.kills) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image();
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.costs) {
|
||||
update_image_if_empty();
|
||||
if (action.trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit()) {
|
||||
update_cost(image);
|
||||
if (m_cost >= action.costs) {
|
||||
if (m_cost >= action.trigger.costs) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image();
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (action.cooling >= 0) {
|
||||
update_image_if_empty();
|
||||
if (action.trigger.cooling > TriggerInfo::DEACTIVE_COOLING) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit()) {
|
||||
if (!update_deployment(false, image)) {
|
||||
return false;
|
||||
}
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(action.cooling)) {
|
||||
if (cooling_count == static_cast<size_t>(action.trigger.cooling)) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
do_strategy_and_update_image();
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
// 部署干员还要额外等待费用够或 CD 转好
|
||||
if (action.type == ActionType::Deploy) {
|
||||
const std::string& name = get_name_from_group(action.name);
|
||||
update_image_if_empty();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 等待至所有被设置的条件被满足
|
||||
bool asst::BattleProcessTask::wait_condition_all(const Action& action)
|
||||
{
|
||||
cv::Mat image;
|
||||
|
||||
// cost_changes 被指定才进入判断,且等待直至满足
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
update_image_if_empty(&image);
|
||||
update_cost(image);
|
||||
int pre_cost = m_cost;
|
||||
|
||||
while (!need_exit()) {
|
||||
update_cost(image);
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
if ((pre_cost + action.trigger.cost_changes < 0) ? (m_cost <= pre_cost + action.trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + action.trigger.cost_changes)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_kills < action.trigger.kills) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit() && m_kills < action.trigger.kills) {
|
||||
update_kills(image);
|
||||
if (m_kills >= action.trigger.kills) {
|
||||
break;
|
||||
}
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
if (action.trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit()) {
|
||||
update_cost(image);
|
||||
if (m_cost >= action.trigger.costs) {
|
||||
break;
|
||||
}
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (action.trigger.cooling > TriggerInfo::DEACTIVE_COOLING) {
|
||||
update_image_if_empty(&image);
|
||||
while (!need_exit()) {
|
||||
if (!update_deployment(false, image)) {
|
||||
return false;
|
||||
}
|
||||
if (auto iter =
|
||||
ranges::find_if(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == name; });
|
||||
iter != m_cur_deployment_opers.end() && iter->available) {
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(action.trigger.cooling)) {
|
||||
break;
|
||||
}
|
||||
do_strategy_and_update_image();
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 等待至被设定的任意一个条件被满足
|
||||
bool asst::BattleProcessTask::wait_condition_any(const Action& action)
|
||||
{
|
||||
cv::Mat image;
|
||||
|
||||
// 提前准备好快照,便于后续设置判断费用差距
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
update_image_if_empty(&image);
|
||||
update_cost(image);
|
||||
}
|
||||
int pre_cost = m_cost;
|
||||
|
||||
while (!need_exit()) {
|
||||
update_image_if_empty(&image);
|
||||
|
||||
if (action.trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
update_cost(image);
|
||||
if ((pre_cost + action.trigger.cost_changes < 0) ? (m_cost <= pre_cost + action.trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + action.trigger.cost_changes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action.trigger.kills != TriggerInfo::DEACTIVE_KILLS) {
|
||||
update_kills(image);
|
||||
if (m_kills >= action.trigger.kills) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action.trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_cost(image);
|
||||
if (m_cost >= action.trigger.costs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (action.trigger.cooling != TriggerInfo::DEACTIVE_COOLING) {
|
||||
if (update_deployment(false, image)) {
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(action.trigger.cooling)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!check_in_battle(image)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do_strategy_and_update_image(&image);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::check_condition_not(const battle::copilot::TriggerInfo& _Trigger)
|
||||
{
|
||||
using TriggerInfo = battle::copilot::TriggerInfo;
|
||||
|
||||
cv::Mat image;
|
||||
|
||||
update_image_if_empty(&image);
|
||||
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
int pre_cost = m_cost;
|
||||
update_cost(image);
|
||||
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
if ((pre_cost + _Trigger.cost_changes < 0) ? (m_cost <= pre_cost + _Trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + _Trigger.cost_changes)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.kills != TriggerInfo::DEACTIVE_KILLS) {
|
||||
update_kills(image);
|
||||
if (m_kills >= _Trigger.kills) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_cost(image);
|
||||
if (m_cost >= _Trigger.costs) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (_Trigger.cooling != TriggerInfo::DEACTIVE_COOLING) {
|
||||
if (update_deployment(false, image)) {
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(_Trigger.cooling)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.count != TriggerInfo::DEACTIVE_COUNT) {
|
||||
if (_Trigger.counter == _Trigger.count) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
do_strategy_and_update_image(&image);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::check_condition_all(const battle::copilot::TriggerInfo& _Trigger)
|
||||
{
|
||||
using TriggerInfo = battle::copilot::TriggerInfo;
|
||||
|
||||
cv::Mat image;
|
||||
|
||||
update_image_if_empty(&image);
|
||||
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
int pre_cost = m_cost;
|
||||
update_cost(image);
|
||||
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
if ((pre_cost + _Trigger.cost_changes < 0) ? (m_cost <= pre_cost + _Trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + _Trigger.cost_changes)) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.kills != TriggerInfo::DEACTIVE_KILLS) {
|
||||
update_kills(image);
|
||||
if (m_kills >= _Trigger.kills) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_cost(image);
|
||||
if (m_cost >= _Trigger.costs) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (_Trigger.cooling != TriggerInfo::DEACTIVE_COOLING) {
|
||||
if (!update_deployment(false, image)) {
|
||||
return false;
|
||||
}
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(_Trigger.cooling)) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.count != TriggerInfo::DEACTIVE_COUNT) {
|
||||
if (_Trigger.counter == _Trigger.count) {
|
||||
;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
do_strategy_and_update_image(&image);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::check_condition_any(const battle::copilot::TriggerInfo& _Trigger)
|
||||
{
|
||||
using TriggerInfo = battle::copilot::TriggerInfo;
|
||||
|
||||
cv::Mat image;
|
||||
|
||||
update_image_if_empty(&image);
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
int pre_cost = m_cost;
|
||||
update_cost(image);
|
||||
|
||||
if (_Trigger.cost_changes != TriggerInfo::DEACTIVE_COST_CHANGES) {
|
||||
if ((pre_cost + _Trigger.cost_changes < 0) ? (m_cost <= pre_cost + _Trigger.cost_changes)
|
||||
: (m_cost >= pre_cost + _Trigger.cost_changes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.kills != TriggerInfo::DEACTIVE_KILLS) {
|
||||
update_kills(image);
|
||||
if (m_kills >= _Trigger.kills) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.costs != TriggerInfo::DEACTIVE_COST) {
|
||||
update_cost(image);
|
||||
if (m_cost >= _Trigger.costs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算有几个干员在cd
|
||||
if (_Trigger.cooling > TriggerInfo::DEACTIVE_COOLING) {
|
||||
if (update_deployment(false, image)) {
|
||||
size_t cooling_count =
|
||||
ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; });
|
||||
if (cooling_count == static_cast<size_t>(_Trigger.cooling)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Trigger.count != TriggerInfo::DEACTIVE_COUNT) {
|
||||
if (_Trigger.counter == _Trigger.count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
do_strategy_and_update_image(&image);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::check_condition(const battle::copilot::TriggerInfo& _Trigger)
|
||||
{
|
||||
switch (_Trigger.category) {
|
||||
case TriggerInfo::Category::Succ: // 默认成功,跳过条件阶段,什么都不用做
|
||||
break;
|
||||
case TriggerInfo::Category::Any: {
|
||||
if (check_condition_any(_Trigger)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case TriggerInfo::Category::Not: {
|
||||
if (check_condition_not(_Trigger)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case TriggerInfo::Category::All:
|
||||
[[fallthrough]];
|
||||
default: {
|
||||
if (check_condition_all(_Trigger)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asst::BattleProcessTask::enter_bullet_time(const std::string& name, const Point& location)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
@@ -8,45 +8,67 @@
|
||||
|
||||
namespace asst
|
||||
{
|
||||
class BattleProcessTask : public AbstractTask, public BattleHelper
|
||||
class BattleProcessTask : public AbstractTask, public BattleHelper
|
||||
{
|
||||
public:
|
||||
BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain);
|
||||
virtual ~BattleProcessTask() override = default;
|
||||
|
||||
virtual bool set_stage_name(const std::string& stage_name) override;
|
||||
void set_wait_until_end(bool wait_until_end);
|
||||
void set_formation_task_ptr(std::shared_ptr<std::unordered_map<std::string, std::string>> value);
|
||||
|
||||
protected:
|
||||
virtual bool _run() override;
|
||||
|
||||
virtual AbstractTask& this_task() override { return *this; }
|
||||
|
||||
virtual void clear() override;
|
||||
|
||||
virtual bool
|
||||
do_derived_action([[maybe_unused]] const battle::copilot::Action& action, [[maybe_unused]] size_t index)
|
||||
{
|
||||
public:
|
||||
BattleProcessTask(const AsstCallback& callback, Assistant* inst, std::string_view task_chain);
|
||||
virtual ~BattleProcessTask() override = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool set_stage_name(const std::string& stage_name) override;
|
||||
void set_wait_until_end(bool wait_until_end);
|
||||
void set_formation_task_ptr(std::shared_ptr<std::unordered_map<std::string, std::string>> value);
|
||||
virtual battle::copilot::CombatData& get_combat_data() { return m_combat_data; }
|
||||
|
||||
protected:
|
||||
virtual bool _run() override;
|
||||
virtual AbstractTask& this_task() override { return *this; }
|
||||
virtual void clear() override;
|
||||
virtual bool need_to_wait_until_end() const { return m_need_to_wait_until_end; }
|
||||
|
||||
virtual bool do_derived_action([[maybe_unused]] const battle::copilot::Action& action,
|
||||
[[maybe_unused]] size_t index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual battle::copilot::CombatData& get_combat_data() { return m_combat_data; }
|
||||
virtual bool need_to_wait_until_end() const { return m_need_to_wait_until_end; }
|
||||
bool to_group();
|
||||
|
||||
bool to_group();
|
||||
bool do_action(const battle::copilot::Action& action, size_t index);
|
||||
// 考虑到程序共用部分,把actions执行部分抽取出来
|
||||
bool do_action(const battle::copilot::Action& action, [[maybe_unused]] size_t index);
|
||||
|
||||
const std::string& get_name_from_group(const std::string& action_name);
|
||||
void notify_action(const battle::copilot::Action& action);
|
||||
bool wait_condition(const battle::copilot::Action& action);
|
||||
bool enter_bullet_time(const std::string& name, const Point& location);
|
||||
void sleep_and_do_strategy(unsigned millisecond);
|
||||
// 阻塞式判断action命令中的前置条件是否满足,阻塞至满足才执行后续的操作
|
||||
bool do_action_sync(const battle::copilot::Action& action, [[maybe_unused]] size_t index);
|
||||
|
||||
virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true) override;
|
||||
// 非阻塞式判断action命令中的前置条件是否满足,如果条件满足执行后续的操作,否则返回false
|
||||
bool do_action_async(const battle::copilot::Action& action, [[maybe_unused]] size_t index);
|
||||
|
||||
battle::copilot::CombatData m_combat_data;
|
||||
std::unordered_map</*group*/ std::string, /*oper*/ std::string> m_oper_in_group;
|
||||
const std::string& get_name_from_group(const std::string& action_name);
|
||||
void notify_action(const battle::copilot::Action& action);
|
||||
bool wait_operator_ready(const battle::copilot::Action& action);
|
||||
void update_image_if_empty(cv::Mat* _Image);
|
||||
void do_strategy_and_update_image(cv::Mat* _Image);
|
||||
bool wait_condition_not(const battle::copilot::Action& action); // 等待所有条件都不满足
|
||||
bool wait_condition_all(const battle::copilot::Action& action); // 等待所有条件都满足
|
||||
bool wait_condition_any(const battle::copilot::Action& action); // 等待某个条件都满足
|
||||
bool check_condition_not(const battle::copilot::TriggerInfo& action); // 所有条件都不满足
|
||||
bool check_condition_all(const battle::copilot::TriggerInfo& action); // 所有条件都满足
|
||||
bool check_condition_any(const battle::copilot::TriggerInfo& action); // 某个条件都满足
|
||||
bool check_condition(const battle::copilot::TriggerInfo& action);
|
||||
|
||||
bool m_in_bullet_time = false;
|
||||
bool m_need_to_wait_until_end = false;
|
||||
std::shared_ptr<std::unordered_map<std::string, std::string>> m_formation_ptr = nullptr;
|
||||
};
|
||||
bool enter_bullet_time(const std::string& name, const Point& location);
|
||||
void sleep_and_do_strategy(unsigned millisecond);
|
||||
|
||||
virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true) override;
|
||||
|
||||
battle::copilot::CombatData m_combat_data;
|
||||
std::unordered_map</*group*/ std::string, /*oper*/ std::string> m_oper_in_group;
|
||||
|
||||
bool m_in_bullet_time = false;
|
||||
bool m_need_to_wait_until_end = false;
|
||||
std::shared_ptr<std::unordered_map<std::string, std::string>> m_formation_ptr = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@ bool asst::SSSBattleProcessTask::set_stage_name(const std::string& stage_name)
|
||||
return false;
|
||||
}
|
||||
m_sss_combat_data = SSSCopilot.get_data(stage_name);
|
||||
ranges::transform(m_sss_combat_data.strategies, std::inserter(m_all_cores, m_all_cores.begin()),
|
||||
[](const auto& strategy) { return strategy.core; });
|
||||
ranges::transform(
|
||||
m_sss_combat_data.strategies,
|
||||
std::inserter(m_all_cores, m_all_cores.begin()),
|
||||
[](const auto& strategy) { return strategy.core; });
|
||||
for (const auto& action : m_sss_combat_data.actions) {
|
||||
if (action.type == battle::copilot::ActionType::Deploy) {
|
||||
m_all_action_opers.emplace(action.name);
|
||||
auto& info = action.getPayload<battle::copilot::AvatarInfo>();
|
||||
m_all_action_opers.emplace(info.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +61,8 @@ bool asst::SSSBattleProcessTask::update_deployment_with_skip(const cv::Mat& reus
|
||||
}
|
||||
|
||||
if (ranges::equal(
|
||||
m_cur_deployment_opers, old_deployment_opers,
|
||||
m_cur_deployment_opers,
|
||||
old_deployment_opers,
|
||||
[](const DeploymentOper& oper1, const DeploymentOper& oper2) { return oper1.name == oper2.name; })) {
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_same_time).count() > 30000) {
|
||||
// 30s 能回 60 费,基本上已经到了挂机的时候,放缓检查的速度
|
||||
@@ -100,8 +104,7 @@ bool asst::SSSBattleProcessTask::do_strategic_action(const cv::Mat& reusable)
|
||||
static const auto min_frame_interval = std::chrono::milliseconds(Config.get_options().sss_fight_screencap_interval);
|
||||
|
||||
// prevent our program from consuming too much CPU
|
||||
if (const auto now = std::chrono::steady_clock::now();
|
||||
prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
if (const auto now = std::chrono::steady_clock::now(); prev_frame_time > now - min_frame_interval) [[unlikely]] {
|
||||
Log.debug("Sleeping for framerate limit");
|
||||
std::this_thread::sleep_for(min_frame_interval - (now - prev_frame_time));
|
||||
}
|
||||
@@ -145,8 +148,9 @@ bool asst::SSSBattleProcessTask::wait_until_start(bool weak)
|
||||
}
|
||||
else {
|
||||
replace_count = 4;
|
||||
if (ranges::count_if(m_cur_deployment_opers,
|
||||
[](const auto& oper) { return oper.role == Role::Pioneer; }) /* 先锋数量 */
|
||||
if (ranges::count_if(
|
||||
m_cur_deployment_opers,
|
||||
[](const auto& oper) { return oper.role == Role::Pioneer; }) /* 先锋数量 */
|
||||
< 2) {
|
||||
cost_limit = 25; // 先锋低于2个时,降低费用阈值,以试图换出先锋
|
||||
}
|
||||
@@ -219,8 +223,9 @@ bool asst::SSSBattleProcessTask::check_and_do_strategy(const cv::Mat& reusable)
|
||||
Role role_for_lambda = role;
|
||||
|
||||
// 如果有可用的干员,直接使用
|
||||
auto available_iter = ranges::find_if(
|
||||
tool_men, [&](const DeploymentOper& oper) { return oper.available && oper.role == role_for_lambda; });
|
||||
auto available_iter = ranges::find_if(tool_men, [&](const DeploymentOper& oper) {
|
||||
return oper.available && oper.role == role_for_lambda;
|
||||
});
|
||||
if (available_iter != tool_men.cend()) {
|
||||
--quantity;
|
||||
// 部署完,画面会发生变化,所以直接返回,后续逻辑交给下次循环处理
|
||||
@@ -252,18 +257,19 @@ bool asst::SSSBattleProcessTask::check_if_start_over(const battle::copilot::Acti
|
||||
update_deployment();
|
||||
|
||||
bool to_abandon = false;
|
||||
auto& info = action.getPayload<battle::copilot::CheckIfStartOverInfo>();
|
||||
|
||||
if (!action.name.empty() &&
|
||||
!ranges::any_of(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == action.name; }) &&
|
||||
!m_battlefield_opers.contains(action.name)) {
|
||||
if (!info.name.empty() &&
|
||||
!ranges::any_of(m_cur_deployment_opers, [&](const auto& oper) { return oper.name == info.name; }) &&
|
||||
!m_battlefield_opers.contains(info.name)) {
|
||||
to_abandon = true;
|
||||
}
|
||||
else if (!action.role_counts.empty()) {
|
||||
else if (!info.role_counts.empty()) {
|
||||
std::unordered_map<Role, size_t> cur_counts;
|
||||
for (const auto& oper : m_cur_deployment_opers) {
|
||||
cur_counts[oper.role] += 1;
|
||||
}
|
||||
for (const auto& [role, number] : action.role_counts) {
|
||||
for (const auto& [role, number] : info.role_counts) {
|
||||
if (cur_counts[role] < static_cast<size_t>(number)) {
|
||||
to_abandon = true;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user