From a930afe94903b1bd39bd3e798f820e2bc47f3f30 Mon Sep 17 00:00:00 2001 From: valencia_fly Date: Sun, 28 Jul 2024 16:45:27 +0800 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20=E4=BD=9C=E4=B8=9A=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=9D=97=E5=A2=9E=E5=BC=BA=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A7=A6=E5=8F=91=E6=9D=A1=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E7=AD=96=E7=95=A5=E5=8F=98=E6=9B=B4=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E5=91=BD=E4=BB=A4=EF=BC=8C=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=EF=BC=8C=E6=B4=BE=E5=8F=91=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E6=97=A0=E5=BA=8F=E5=91=BD=E4=BB=A4=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 771 +++++++++------- .../Config/Miscellaneous/CopilotConfig.cpp | 409 ++++++--- .../Config/Miscellaneous/CopilotConfig.h | 43 +- src/MaaCore/Task/BattleHelper.h | 145 +-- .../Task/Miscellaneous/BattleProcessTask.cpp | 825 ++++++++++++++++-- .../Task/Miscellaneous/BattleProcessTask.h | 86 +- src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp | 36 +- 7 files changed, 1677 insertions(+), 638 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index e10939ff71..b87785edd8 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -1,7 +1,10 @@ #pragma once +#include +#include #include #include +#include #include #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 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 ranges; + int rarity = 0; + LocationType location_type = LocationType::None; + std::vector tokens; // 召唤物名字 +}; + +using AttackRange = std::vector; +using RoleCounts = std::unordered_map; + +namespace copilot +{ +using OperUsageGroups = std::unordered_map>; + +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; + +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 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> dispatch_actions; // 对应干员的动作 + std::vector default_action; // 默认的动作 +}; + +// 定义loop操作需要的信息 +struct LoopInfo +{ + TriggerInfo end_info; + TriggerInfo continue_info; + TriggerInfo break_info; + + int counter = 0; // 计数器 + + std::vector loop_actions; +}; + +// 定义check操作需要的信息 +struct CheckInfo +{ + TriggerInfo condition_info; + std::vector then_actions; // 当条件满足时执行 + std::vector else_actions; // 当条件不满足时执行 +}; + +// 定义until操作需要的信息 +struct UntilInfo +{ + TriggerInfo::Category category; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束 + std::vector candidate; // 备用的命令序列 +}; + +struct CheckIfStartOverInfo +{ + std::string name; + RoleCounts role_counts; // 角色统计 + + CheckIfStartOverInfo() = default; + + explicit CheckIfStartOverInfo(RoleCounts&& _Counts) : + role_counts(std::move(_Counts)) + { + } +}; + +struct MoveCameraInfo +{ + std::pair 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(payload); } + + // 读取载荷 + template + auto getPayload() const noexcept -> PayLoad_T const& + { + return std::get(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 ranges; - int rarity = 0; - LocationType location_type = LocationType::None; - std::vector tokens; // 召唤物名字 - }; + // 创建堆对象,由智能指针管理内存 + static auto create() -> ActionPtr { return std::make_shared(); } +}; - using AttackRange = std::vector; - using RoleCounts = std::unordered_map; +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>; +struct CombatData // 作业 JSON 数据 +{ + BasicInfo info; + OperUsageGroups groups; + std::vector 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 strategies; + bool draw_as_possible = false; + int retry_times = 0; + std::vector 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 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 equipment; + std::string strategy; - struct CombatData // 作业 JSON 数据 - { - BasicInfo info; - OperUsageGroups groups; - std::vector actions; - }; - } // namespace copilot + copilot::OperUsageGroups groups; + RoleCounts tool_men; + std::vector order_of_drops; + std::unordered_set blacklist; - namespace sss // 保全派驻 - { - struct Strategy - { - std::string core; - RoleCounts tool_men; - Point location; - DeployDirection direction = DeployDirection::None; - }; + std::unordered_map stages_data; +}; +} - struct CombatData : public copilot::CombatData - { - std::vector strategies; - bool draw_as_possible = false; - int retry_times = 0; - std::vector 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 = {}; +}; - std::string buff; - std::vector equipment; - std::string strategy; +struct CombatData +{ + std::string stage_name; + std::vector replacement_home; + std::unordered_set blacklist_location; + std::unordered_map force_deploy_direction; + std::array 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> deploy_plan; + std::vector retreat_plan; +}; - copilot::OperUsageGroups groups; - RoleCounts tool_men; - std::vector order_of_drops; - std::unordered_set blacklist; +struct Recruitment +{ + std::string name; + Rect rect; + int elite = 0; + int level = 0; +}; - std::unordered_map 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 = {}; - }; - - struct CombatData - { - std::string stage_name; - std::vector replacement_home; - std::unordered_set blacklist_location; - std::unordered_map force_deploy_direction; - std::array 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> deploy_plan; - std::vector 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 diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index ac8a671ec1..12a2e3a077 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -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 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("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(); + 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(action_info.get("skill_usage", 0)); + avatar.modify_times = action_info.get("skill_times", 1); + } break; + case ActionType::CheckIfStartOver: { + auto& info = action.payload.emplace(); + + 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(std::make_pair(dist_arr[0].as_double(), dist_arr[1].as_double())); + } break; + case ActionType::Loop: { + auto& loop = action.payload.emplace(); + + // 必选字段 + 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(); + + // 必选字段 + 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(); + + // 必选字段 + 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(); + + // 必选字段 + 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::CopilotConfig::parse_actions(const json::value& json) { LogTraceFunction; @@ -120,115 +402,10 @@ std::vector asst::CopilotConfig::parse_actions(co std::vector actions_list; for (const auto& action_info : json.at("actions").as_array()) { - Action action; - static const std::unordered_map 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(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("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::CopilotConfig::parse_actions(co return actions_list; } +std::vector asst::CopilotConfig::parse_actions_ptr(const json::value& json) +{ + LogTraceFunction; + + std::vector actions_list; + + for (const auto& action_info : json.as_array()) { + battle::copilot::ActionPtr action = std::make_shared(); + 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; diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.h b/src/MaaCore/Config/Miscellaneous/CopilotConfig.h index 2ea9d92971..244b22df37 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.h +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.h @@ -4,28 +4,33 @@ namespace asst { - class CopilotConfig : public SingletonHolder, 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 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, 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 parse_actions_ptr(const json::value& json); + static std::vector 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(); } diff --git a/src/MaaCore/Task/BattleHelper.h b/src/MaaCore/Task/BattleHelper.h index f597e30245..989baca17d 100644 --- a/src/MaaCore/Task/BattleHelper.h +++ b/src/MaaCore/Task/BattleHelper.h @@ -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& 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 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& delta); - std::string m_stage_name; - Map::Level m_map_data; - std::unordered_map m_side_tile_info; // 子弹时间的坐标映射 - std::unordered_map m_normal_tile_info; // 正常的坐标映射 - Point m_skill_button_pos; - Point m_retreat_button_pos; - std::unordered_map m_skill_usage; - std::unordered_map m_skill_times; - std::unordered_map m_skill_error_count; - std::unordered_map m_last_use_skill_time; - int m_camera_count = 0; - std::pair 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 get_oper_rect_on_deployment(const std::string& name) const; - std::vector m_cur_deployment_opers; + std::string m_stage_name; + Map::Level m_map_data; + std::unordered_map m_side_tile_info; // 子弹时间的坐标映射 + std::unordered_map m_normal_tile_info; // 正常的坐标映射 + Point m_skill_button_pos; + Point m_retreat_button_pos; + std::unordered_map m_skill_usage; + std::unordered_map m_skill_times; + std::unordered_map m_skill_error_count; + std::unordered_map m_last_use_skill_time; + int m_camera_count = 0; + std::pair m_camera_shift = { 0., 0. }; - std::map m_battlefield_opers; - std::map 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 m_cur_deployment_opers; + + std::map m_battlefield_opers; + std::map m_used_tiles; + +private: + InstHelper m_inst_helper; +}; } // namespace asst diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index 1da91bec52..3efefa90f2 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -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> value) +void asst::BattleProcessTask::set_formation_task_ptr( + std::shared_ptr> 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(); + 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(); + + 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(); + + 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(); + + 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(); + + 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(); - 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(); - 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(); + + // 假设被设置了自然数才赋值 + 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(); + + 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(); + + // 循环遍历携带的所有子动作,只要成功一个才退出 + // 防止死循环,添加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 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(); + + 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().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().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(action.cooling)) { + if (cooling_count == static_cast(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(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(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(_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(_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(_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; diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h index dc363ee4de..c2b3513223 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h @@ -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> 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> 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 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> 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 m_oper_in_group; + + bool m_in_bullet_time = false; + bool m_need_to_wait_until_end = false; + std::shared_ptr> m_formation_ptr = nullptr; +}; } diff --git a/src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp b/src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp index d09fd373c8..03f56c856f 100644 --- a/src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp +++ b/src/MaaCore/Task/SSS/SSSBattleProcessTask.cpp @@ -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(); + 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(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(); - 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 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(number)) { to_abandon = true; break; From 6035f67aad13c1654ba6c904884ca9a32651aed4 Mon Sep 17 00:00:00 2001 From: valencia_fly Date: Sun, 28 Jul 2024 17:34:23 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8?= =?UTF-8?q?=E8=A7=A3=E6=9E=90until=E5=91=BD=E4=BB=A4=E5=9D=97=E6=97=B6?= =?UTF-8?q?=EF=BC=8Ccategory=E5=AD=97=E6=AE=B5=E6=98=AF=E5=86=B2=E7=AA=81?= =?UTF-8?q?=E7=9A=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index 12a2e3a077..fe43fc2afa 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -361,11 +361,10 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat auto& until = action.payload.emplace(); // 必选字段 - until.category = TriggerInfo::loadCategoryFrom(action_info.at("category").as_string()); + until.category = TriggerInfo::loadCategoryFrom(action_info.at("mode").as_string()); switch (until.category) { case TriggerInfo::Category::Any: case TriggerInfo::Category::All: - case TriggerInfo::Category::Succ: break; default: until.category = TriggerInfo::Category::All; From 5ccaab33026a26d68d9c1b4df370d4c6fbf888ef Mon Sep 17 00:00:00 2001 From: valencia_fly Date: Sun, 28 Jul 2024 21:03:22 +0800 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=9C=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E5=9D=97=E4=B8=AD=E5=8A=A0=E5=85=A5=E9=94=9A?= =?UTF-8?q?=E7=82=B9=EF=BC=8C=E5=B9=B6=E4=B8=94=E6=94=AF=E6=8C=81=E9=94=9A?= =?UTF-8?q?=E7=82=B9=E7=9B=B8=E5=85=B3=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 29 +- .../Config/Miscellaneous/CopilotConfig.cpp | 77 ++++- src/MaaCore/Task/BattleHelper.h | 1 + .../Task/Miscellaneous/BattleProcessTask.cpp | 273 +++++++++++++++++- .../Task/Miscellaneous/BattleProcessTask.h | 10 + 5 files changed, 378 insertions(+), 12 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index b87785edd8..af256c0652 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -220,11 +221,13 @@ struct TriggerInfo }; 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; // 计数器 // 触发器是否被激活 @@ -314,10 +317,31 @@ struct CheckInfo // 定义until操作需要的信息 struct UntilInfo { - TriggerInfo::Category category; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束 + TriggerInfo::Category mode; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束 std::vector candidate; // 备用的命令序列 }; +// 定义point操作需要的信息 +struct PointInfo +{ + struct SnapShot + { + int kills = TriggerInfo::DEACTIVE_KILLS; + int cost = TriggerInfo::DEACTIVE_COST; + int64_t cooling_count = TriggerInfo::DEACTIVE_COOLING; + int interval = 0; + + std::chrono::steady_clock::time_point tNow; + }; + + std::string target_code; + TriggerInfo::Category mode; + std::pair range; + + std::vector then_actions; // 当条件满足时执行 + std::vector else_actions; // 当条件不满足时执行 +}; + struct CheckIfStartOverInfo { std::string name; @@ -363,7 +387,8 @@ struct Action CaseInfo, LoopInfo, CheckInfo, - UntilInfo> + UntilInfo, + PointInfo> payload; // 信息载荷 // 是否携带干员信息 diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index fe43fc2afa..74542f7580 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -224,6 +224,22 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat { "until", ActionType::Until }, { "UNTIL", ActionType::Until }, { "直到", ActionType::Until }, + + { "SavePoint", ActionType::SavePoint }, + { "savepoint", ActionType::SavePoint }, + { "SAVEPOINT", ActionType::SavePoint }, + { "锚点", ActionType::SavePoint }, + { "保存锚点", ActionType::SavePoint }, + + { "SyncPoint", ActionType::SyncPoint }, + { "syncpoint", ActionType::SyncPoint }, + { "SYNCPOINT", ActionType::SyncPoint }, + { "同步锚点", ActionType::SyncPoint }, + + { "CheckPoint", ActionType::CheckPoint }, + { "checkpoint", ActionType::CheckPoint }, + { "CHECKPOINT", ActionType::CheckPoint }, + { "检查锚点", ActionType::CheckPoint }, }; auto& action = (*_Out); @@ -361,13 +377,13 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat auto& until = action.payload.emplace(); // 必选字段 - until.category = TriggerInfo::loadCategoryFrom(action_info.at("mode").as_string()); - switch (until.category) { + until.mode = TriggerInfo::loadCategoryFrom(action_info.at("mode").as_string()); + switch (until.mode) { case TriggerInfo::Category::Any: case TriggerInfo::Category::All: break; default: - until.category = TriggerInfo::Category::All; + until.mode = TriggerInfo::Category::All; break; } @@ -379,13 +395,64 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat until.candidate = parse_actions_ptr(t.value()); } } break; + case ActionType::SyncPoint: + case ActionType::CheckPoint: { + auto& point = action.payload.emplace(); + + point.target_code = action_info.at("target_code").as_string(); + + point.mode = TriggerInfo::loadCategoryFrom(action_info.at("mode").as_string()); + switch (point.mode) { + case TriggerInfo::Category::Any: + case TriggerInfo::Category::All: + case TriggerInfo::Category::Not: + case TriggerInfo::Category::Succ: + break; + default: + point.mode = TriggerInfo::Category::All; + break; + } + + if (auto t = action_info.find("kill_range")) { + auto range = t.value().as_array(); + point.range.first.kills = range[0].as_integer(); + point.range.second.kills = range[1].as_integer(); + } + + if (auto t = action_info.find("cost_range")) { + auto range = t.value().as_array(); + point.range.first.cost = range[0].as_integer(); + point.range.second.cost = range[1].as_integer(); + } + + if (auto t = action_info.find("cooling_range")) { + auto range = t.value().as_array(); + point.range.first.cooling_count = range[0].as_integer(); + point.range.second.cooling_count = range[1].as_integer(); + } + + if (auto t = action_info.find("time_range")) { + auto range = t.value().as_array(); + point.range.first.interval = range[0].as_integer(); + point.range.second.interval = range[1].as_integer(); + } + + if (auto t = action_info.find("then_actions")) { + point.then_actions = parse_actions_ptr(t.value()); + } + + if (action.type == ActionType::CheckPoint) { + if (auto t = action_info.find("else_actions")) { + point.else_actions = 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; diff --git a/src/MaaCore/Task/BattleHelper.h b/src/MaaCore/Task/BattleHelper.h index 989baca17d..faa11ab878 100644 --- a/src/MaaCore/Task/BattleHelper.h +++ b/src/MaaCore/Task/BattleHelper.h @@ -96,6 +96,7 @@ protected: int m_total_kills = 0; int m_kills = 0; int m_cost = 0; + int m_cooling_count = 0; std::vector m_cur_deployment_opers; diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index 3efefa90f2..a81b26e6f8 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -178,6 +178,9 @@ bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, s update_deployment(); } + // 如果携带锚点编码,则存储快照 + save_snap_shot(action.point_code); + bool ret = false; switch (action.type) { @@ -315,7 +318,7 @@ END_LOOP:; // 循环遍历携带的所有子动作,只要成功一个才退出 // 防止死循环,添加loop_limit参数来限制循环 action.trigger.resetCounter(); - switch (info.category) { + switch (info.mode) { case TriggerInfo::Category::Any: { int idx = 0; while (ret == false) { @@ -411,6 +414,75 @@ END_LOOP:; } } break; + case ActionType::SavePoint: + ret = true; // 锚点保存动作由于携带锚点编码,已经在前面进行存储 + break; + case ActionType::SyncPoint: { + auto& info = action.getPayload(); + + 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); + + do { + const auto now = std::chrono::steady_clock::now(); + + if (check_point(info, now)) { + break; + } + + // prevent our program from consuming too much CPU + if (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)); + } + } while (!need_exit() && m_in_battle); + + 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()); + } break; + case ActionType::CheckPoint: { + auto& info = action.getPayload(); + + if (check_point(info, std::chrono::steady_clock::now())) { + 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.then_actions.size()); + } + } break; default: ret = do_derived_action(action, index); break; @@ -489,7 +561,7 @@ bool asst::BattleProcessTask::do_action_async(const battle::copilot::Action& act } // 所有被设置的触发器都满足, 不等待 - if (check_condition(action.trigger)) { + if (!check_condition(action.trigger)) { return false; } @@ -533,6 +605,9 @@ void asst::BattleProcessTask::notify_action(const battle::copilot::Action& actio { ActionType::Case, "Case" }, { ActionType::Check, "Check" }, { ActionType::Until, "Until" }, + { ActionType::SavePoint, "SavePoint" }, + { ActionType::SyncPoint, "SyncPoint" }, + { ActionType::CheckPoint, "CheckPoint" }, }; json::value info = basic_info_with_what("CopilotAction"); @@ -991,19 +1066,19 @@ bool asst::BattleProcessTask::check_condition(const battle::copilot::TriggerInfo case TriggerInfo::Category::Succ: // 默认成功,跳过条件阶段,什么都不用做 break; case TriggerInfo::Category::Any: { - if (check_condition_any(_Trigger)) { + if (!check_condition_any(_Trigger)) { return false; } } break; case TriggerInfo::Category::Not: { - if (check_condition_not(_Trigger)) { + if (!check_condition_not(_Trigger)) { return false; } } break; case TriggerInfo::Category::All: [[fallthrough]]; default: { - if (check_condition_all(_Trigger)) { + if (!check_condition_all(_Trigger)) { return false; } } break; @@ -1012,6 +1087,158 @@ bool asst::BattleProcessTask::check_condition(const battle::copilot::TriggerInfo return true; } +bool asst::BattleProcessTask::check_point( + const battle::copilot::PointInfo& _Current, + std::chrono::steady_clock::time_point const& _Now) +{ + switch (_Current.mode) { + case TriggerInfo::Category::Succ: // 默认成功,跳过条件阶段,什么都不用做 + break; + case TriggerInfo::Category::Any: { + if (!check_point_any(_Current, _Now)) { + return false; + } + } break; + case TriggerInfo::Category::Not: { + if (!check_point_not(_Current, _Now)) { + return false; + } + } break; + case TriggerInfo::Category::All: + [[fallthrough]]; + default: { + if (!check_point_all(_Current, _Now)) { + return false; + } + } break; + } + + return true; +} + +bool asst::BattleProcessTask::check_point_not( + const battle::copilot::PointInfo& _Current, + std::chrono::steady_clock::time_point const& _Now) +{ + using TriggerInfo = battle::copilot::TriggerInfo; + + auto& target = get_snap_shot(_Current.target_code); + + if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { + if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + return false; + } + } + + if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + return false; + } + } + + if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cooling_count <= target.cooling_count) && + (target.cooling_count <= _Current.range.second.cooling_count)) { + return false; + } + } + + if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { + auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { + return false; + } + } + + return true; +} + +bool asst::BattleProcessTask::check_point_all( + const battle::copilot::PointInfo& _Current, + std::chrono::steady_clock::time_point const& _Now) +{ + using TriggerInfo = battle::copilot::TriggerInfo; + + auto& target = get_snap_shot(_Current.target_code); + + if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { + if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + ; + } + else { + return false; + } + } + + if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + ; + } + else { + return false; + } + } + + if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cooling_count <= target.cooling_count) && + (target.cooling_count <= _Current.range.second.cooling_count)) { + ; + } + else { + return false; + } + } + + if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { + auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { + ; + } + else { + return false; + } + } + + return true; +} + +bool asst::BattleProcessTask::check_point_any( + const battle::copilot::PointInfo& _Current, + std::chrono::steady_clock::time_point const& _Now) +{ + using TriggerInfo = battle::copilot::TriggerInfo; + + auto& target = get_snap_shot(_Current.target_code); + + if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { + if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + return true; + } + } + + if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + return true; + } + } + + if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { + if ((_Current.range.first.cooling_count <= target.cooling_count) && + (target.cooling_count <= _Current.range.second.cooling_count)) { + return true; + } + } + + if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { + auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { + return true; + } + } + + return false; +} + bool asst::BattleProcessTask::enter_bullet_time(const std::string& name, const Point& location) { LogTraceFunction; @@ -1038,6 +1265,42 @@ void asst::BattleProcessTask::sleep_and_do_strategy(unsigned millisecond) } } +void asst::BattleProcessTask::save_snap_shot(const std::string& code) +{ + using TriggerInfo = battle::copilot::TriggerInfo; + + if (code.empty()) { + return; + } + + auto& shot = m_snap_shots[code]; + + cv::Mat image; + + update_image_if_empty(&image); + + update_cost(image); + shot.cost = m_cost; + + update_kills(image); + shot.kills = m_kills; + + if (update_deployment(false, image)) { + shot.cooling_count = + ranges::count_if(m_cur_deployment_opers, [](const auto& oper) -> bool { return oper.cooling; }); + } + + shot.tNow = std::chrono::steady_clock::now(); + + do_strategy_and_update_image(&image); +} + +auto asst::BattleProcessTask::get_snap_shot(const std::string& code) const noexcept + -> battle::copilot::PointInfo::SnapShot const& +{ + return m_snap_shots.at(code); +} + bool asst::BattleProcessTask::check_in_battle(const cv::Mat& reusable, bool weak) { LogTraceFunction; diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h index c2b3513223..9aca943cf3 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h @@ -59,13 +59,23 @@ protected: bool check_condition_any(const battle::copilot::TriggerInfo& action); // 某个条件都满足 bool check_condition(const battle::copilot::TriggerInfo& action); + bool check_point(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); + bool check_point_not(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); + bool check_point_all(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); + bool check_point_any(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); + bool enter_bullet_time(const std::string& name, const Point& location); void sleep_and_do_strategy(unsigned millisecond); + void save_snap_shot(const std::string& code); + + auto get_snap_shot(const std::string& code) const noexcept -> battle::copilot::PointInfo::SnapShot const&; + virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true) override; battle::copilot::CombatData m_combat_data; std::unordered_map m_oper_in_group; + std::map m_snap_shots; // 在运行过程中存储的快照信息 bool m_in_bullet_time = false; bool m_need_to_wait_until_end = false; From 6527c9859e97d1294ecb2e4ca9bcad881feec6a2 Mon Sep 17 00:00:00 2001 From: valencia_fly Date: Sun, 28 Jul 2024 22:35:39 +0800 Subject: [PATCH 04/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=8A=9F=E8=83=BD=E4=B8=8A=E4=B8=8D=E8=83=BD=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 8 +- .../Config/Miscellaneous/CopilotConfig.cpp | 2 +- .../Task/Miscellaneous/BattleProcessTask.cpp | 95 ++++++++++--------- .../Task/Miscellaneous/BattleProcessTask.h | 10 +- 4 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index af256c0652..ec88256a9a 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -241,19 +241,19 @@ struct TriggerInfo static auto loadCategoryFrom(std::string const& _Str) -> Category { - if (_Str == "Succ") { + if (_Str == "succ") { return Category::Succ; } - if (_Str == "All") { + if (_Str == "all") { return Category::All; } - if (_Str == "Any") { + if (_Str == "any") { return Category::Any; } - if (_Str == "Not") { + if (_Str == "not") { return Category::Not; } diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index 74542f7580..6b89c99b94 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -119,7 +119,7 @@ TriggerInfo asst::CopilotConfig::parse_trigger(const json::value& json) 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.cost_changes = json.get("cost_changes", TriggerInfo::DEACTIVE_COST_CHANGES); trigger.cooling = json.get("cooling", TriggerInfo::DEACTIVE_COOLING); trigger.count = json.get("count", TriggerInfo::DEACTIVE_COUNT); diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index a81b26e6f8..6af230b743 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -270,11 +270,11 @@ bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, s goto END_LOOP; } - if (info.continue_info.active() && !check_condition(info.continue_info)) { + if (info.continue_info.active() && check_condition(info.continue_info)) { goto NEXT_LOOP; } - if (info.break_info.active() && !check_condition(info.break_info)) { + if (info.break_info.active() && check_condition(info.break_info)) { goto BREAK_LOOP; } @@ -427,7 +427,7 @@ END_LOOP:; do { const auto now = std::chrono::steady_clock::now(); - if (check_point(info, now)) { + if (check_point(info)) { break; } @@ -454,7 +454,7 @@ END_LOOP:; case ActionType::CheckPoint: { auto& info = action.getPayload(); - if (check_point(info, std::chrono::steady_clock::now())) { + if (check_point(info)) { int i = 0; for (; i < info.then_actions.size(); ++i) { if (!do_action_sync(*info.then_actions[i], i)) { @@ -1087,27 +1087,25 @@ bool asst::BattleProcessTask::check_condition(const battle::copilot::TriggerInfo return true; } -bool asst::BattleProcessTask::check_point( - const battle::copilot::PointInfo& _Current, - std::chrono::steady_clock::time_point const& _Now) +bool asst::BattleProcessTask::check_point(const battle::copilot::PointInfo& _Current) { switch (_Current.mode) { case TriggerInfo::Category::Succ: // 默认成功,跳过条件阶段,什么都不用做 break; case TriggerInfo::Category::Any: { - if (!check_point_any(_Current, _Now)) { + if (!check_point_any(_Current)) { return false; } } break; case TriggerInfo::Category::Not: { - if (!check_point_not(_Current, _Now)) { + if (!check_point_not(_Current)) { return false; } } break; case TriggerInfo::Category::All: [[fallthrough]]; default: { - if (!check_point_all(_Current, _Now)) { + if (!check_point_all(_Current)) { return false; } } break; @@ -1116,35 +1114,36 @@ bool asst::BattleProcessTask::check_point( return true; } -bool asst::BattleProcessTask::check_point_not( - const battle::copilot::PointInfo& _Current, - std::chrono::steady_clock::time_point const& _Now) +bool asst::BattleProcessTask::check_point_not(const battle::copilot::PointInfo& _Current) { using TriggerInfo = battle::copilot::TriggerInfo; auto& target = get_snap_shot(_Current.target_code); + auto now = gen_snap_shot(); if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { - if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + auto diff = now.kills - target.kills; + if ((_Current.range.first.kills <= diff) && (diff <= _Current.range.second.kills)) { return false; } } if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + auto diff = now.cost - target.cost; + if ((_Current.range.first.cost <= diff) && (diff <= _Current.range.second.cost)) { return false; } } if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cooling_count <= target.cooling_count) && - (target.cooling_count <= _Current.range.second.cooling_count)) { + auto diff = now.cooling_count - target.cooling_count; + if ((_Current.range.first.cooling_count <= diff) && (diff <= _Current.range.second.cooling_count)) { return false; } } if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { - auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + auto diff = std::chrono::duration_cast(now.tNow - target.tNow).count(); if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { return false; } @@ -1153,16 +1152,16 @@ bool asst::BattleProcessTask::check_point_not( return true; } -bool asst::BattleProcessTask::check_point_all( - const battle::copilot::PointInfo& _Current, - std::chrono::steady_clock::time_point const& _Now) +bool asst::BattleProcessTask::check_point_all(const battle::copilot::PointInfo& _Current) { using TriggerInfo = battle::copilot::TriggerInfo; auto& target = get_snap_shot(_Current.target_code); + auto now = gen_snap_shot(); if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { - if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + auto diff = now.kills - target.kills; + if ((_Current.range.first.kills <= diff) && (diff <= _Current.range.second.kills)) { ; } else { @@ -1171,7 +1170,8 @@ bool asst::BattleProcessTask::check_point_all( } if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + auto diff = now.cost - target.cost; + if ((_Current.range.first.cost <= diff) && (diff <= _Current.range.second.cost)) { ; } else { @@ -1180,8 +1180,8 @@ bool asst::BattleProcessTask::check_point_all( } if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cooling_count <= target.cooling_count) && - (target.cooling_count <= _Current.range.second.cooling_count)) { + auto diff = now.cooling_count - target.cooling_count; + if ((_Current.range.first.cooling_count <= diff) && (diff <= _Current.range.second.cooling_count)) { ; } else { @@ -1190,7 +1190,7 @@ bool asst::BattleProcessTask::check_point_all( } if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { - auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + auto diff = std::chrono::duration_cast(now.tNow - target.tNow).count(); if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { ; } @@ -1202,35 +1202,36 @@ bool asst::BattleProcessTask::check_point_all( return true; } -bool asst::BattleProcessTask::check_point_any( - const battle::copilot::PointInfo& _Current, - std::chrono::steady_clock::time_point const& _Now) +bool asst::BattleProcessTask::check_point_any(const battle::copilot::PointInfo& _Current) { using TriggerInfo = battle::copilot::TriggerInfo; auto& target = get_snap_shot(_Current.target_code); + auto now = gen_snap_shot(); if (_Current.range.first.kills != TriggerInfo::DEACTIVE_KILLS) { - if ((_Current.range.first.kills <= target.kills) && (target.kills <= _Current.range.second.kills)) { + auto diff = now.kills - target.kills; + if ((_Current.range.first.kills <= diff) && (diff <= _Current.range.second.kills)) { return true; } } if (_Current.range.first.cost != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cost <= target.cost) && (target.cost <= _Current.range.second.cost)) { + auto diff = now.cost - target.cost; + if ((_Current.range.first.cost <= diff) && (diff <= _Current.range.second.cost)) { return true; } } if (_Current.range.first.cooling_count != TriggerInfo::DEACTIVE_COST) { - if ((_Current.range.first.cooling_count <= target.cooling_count) && - (target.cooling_count <= _Current.range.second.cooling_count)) { + auto diff = now.cooling_count - target.cooling_count; + if ((_Current.range.first.cooling_count <= diff) && (diff <= _Current.range.second.cooling_count)) { return true; } } if (_Current.range.first.interval != 0 || _Current.range.second.interval != 0) { - auto diff = std::chrono::duration_cast(_Now - target.tNow).count(); + auto diff = std::chrono::duration_cast(now.tNow - target.tNow).count(); if ((_Current.range.first.interval <= diff) && (diff <= _Current.range.second.interval)) { return true; } @@ -1265,21 +1266,16 @@ void asst::BattleProcessTask::sleep_and_do_strategy(unsigned millisecond) } } -void asst::BattleProcessTask::save_snap_shot(const std::string& code) +auto asst::BattleProcessTask::gen_snap_shot() -> battle::copilot::PointInfo::SnapShot { - using TriggerInfo = battle::copilot::TriggerInfo; - - if (code.empty()) { - return; - } - - auto& shot = m_snap_shots[code]; - cv::Mat image; update_image_if_empty(&image); update_cost(image); + + battle::copilot::PointInfo::SnapShot shot; + shot.cost = m_cost; update_kills(image); @@ -1293,6 +1289,19 @@ void asst::BattleProcessTask::save_snap_shot(const std::string& code) shot.tNow = std::chrono::steady_clock::now(); do_strategy_and_update_image(&image); + + return shot; +} + +void asst::BattleProcessTask::save_snap_shot(const std::string& code) +{ + using TriggerInfo = battle::copilot::TriggerInfo; + + if (code.empty()) { + return; + } + + m_snap_shots[code] = gen_snap_shot(); } auto asst::BattleProcessTask::get_snap_shot(const std::string& code) const noexcept diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h index 9aca943cf3..5cb57388c5 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h @@ -59,14 +59,16 @@ protected: bool check_condition_any(const battle::copilot::TriggerInfo& action); // 某个条件都满足 bool check_condition(const battle::copilot::TriggerInfo& action); - bool check_point(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); - bool check_point_not(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); - bool check_point_all(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); - bool check_point_any(const battle::copilot::PointInfo& _Current, std::chrono::steady_clock::time_point const& _Now); + bool check_point(const battle::copilot::PointInfo& _Current); + bool check_point_not(const battle::copilot::PointInfo& _Current); + bool check_point_all(const battle::copilot::PointInfo& _Current); + bool check_point_any(const battle::copilot::PointInfo& _Current); bool enter_bullet_time(const std::string& name, const Point& location); void sleep_and_do_strategy(unsigned millisecond); + auto gen_snap_shot() -> battle::copilot::PointInfo::SnapShot; + void save_snap_shot(const std::string& code); auto get_snap_shot(const std::string& code) const noexcept -> battle::copilot::PointInfo::SnapShot const&; From 7b205f62fe1b20629f7202121800dc4018f3f01f Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 12:49:16 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8MacOs?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=97=A8=E7=A6=81=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 2 +- .../Task/Miscellaneous/BattleProcessTask.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index ec88256a9a..3e3c6514ce 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -335,7 +335,7 @@ struct PointInfo }; std::string target_code; - TriggerInfo::Category mode; + TriggerInfo::Category mode = TriggerInfo::Category::All; std::pair range; std::vector then_actions; // 当条件满足时执行 diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index 6af230b743..414f3c4096 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -265,7 +265,7 @@ bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, s info.end_info.activeCounter(); // 执行循环体 - for (int i = 0; i < info.loop_actions.size(); ++i) { + for (size_t i = 0; i < info.loop_actions.size(); ++i) { if (need_exit() || !m_in_battle) { goto END_LOOP; } @@ -297,7 +297,7 @@ END_LOOP:; // 没找到或者没在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) { + for (size_t i = 0; i < itFind->second.size(); ++i) { ret &= do_action_sync(*itFind->second[i], i); if (need_exit() || !m_in_battle) { @@ -320,7 +320,7 @@ END_LOOP:; action.trigger.resetCounter(); switch (info.mode) { case TriggerInfo::Category::Any: { - int idx = 0; + size_t idx = 0; while (ret == false) { // 到达循环极限,退出 if (action.trigger.counter == action.trigger.count) { @@ -347,7 +347,7 @@ END_LOOP:; [[fallthrough]]; default: { std::set setSucc; - int idx = 0; + size_t idx = 0; // 全部成功则完成循环 while (setSucc.size() != info.candidate.size()) { @@ -385,7 +385,7 @@ END_LOOP:; if (check_condition(info.condition_info)) { // 触发器满足条件 - int i = 0; + size_t i = 0; for (; i < info.then_actions.size(); ++i) { if (!do_action_sync(*info.then_actions[i], i)) { break; @@ -399,7 +399,7 @@ END_LOOP:; ret = (i == info.then_actions.size()); } else { // 触发器不满足条件 - int i = 0; + size_t i = 0; for (; i < info.else_actions.size(); ++i) { if (!do_action_sync(*info.else_actions[i], i)) { break; @@ -438,7 +438,7 @@ END_LOOP:; } } while (!need_exit() && m_in_battle); - int i = 0; + size_t i = 0; for (; i < info.then_actions.size(); ++i) { if (!do_action_sync(*info.then_actions[i], i)) { break; @@ -455,7 +455,7 @@ END_LOOP:; auto& info = action.getPayload(); if (check_point(info)) { - int i = 0; + size_t i = 0; for (; i < info.then_actions.size(); ++i) { if (!do_action_sync(*info.then_actions[i], i)) { break; @@ -469,7 +469,7 @@ END_LOOP:; ret = (i == info.then_actions.size()); } else { - int i = 0; + size_t i = 0; for (; i < info.else_actions.size(); ++i) { if (!do_action_sync(*info.else_actions[i], i)) { break; From 557329075fbd8968bb895450477643fd2db24f6f Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 14:41:26 +0800 Subject: [PATCH 06/10] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=93=8D=E4=BD=9C=E7=9A=84=E4=B8=AD=E6=96=87=E5=91=BD?= =?UTF-8?q?=E5=90=8D=EF=BC=8C=E5=BD=93=E9=94=9A=E7=82=B9=E4=B8=8D=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E6=97=B6=E6=96=B0=E5=A2=9E=E9=80=80=E5=87=BA=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E4=BD=9C=E4=B8=BA=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 8 ++++++-- .../Config/Miscellaneous/CopilotConfig.cpp | 20 +++++++++++++------ .../Task/Miscellaneous/BattleProcessTask.cpp | 19 ++++++++++++++++-- .../Task/Miscellaneous/BattleProcessTask.h | 3 +-- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index 3e3c6514ce..9a5bf31b79 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -317,8 +317,8 @@ struct CheckInfo // 定义until操作需要的信息 struct UntilInfo { - TriggerInfo::Category mode; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束 - std::vector candidate; // 备用的命令序列 + TriggerInfo::Category mode = TriggerInfo::Category::All; // all 全部命令执行完毕后才结束, any 只要有一个执行就结束 + std::vector candidate; // 备用的命令序列 }; // 定义point操作需要的信息 @@ -332,6 +332,8 @@ struct PointInfo int interval = 0; std::chrono::steady_clock::time_point tNow; + + SnapShot() = default; }; std::string target_code; @@ -340,6 +342,8 @@ struct PointInfo std::vector then_actions; // 当条件满足时执行 std::vector else_actions; // 当条件不满足时执行 + + PointInfo() = default; }; struct CheckIfStartOverInfo diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index 6b89c99b94..de8b4b50d3 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -217,13 +217,13 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat { "Check", ActionType::Check }, { "check", ActionType::Check }, { "CHECK", ActionType::Check }, - { "检查", ActionType::Check }, + { "确认", ActionType::Check }, { "分支", ActionType::Check }, { "Until", ActionType::Until }, { "until", ActionType::Until }, { "UNTIL", ActionType::Until }, - { "直到", ActionType::Until }, + { "轮询", ActionType::Until }, { "SavePoint", ActionType::SavePoint }, { "savepoint", ActionType::SavePoint }, @@ -376,8 +376,11 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat case ActionType::Until: { auto& until = action.payload.emplace(); - // 必选字段 - until.mode = TriggerInfo::loadCategoryFrom(action_info.at("mode").as_string()); + // 可选字段 + if (auto t = action_info.find("mode")) { + until.mode = TriggerInfo::loadCategoryFrom(t.value().as_string()); + } + switch (until.mode) { case TriggerInfo::Category::Any: case TriggerInfo::Category::All: @@ -387,8 +390,13 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat break; } - // 必选字段,缺省值为0 - action.trigger.count = action_info.get("limit", 0); + // 可选字段,缺省值为0 + if (auto t = action_info.find("limit")) { + action.trigger.count = t.value().as_integer(); + } + else if (action.trigger.count == TriggerInfo::DEACTIVE_COUNT) { + action.trigger.count = 0; + } // 可选字段 if (auto t = action_info.find("candidate_actions")) { diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index 414f3c4096..3903ed0406 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -420,6 +420,12 @@ END_LOOP:; case ActionType::SyncPoint: { auto& info = action.getPayload(); + // 目标编码不匹配,退出 + if (!find_snap_shot(info.target_code)) { + ret = false; + break; + } + 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); @@ -454,6 +460,12 @@ END_LOOP:; case ActionType::CheckPoint: { auto& info = action.getPayload(); + // 目标编码不匹配,退出 + if (!find_snap_shot(info.target_code)) { + ret = false; + break; + } + if (check_point(info)) { size_t i = 0; for (; i < info.then_actions.size(); ++i) { @@ -1293,10 +1305,13 @@ auto asst::BattleProcessTask::gen_snap_shot() -> battle::copilot::PointInfo::Sna return shot; } +bool asst::BattleProcessTask::find_snap_shot(const std::string& code) const noexcept +{ + return m_snap_shots.find(code) != m_snap_shots.cend(); +} + void asst::BattleProcessTask::save_snap_shot(const std::string& code) { - using TriggerInfo = battle::copilot::TriggerInfo; - if (code.empty()) { return; } diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h index 5cb57388c5..37b22ab065 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.h @@ -68,9 +68,8 @@ protected: void sleep_and_do_strategy(unsigned millisecond); auto gen_snap_shot() -> battle::copilot::PointInfo::SnapShot; - + bool find_snap_shot(const std::string& code) const noexcept; void save_snap_shot(const std::string& code); - auto get_snap_shot(const std::string& code) const noexcept -> battle::copilot::PointInfo::SnapShot const&; virtual bool check_in_battle(const cv::Mat& reusable = cv::Mat(), bool weak = true) override; From 95ea69a78fcd3c8b1412e3c6e548363c7340e135 Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 14:41:49 +0800 Subject: [PATCH 07/10] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E6=88=98?= =?UTF-8?q?=E6=96=97=E6=B5=81=E7=A8=8B=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh-cn/protocol/copilot-schema.md | 217 +++++++++++++++++++++++--- 1 file changed, 194 insertions(+), 23 deletions(-) diff --git a/docs/zh-cn/protocol/copilot-schema.md b/docs/zh-cn/protocol/copilot-schema.md index 04197ed37e..e8f3be89d9 100644 --- a/docs/zh-cn/protocol/copilot-schema.md +++ b/docs/zh-cn/protocol/copilot-schema.md @@ -57,12 +57,15 @@ icon: ph:sword-bold ] } ], - "actions": [ - // 战斗中的操作。有序,执行完前一个才会去执行下一个。必选 + "actions": [ // 战斗中的操作。有序,执行完前一个才会去执行下一个。必选 + // 以下介绍一个操作通用携带的属性,包括类型、触发、延时、说明、命令补充信息等 { - "type": "部署", // 操作类型,可选,默认为 "Deploy" + "type": "打印", // 操作类型,可选,默认为 "Deploy" // "Deploy" | "Skill" | "Retreat" | "SpeedUp" | "BulletTime" | "SkillUsage" | "Output" | "SkillDaemon" | "MoveCamera" // "部署" | "技能" | "撤退" | "二倍速" | "子弹时间" | "技能用法" | "打印" | "摆完挂机" | "移动镜头" + // + // "Loop" | "Case" | "Check" | "Until" | "SavePoint" | "SyncPoint" | "CheckPoint" + // "循环" | "派发" | "确认" | "轮询" | "保存锚点" | "同步锚点" | "检查锚点" // 中英文皆可,效果相同 // 若为 "部署", 当费用不够时,会一直等待到费用够(除非 timeout) // 若为 "技能", 当技能 cd 没转好时,一直等待到技能 cd 好(除非 timeout) @@ -74,9 +77,17 @@ icon: ph:sword-bold // "打印" 界面不显示这条步骤,仅用于输出 doc 里的内容(用来做字幕之类的) // "摆完挂机" 仅使用 "好了就用" 的技能,其他什么都不做,直到战斗结束 // "移动镜头" 用于 “引航者试炼” 模式,还需要填写 distance 字段 - // 目前下面四个条件是且的关系,即 && - "kills": 0, // 击杀数条件,如果没达到就一直等待。可选,默认为 0,直接执行 - "costs": 50, // 费用条件,如果没达到就一直等待。可选,默认为 0,直接执行 + // "循环" 用于构建循环流程,可以重复执行包含的动作 + // "派发" 用于根据当前用户在抄作业时在干员组中选定的干员派发出不同的动作 + // "检查" 用于根据当前战场中的信息条件做出不同的分支选择,不阻塞 + // "直到" 用于创建一个无序的动作集合, 直到其中一个完成,或者是全部完成 + // "保存锚点" 手动创建锚点,保存当前的 击杀数,费用数,时间戳等信息快照 + // "同步锚点" 表示等待至当前信息快照减去指定锚点的信息快照之后满足指定区间 + // "检查锚点" 表示当前信息快照减去指定锚点的信息快照之后是否满足指定区间,不阻塞,根据判断结果来执行分支动作 + + // 动作的触发前提 + "kills": 0, // 可选, 击杀数条件,如果没达到就一直等待。 + "costs": 50, // 可选, 费用条件,如果没达到就一直等待。 // 费用受潜能等影响,可能并不完全正确,仅适合对时间轴要求不严格的战斗。 // 否则请使用下面的 cost_changes // 另外仅在费用是两位数的时候识别的比较准,三位数的费用可能会识别错,不推荐使用 @@ -85,14 +96,35 @@ icon: ph:sword-bold // 支持负数,即费用变少了(例如“孑”等吸费干员使得费用变少了) // 另外仅在费用是两位数的时候识别的比较准,三位数的费用可能会识别错,不推荐使用 "cooling": 2, // CD 中干员数量条件,如果没达到就一直等待。可选,默认为 -1,不识别 + "count" : 0, // 用于循环操作的计数, 0表示一次都不执行,100则表示循环执行100次。 // TODO: 其他条件 - // TODO: "condition_type": 0, // 执行条件间的关系,可选,默认为 0 - // // 0 - 且; 1 - 或 + "category" : "all", // 可选,succ|not|all|any 表示动作触发条件的组合策略,默认是all + // succ表示跳过判断触发动作直接成功 + // not 表示所有触发条件都不满足 + // all 表示所有触发条件都要满足 + // any 表示只要有一个触发条件满足 + + // 延时操作 + "pre_delay": 0, // 前置延时。可选,默认为 0, 单位毫秒,表示在前面的触发条件满足之后,指定延迟才开始执行动作 + "post_delay": 0, // 后置延时。可选,默认为 0, 单位毫秒,表示在动作执行完毕之后,指定延迟 + // "timeout": 999999999, // 保留字段,暂未实现。 + // 超时时间。当 type 为 "部署" | "技能" 时可选。默认 INT_MAX, 单位毫秒 + // 等待超时则放弃当前动作, 转而执行下一个动作 + + "point_code" :"POINT-1", // 可选,表示在执行命令之前注册锚点,锚点功能在后续举例中进行补充 + + // 说明操作 + "doc": "请开始你的表演~", // 描述,可选。会显示在界面上,没有实际作用 + "doc_color": "green" // 描述文字的颜色,可选,默认灰色。会显示在界面上,没有实际作用。 + }, + + // 干员操作类动作,一般是 "部署" | "技能" | "撤退" | "子弹时间" | "技能用法" + { "name": "棘刺", // 干员名 或 群组名, type 为 "部署" 时必选,为 "技能" | "撤退" 时可选 "location": [ 5, 5 - ], // 部署干员的位置。 + ], // 部署干员的位置,使用技能的位置。 // type 为 "部署" 时必选。 // type 为 "技能" | "撤退" 时可选, // "技能":仅推荐场地上自动的装置等,不填写 name,并使用 location 开启技能。正常部署的干员推荐使用 name 开启技能 @@ -105,17 +137,7 @@ icon: ph:sword-bold // 举例:刚下淘金娘需要她帮忙打几个怪,不能自动开技能,中后期平稳了需要她自动开技能 // 则可以在对应时刻设置为 1 "skill_times": 5, // 技能使用次数。可选,默认为 1 - "pre_delay": 0, // 前置延时。可选,默认为 0, 单位毫秒 - "post_delay": 0, // 后置延时。可选,默认为 0, 单位毫秒 - // "timeout": 999999999, // 保留字段,暂未实现。 - // 超时时间。当 type 为 "部署" | "技能" 时可选。默认 INT_MAX, 单位毫秒 - // 等待超时则放弃当前动作, 转而执行下一个动作 - "distance": [ - 4.5, - 0 - ], // type 为 "移动镜头" 时必选 - // [ x 移动格子数,y 移动格子数 ],可为小数 - // 注意 "移动镜头" 时是识别不到正在站场中的,需要用 sleep 完整覆盖整个移动动画 + "doc": "下棘刺了!", // 描述,可选。会显示在界面上,没有实际作用 "doc_color": "orange" // 描述文字的颜色,可选,默认灰色。会显示在界面上,没有实际作用。 }, @@ -139,10 +161,159 @@ icon: ph:sword-bold "doc": "你史尔特尔奶奶来啦!", "doc_color": "red" }, - // 举例 3 + + // 工具类 { - "type": "二倍速" - } + "type": "二倍速", // 切换速度,如果机器性能不好,建议加一点后置延时,让屏幕点击能流畅衔接 + "post_delay" : 200 + }, + { + "type": "打印", // 单纯用来打印,也可以用来说明一些操作场景的开始与结束 + "doc": "你史尔特尔奶奶来啦!", + "doc_color": "red" + }, + { + "type": "摆完挂机", // 如果没有摆完挂机,maa会直接结束所有操作不再控制,在需要自动开启干员技能时,都建议在最后加上"摆完挂机"操作 + }, + + // 流程类 + { + "type" : "循环", + "kills" : 10, + "costs" : 20, + "category" : "any", // 这里表示 击杀10和费用20 等待到只要其中一个发生就开始触发 + + "end" : { // 必选,循环结束的触发条件 + "kills" : 20, + "costs" : 99, + "count" : 10, + "category" : "all", // 这里表示 击杀20,费用99, 循环10次 全部满足才结束循环 + }, + + "continue" : { // 可选,循环重新执行的条件 + "costs" : 20, + "category" : "all", // 表示执行循环操作中的间隙,费用超过了20,就不继续执行后面的操作,重新开头执行 + }, + + "break" : { // 可选,循环中止的条件 + "cooling" : 2, + "category" : "all", // 表示执行循环操作中的间隙,有两个干员意外撤退了,终止循环,结束循环命令 + }, + + "loop_actions" :[] // 这里放需要循环执行的操作 + }, + { + "type" : "派发", + "select" : "法卫", // 必选,需要匹配的干员组,如果不匹配,什么也不做 + "dispatch_actions" :{ + "史尔特尔" : [ + { + "type": "打印", + "doc": "你史尔特尔奶奶来啦!", + "doc_color": "red" + }, + ], + "拉普兰德" : [ + { + "type": "打印", + "doc": "拉狗也有法术输出", + "doc_color": "grey" + } + ], + + }, + "default_action" :[ + { + "type": "打印", + "doc": "法卫中的其他杂鱼", + "doc_color": "grey" + } + ] + }, + { + "type" : "确认", + "condition" : { // 检查的相关条件,不阻塞 + "kill" : 50, + "costs" : 60, + "category" : "not" // 表示当前的状态,击杀数小于50,且 costs也低于60 + }, + "then_actions" :[ + { + "type": "打印", + "doc": "危!练度不足,先锋疲软。" + } + ], + "else_actions" :[ + { + "type": "打印", + "doc": "不足为惧!" + } + ] + }, + { + "type" : "轮询", // 轮询执行候选操作中的一个,只要满足就可以执行 + "mode" : "all", // 可选,默认为all,all表示全部执行完才退出,any表示其中一个满足就执行并退出,其余不识别的字符当作 all策略 + "limit" : 100, // 表示当前直到动作满足的过程中限制的循环次数,与 "count" 等效,limit指定的优先级更高 + "candidate_actions" :[ + { + "type": "打印", + "kills" : 2, + "doc": "一鼓作气" + }, + { + "type": "打印", + "kills" : 3, + "doc": "再而衰" + }, + { + "type": "打印", + "kills" : 1, + "doc": "三而竭" + } + ] + }, + + // 锚点操作类,锚点表示在命令执行前一刻记录下的击杀,费用,时间戳等信息,可以跨操作进行区间比较,减小多个操作连续允许造成的相对误差 + { + "type":"保存锚点", + "point_code" : "Point-1" // 必选,设定一个锚点编码。如果多个操作的编码重复,则覆盖之前保存的锚点信息 + }, + { + "type": "同步锚点", // 等待至条件满足 + "point_code" : "Point-2", // 可选,同步锚点在执行时也可以创建锚点 + "target_code" : "Point-1", // 需要进行比较的锚点,如果锚点编码不存在,结束操作 + "kill_range":[-1, 1], // 当前的击杀数-目标的击杀数 在 [-1,1]之间 + "cost_range":[0, 50], + "cooling_range":[0, 50], + "time_range":[5000, 5000], // 相比指定锚点,过去了5000ms也就是5s + "mode" : "all", // 表示条件策略, 满足条件策略之后才执行后续的操作。 succ|not|all|any + "then_actions" :[] + }, + { + "type": "检查锚点", // 如果条件满足执行then_actions,否则执行else_actions,不等待。 + "point_code" : "Point-3", // 可选,同步锚点在执行时也可以创建锚点 + "target_code" : "Point-2", // 需要进行比较的锚点,如果锚点编码不存在,结束操作 + "kill_range":[-1, 1], // 当前的击杀数-目标的击杀数 在 [-1,1]之间 + "cost_range":[0, 50], + "cooling_range":[0, 50], + "time_range":[5000, 5000], // 相比指定锚点,过去了5000ms也就是5s + "mode" : "all", // 表示条件策略, 满足条件策略之后才执行后续的操作。 succ|not|all|any + "then_actions" :[], + "else_actions" :[], + }, + + // 特殊场景类 + { + "type": "移动镜头", + "distance": [ + 4.5, + 0 + ], // type 为 "移动镜头" 时必选 + // [ x 移动格子数,y 移动格子数 ],可为小数, 也可以是负数,用来校准镜头 + // 注意 "移动镜头" 时是识别不到正在站场中的,需要用 sleep 完整覆盖整个移动动画 + }, + + ], "minimum_required": "v4.0", // 最低要求 maa 版本号,必选。保留字段,暂未实现 "doc": { From c8f2696d4c0d619d364347e9142d64f1bcb8fce2 Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 15:16:27 +0800 Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DLinux/MacOS?= =?UTF-8?q?=E4=B8=8B=E5=85=B3=E4=BA=8EPointInfo=E7=9A=84=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index 9a5bf31b79..fa87643742 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -208,7 +208,7 @@ 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 int64_t DEACTIVE_COOLING = -1; // 干员冷却数未设置 static constexpr int DEACTIVE_COUNT = -1; // 循环计数 enum class Category @@ -333,7 +333,7 @@ struct PointInfo std::chrono::steady_clock::time_point tNow; - SnapShot() = default; + SnapShot() {} }; std::string target_code; @@ -343,7 +343,7 @@ struct PointInfo std::vector then_actions; // 当条件满足时执行 std::vector else_actions; // 当条件不满足时执行 - PointInfo() = default; + PointInfo() {} }; struct CheckIfStartOverInfo From 89ac713d9209b1f042874ae4a9817c3ab0d5fcd0 Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 16:49:29 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20=E6=B6=88=E9=99=A4linux=E4=B8=8B?= =?UTF-8?q?=E9=97=A8=E7=A6=81=E7=9A=84=E7=BC=96=E8=AF=91=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 6 +++--- src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index fa87643742..36016a74ec 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -239,7 +239,7 @@ struct TriggerInfo // 激活一次计数器 void activeCounter() const noexcept { ++counter; } - static auto loadCategoryFrom(std::string const& _Str) -> Category + static auto loadCategoryFrom(std::string const& _Str) noexcept -> Category { if (_Str == "succ") { return Category::Succ; @@ -353,7 +353,7 @@ struct CheckIfStartOverInfo CheckIfStartOverInfo() = default; - explicit CheckIfStartOverInfo(RoleCounts&& _Counts) : + explicit CheckIfStartOverInfo(RoleCounts&& _Counts) noexcept : role_counts(std::move(_Counts)) { } @@ -363,7 +363,7 @@ struct MoveCameraInfo { std::pair distance; - explicit MoveCameraInfo(decltype(distance)&& _Dist) : + explicit MoveCameraInfo(decltype(distance)&& _Dist) noexcept : distance(std::move(_Dist)) { } diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index de8b4b50d3..18fcb3ba55 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -296,7 +296,8 @@ bool asst::CopilotConfig::parse_action(const json::value& action_info, asst::bat } break; case ActionType::MoveCamera: { auto dist_arr = action_info.at("distance").as_array(); - action.payload.emplace(std::make_pair(dist_arr[0].as_double(), dist_arr[1].as_double())); + action.payload.emplace( + std::pair(dist_arr[0].as_double(), dist_arr[1].as_double())); } break; case ActionType::Loop: { auto& loop = action.payload.emplace(); From 1efd262315cda92b33aa54752cd1b7377dd42508 Mon Sep 17 00:00:00 2001 From: ValenciaFly Date: Mon, 29 Jul 2024 17:46:37 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9F=90?= =?UTF-8?q?=E4=B8=AA=E7=8E=AF=E8=8A=82=E4=BD=BF=E7=94=A8range=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=90=8E=EF=BC=8C=E6=95=B0=E6=8D=AE=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E7=94=B1int->int64=5Ft=E5=AF=BC=E8=87=B4=E7=9A=84=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index 36016a74ec..53b5a3992e 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -225,7 +225,7 @@ struct TriggerInfo int kills = DEACTIVE_KILLS; // 击杀数条件 int costs = DEACTIVE_COST; // 费用条件 int cost_changes = DEACTIVE_COST_CHANGES; // 费用变化条件 - int cooling = DEACTIVE_COOLING; // 冷却中的干员条件 + int64_t cooling = DEACTIVE_COOLING; // 冷却中的干员条件 int count = DEACTIVE_COUNT; // 计数条件,不做变化,记录初始值 mutable int counter = 0; // 计数器