Files
MaaAssistantArknights/src/MaaCore/Common/AsstBattleDef.h
2026-02-06 14:57:04 +08:00

608 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include "AsstTypes.h"
#include "MaaUtils/NoWarningCVMat.hpp"
namespace asst::battle
{
// 统一变量名:
// loc, location, 表示格子坐标,例如 [1, 1], [5, 5]
// pos, position, 表示像素坐标,例如 [1280, 720], [500, 300]
enum class SkillUsage // 技能用法
{
NotUse = 0, // 不自动使用
Possibly = 1, // 有就用,例如干员 棘刺 3 技能
Times = 2, // 用 X 次,例如干员 山 2 技能用 1 次、重岳 3 技能用 5 次,由 "skill_times" 字段控制
InTime = 3, // 自动判断使用时机,画饼.jpg
TimesUsed // 已经使用了 X 次
};
// 干员练度需求
struct OperatorRequirements
{
int elite = -1; // 精英化等级
int level = -1; // 干员等级
int skill_level = -1; // 技能等级
int module = -1; // 模组编号 -1: 不切换模组 / 无要求, 0: 不使用模组, 1: 模组χ, 2: 模组γ, 3: 模组α, 4: 模组Δ
// int potentiality = -1; // 潜能要求
bool operator==(const OperatorRequirements& req) const
{
return elite == req.elite && level == req.level && skill_level == req.skill_level && module == req.module;
}
};
// 干员编队状态
enum class OperStatus
{
Unchecked, // 未检查, 默认值; 理论上仅group中有干员选中后, 其余干员才会保留该状态
Selected, // 已编入
Missing, // 缺失
Unavailable, // 不可用, 要求不达标
// Unknown, // 未知状态
};
struct OperUsage // 干员用法
{
std::string name;
int skill = 0; // 技能序号,取值范围 [0, 3]0时使用默认技能 或 上次编队时使用的技能
SkillUsage skill_usage = SkillUsage::NotUse;
int skill_times = 1; // 使用技能的次数,默认为 1兼容曾经的作业
battle::OperatorRequirements requirements {}; // 练度需求
OperStatus status = OperStatus::Unchecked; // 编队状态, 可能有其他更好的位置存储
bool operator==(const OperUsage& other) const
{
return name == other.name && skill == other.skill && skill_usage == other.skill_usage &&
skill_times == other.skill_times && requirements == other.requirements;
}
};
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)
{
// clang-format off
static const std::unordered_map<std::string, Role> NameToRole = {
{ "WARRIOR", Role::Warrior }, { "Warrior", Role::Warrior }, { "warrior", Role::Warrior },
{ "GUARD", Role::Warrior }, { "Guard", Role::Warrior }, { "guard", Role::Warrior },
{ "近卫", Role::Warrior },
{ "PIONEER", Role::Pioneer }, { "Pioneer", Role::Pioneer }, { "pioneer", Role::Pioneer },
{ "VANGUARD", Role::Pioneer }, { "Vanguard", Role::Pioneer }, { "vanguard", Role::Pioneer },
{ "先锋", Role::Pioneer },
{ "MEDIC", Role::Medic }, { "Medic", Role::Medic }, { "medic", Role::Medic },
{ "医疗", Role::Medic },
{ "TANK", Role::Tank }, { "Tank", Role::Tank }, { "tank", Role::Tank },
{ "DEFENDER", Role::Tank }, { "Defender", Role::Tank }, { "defender", Role::Tank },
{ "重装", 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 },
{ "SPECIALIST", Role::Special }, { "Specialist", Role::Special }, { "specialist", Role::Special },
{ "特种", Role::Special },
{ "DRONE", Role::Drone }, { "Drone", Role::Drone }, { "drone", Role::Drone },
{ "SUMMON", Role::Drone }, { "Summon", Role::Drone }, { "summon", Role::Drone },
{ "无人机", Role::Drone }, { "召唤物", Role::Drone },
}; // clang-format on
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;
}
}
// ————————————————————————————————————————————————————————————————
// 招募相关
// ————————————————————————————————————————————————————————————————
/// <summary>
/// 编队/招募时对所需干员模组的要求。
/// </summary>
enum class OperModule
{
/// <summary>
/// 无指定模组
/// </summary>
Unspecified = -1,
/// <summary>
/// 基础模组/无模组。
/// </summary>
Original = 0,
/// <summary>
/// Chi 模组。
/// </summary>
Chi,
/// <summary>
/// Upsilon 模组。
/// </summary>
Upsilon,
/// <summary>
/// Delta 模组。
/// </summary>
Delta,
/// <summary>
/// Alpha 模组。
/// </summary>
Alpha,
/// <summary>
/// Beta 模组。
/// </summary>
Beta,
};
/// <summary>
/// 编队/招募需要的干员。
/// </summary>
struct RequiredOper
{
/// <summary>
/// 所需干员职业。
/// </summary>
Role role = Role::Unknown;
/// <summary>
/// 所需干员名称。
/// </summary>
std::string name;
/// <summary>
/// 所需干员最低精英阶段,当且仅当 <c>level != 0</c> 时有效。
/// 为 02 的整数,分别表示精英阶段 12。
/// </summary>
int elite = 0;
/// <summary>
/// 所需干员最低等级。
/// 精英阶段高于 <c>elite</c> 的干员不受此要求限制。
/// 为 090 的整数,其中 0 表示无要求190 分别表示 190 级。
/// </summary>
int level = 0;
/// <summary>
/// 所需干员携带技能。为 03 的整数,其中 0 表示无需指定技能13 分别表示一、二、三技能。
/// </summary>
int skill = 0;
/// <summary>
/// 所需干员最低技能等级。
/// 仅在 <c>RequiredOper::skill != 0</c> 时有效。
/// 为 010 的整数,其中 0 表示无要求17 分别表示 17 级810 分别表示专精等级 13 级。
/// </summary>
int skill_level = 0;
/// <summary>
/// 所需干员携带模组。
/// </summary>
OperModule module = OperModule::Unspecified;
/// <summary>
/// 所需干员携带模组的最低等级。
/// 仅在 <c>module</c> 不为 <c>OperModule::Unspecified</c> 或 <c>OperModule::Original</c> 时有效。
/// 为 03 的整数,其中 0 表示无要求13 分别表示 13 级。
/// </summary>
int module_level = 0;
/// <summary>
/// 所需干员最低潜能。
/// 为 06 的整数,其中 0 表示无要求16 分别表示 16 潜。
/// </summary>
int potential = 0;
};
/// <summary>
/// 好友关系。
/// </summary>
enum class Friendship
{
/// <summary>
/// 陌生人。
/// </summary>
Stranger = 0,
/// <summary>
/// 好友。
/// </summary>
Friend,
/// <summary>
/// 挚友。
/// </summary>
BestFriend,
};
/// <summary>
/// 备选助战干员。
/// </summary>
struct SupportUnit
{
cv::Mat templ;
/// <summary>
/// 助战干员名称。
/// </summary>
std::string name;
/// <summary>
/// 助战干员精英化阶段。
/// </summary>
int elite = 0;
/// <summary>
/// 助战干员等级。
/// </summary>
int level = 0;
/// <summary>
/// 助战干员潜能。
/// </summary>
int potential = 0;
/// <summary>
/// 是否可以选择模组。
/// </summary>
bool module_enabled = false;
/// <summary>
/// 与助战干员提供者之间的亲密度。
/// </summary>
Friendship friendship = Friendship::Stranger;
// ———————— 以下字段仅在集成战略中有效 ————————
// int hope = 0; // 希望消耗
// int elite_after_promotion = 0; // 进阶后精英化阶段,仅在集成战略中有效,
// int level_after_promotion = 0; // 进阶后等级,仅在集成战略中有效,
};
/// <summary>
/// 根据 <c>role</c> 对干员名 <c>literal_name</c> 进行消歧义,目前仅用于区分不同升变形态下的阿米娅。
/// </summary>
inline static std::string canonical_oper_name(battle::Role role, const std::string& literal_name)
{
using battle::Role;
static const std::unordered_map<std::pair<Role, std::string>, std::string, std::pair_hash<Role, std::string>>
CanonicalOperNameDict {
{ { Role::Caster, "阿米娅" }, "阿米娅" },
{ { Role::Warrior, "阿米娅" }, "阿米娅-WARRIOR" },
{ { Role::Medic, "阿米娅" }, "阿米娅-MEDIC" },
};
if (const auto iter = CanonicalOperNameDict.find({ role, literal_name }); iter != CanonicalOperNameDict.end()) {
return iter->second;
}
return literal_name;
}
// ————————————————————————————————————————————————————————————————
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_usual_location = false; // 用于判断地面辅助高台先锋unusual 时此值为 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; // 稀有度 1-6
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 OperUsageGroup = std::pair<std::string, std::vector<OperUsage>>;
using OperUsageGroups = std::vector<OperUsageGroup>;
enum class ActionType
{
Deploy, // 部署干员
UseSkill, // 开技能
Retreat, // 撤退干员
SkillUsage, // 技能用法
SwitchSpeed, // 切换二倍速
BulletTime, // 使用 1/5 的速度
Output, // 仅输出,什么都不操作,界面上也不显示
SkillDaemon, // 什么都不做,有技能开技能,直到战斗结束
ResetStopwatch, // 重置全局计时器 (试验性功能)
/* for TRN */
MoveCamera, // 引航者试炼,移动镜头
/* for SSS */
DrawCard, // “调配干员”
CheckIfStartOver, // 检查如果没有某干员则退出重开
};
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;
bool skip_if_not_ready = false; // 跳过使用未准备好的技能,主要用于关闭技能的场景 (试验性功能)
int elapsed_time = 0; // 全局计时条件 (试验性功能)
};
struct BasicInfo
{
std::string stage_name;
std::string minimum_required;
std::string title;
std::string title_color;
std::string details;
std::string details_color;
};
struct CombatData // 作业 JSON 数据
{
BasicInfo info;
OperUsageGroups groups;
std::vector<Action> actions;
};
} // namespace copilot
namespace sss // 保全派驻
{
struct Strategy
{
std::optional<std::string> core;
RoleCounts tool_men; // 初始需要多少工具人
Point location;
DeployDirection direction = DeployDirection::None;
bool all_deployed = false; // 当前 strategy 是否已经完成
};
struct CombatData : public copilot::CombatData
{
std::vector</*const*/ Strategy> strategies; // 按顺序存储的 strategies
bool draw_as_possible = false;
int retry_times = 0;
std::vector<std::string> order_of_drops;
};
enum class EquipmentType
{
NotChoose,
A,
B,
};
struct CompleteData
{
copilot::BasicInfo info;
std::string buff;
std::vector<EquipmentType> equipment;
std::string strategy;
copilot::OperUsageGroups groups;
RoleCounts tool_men;
std::vector<std::string> order_of_drops;
std::unordered_set<std::string> blacklist;
std::unordered_map<std::string, CombatData> stages_data;
};
}
namespace roguelike
{
struct ReplacementHome
{
Point location;
DeployDirection direction = DeployDirection::Right;
};
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
} // namespace asst::battle
namespace asst
{
inline std::string enum_to_string(asst::battle::Role role, bool en = false)
{
using asst::battle::Role;
static const std::unordered_map<Role, std::pair<std::string, std::string>> RoleToName {
{ Role::Warrior, { "近卫", "Warrior" } }, { Role::Pioneer, { "先锋", "Pioneer" } },
{ Role::Medic, { "医疗", "Medic" } }, { Role::Tank, { "重装", "Tank" } },
{ Role::Sniper, { "狙击", "Sniper" } }, { Role::Caster, { "术师", "Caster" } },
{ Role::Support, { "辅助", "Support" } }, { Role::Special, { "特种", "Special" } },
{ Role::Drone, { "无人机", "Drone" } },
};
if (auto iter = RoleToName.find(role); iter != RoleToName.end()) {
return en ? iter->second.second : iter->second.first;
}
return "Unknown";
}
inline std::string enum_to_string(const battle::OperModule module)
{
using OperModule = battle::OperModule;
static const std::unordered_map<OperModule, std::string> OPER_MODULE_STR_MAP {
{ OperModule::Unspecified, "Unspecified" },
{ OperModule::Original, "Original" },
{ OperModule::Chi, "Chi" },
{ OperModule::Upsilon, "Upsilon" },
{ OperModule::Delta, "Delta" },
{ OperModule::Alpha, "Alpha" },
{ OperModule::Beta, "Beta" },
};
if (const auto iter = OPER_MODULE_STR_MAP.find(module); iter != OPER_MODULE_STR_MAP.end()) {
return iter->second;
}
return "Unknown";
}
} // namespace asst