From 1edd00698200b7c2a14406d7a29f51689f6871d1 Mon Sep 17 00:00:00 2001 From: Weiyou Wang <44151844+Alan-Charred@users.noreply.github.com> Date: Mon, 20 Oct 2025 01:35:37 +1100 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B7=B3=E8=BF=87=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=9C=AA=E5=87=86=E5=A4=87=E5=A5=BD=E7=9A=84=E6=8A=80=E8=83=BD?= =?UTF-8?q?=20&&=20=E5=85=A8=E5=B1=80=E8=AE=A1=E6=97=B6=E5=99=A8=20(#13913?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: skip_if_not_ready * feat: elapsed_time * feat: WpfGui log 输出计时器信息 * chore: Auto update by pre-commit hooks [skip changelog] * feat: 不默认启用全局计时器 * refactor: 支持 ResetTimer 拼写变种 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/MaaCore/Common/AsstBattleDef.h | 3 ++ .../Config/Miscellaneous/CopilotConfig.cpp | 17 ++++++++ src/MaaCore/Task/BattleHelper.cpp | 15 +++++++ src/MaaCore/Task/BattleHelper.h | 4 ++ .../Task/Miscellaneous/BattleProcessTask.cpp | 40 +++++++++++++++---- src/MaaWpfGui/Main/AsstProxy.cs | 6 +++ src/MaaWpfGui/Res/Localizations/en-us.xaml | 1 + src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 1 + src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 1 + src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 1 + src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 1 + 11 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index 3f5c072ae1..1277a73bc3 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -225,6 +225,7 @@ enum class ActionType BulletTime, // 使用 1/5 的速度 Output, // 仅输出,什么都不操作,界面上也不显示 SkillDaemon, // 什么都不做,有技能开技能,直到战斗结束 + ResetTimer, // 重置全局计时器 (试验性功能) /* for TRN */ MoveCamera, // 引航者试炼,移动镜头 @@ -253,6 +254,8 @@ struct Action std::string doc_color; RoleCounts role_counts; std::pair distance; + bool skip_if_not_ready = false; // 跳过使用未准备好的技能,主要用于关闭技能的场景 (试验性功能) + int elapsed_time = 0; // 全局计时条件 (试验性功能) }; struct BasicInfo diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index 5d7a3989d7..a40ac52f3a 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -176,6 +176,11 @@ std::vector asst::CopilotConfig::parse_actions(co { "CHECKIFSTARTOVER", ActionType::CheckIfStartOver }, { "checkifstartover", ActionType::CheckIfStartOver }, { "检查重开", ActionType::CheckIfStartOver }, + + { "ResetTimer", ActionType::ResetTimer }, + { "RESETTIMER", ActionType::ResetTimer }, + { "resettimer", ActionType::ResetTimer }, + { "Resettimer", ActionType::ResetTimer }, }; std::string type_str = action_info.get("type", "Deploy"); @@ -217,6 +222,18 @@ std::vector asst::CopilotConfig::parse_actions(co action.distance = std::make_pair(dist_arr[0].as_double(), dist_arr[1].as_double()); } + // ———————————————————————————————————————————————————————————————— + // 实验性功能 + // ———————————————————————————————————————————————————————————————— + // 跳过使用未准备好的技能,主要用于关闭技能的场景 + if (action.type == ActionType::UseSkill) { + action.skip_if_not_ready = action_info.get("skip_if_not_ready", false); + } + + // 计时器 + action.elapsed_time = action_info.get("elapsed_time", 0); + // ———————————————————————————————————————————————————————————————— + actions_list.emplace_back(std::move(action)); } diff --git a/src/MaaCore/Task/BattleHelper.cpp b/src/MaaCore/Task/BattleHelper.cpp index 11210e3c95..84d9188971 100644 --- a/src/MaaCore/Task/BattleHelper.cpp +++ b/src/MaaCore/Task/BattleHelper.cpp @@ -54,6 +54,7 @@ void asst::BattleHelper::clear() m_in_battle = false; m_kills = 0; m_total_kills = 0; + m_timer_enabled = false; m_cur_deployment_opers.clear(); m_battlefield_opers.clear(); m_used_tiles.clear(); @@ -1032,6 +1033,20 @@ std::optional asst::BattleHelper::get_oper_rect_on_deployment(const return oper_iter->rect; } +int asst::BattleHelper::elapsed_time() +{ + if (!m_timer_enabled) { + return -1; + } + auto now = std::chrono::steady_clock::now(); + auto elapsed_ms = std::chrono::duration_cast(now - m_baseline_time).count(); + if (elapsed_ms > std::numeric_limits::max()) { + Log.error(__FUNCTION__, "| elapsed time exceeds int maximum"); + return std::numeric_limits::max(); + } + return static_cast(elapsed_ms); +} + void asst::BattleHelper::remove_cooling_from_battlefield(const battle::DeploymentOper& oper) { if (!oper.cooling) { diff --git a/src/MaaCore/Task/BattleHelper.h b/src/MaaCore/Task/BattleHelper.h index c6269dc081..9813e25575 100644 --- a/src/MaaCore/Task/BattleHelper.h +++ b/src/MaaCore/Task/BattleHelper.h @@ -96,6 +96,8 @@ protected: std::string analyze_detail_page_oper_name(const cv::Mat& image); std::optional get_oper_rect_on_deployment(const std::string& name) const; + int elapsed_time(); + // 从场上干员和已占用格子中移除冷却中的干员 void remove_cooling_from_battlefield(const battle::DeploymentOper& oper); @@ -117,6 +119,8 @@ protected: int m_kills = 0; int m_total_kills = 0; int m_cost = 0; + bool m_timer_enabled = false; + std::chrono::steady_clock::time_point m_baseline_time; std::vector m_cur_deployment_opers; diff --git a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp index c2d9ac1e83..00fae1a896 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleProcessTask.cpp @@ -228,7 +228,9 @@ bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, s break; case ActionType::UseSkill: - ret = m_in_bullet_time ? click_skill() : (location.empty() ? use_skill(name) : use_skill(location)); + ret = m_in_bullet_time ? click_skill(!action.skip_if_not_ready) + : (location.empty() ? use_skill(name, !action.skip_if_not_ready) + : use_skill(location, !action.skip_if_not_ready)); if (ret) { m_in_bullet_time = false; } @@ -262,6 +264,11 @@ bool asst::BattleProcessTask::do_action(const battle::copilot::Action& action, s ret = move_camera(action.distance); break; + case ActionType::ResetTimer: + m_baseline_time = std::chrono::steady_clock::now(); + m_timer_enabled = true; + break; + case ActionType::SkillDaemon: ret = wait_until_end(); break; @@ -299,15 +306,15 @@ void asst::BattleProcessTask::notify_action(const battle::copilot::Action& actio { ActionType::MoveCamera, "MoveCamera" }, { ActionType::DrawCard, "DrawCard" }, { ActionType::CheckIfStartOver, "CheckIfStartOver" }, + { ActionType::Deploy, "ResetTimer" }, }; json::value info = basic_info_with_what("CopilotAction"); - info["details"] |= json::object { - { "action", ActionNames.at(action.type) }, - { "target", action.name }, - { "doc", action.doc }, - { "doc_color", action.doc_color }, - }; + info["details"] |= json::object { { "action", ActionNames.at(action.type) }, + { "target", action.name }, + { "doc", action.doc }, + { "doc_color", action.doc_color }, + { "elapsed_time", elapsed_time() } }; callback(AsstMsg::SubTaskExtraInfo, info); } @@ -392,6 +399,25 @@ bool asst::BattleProcessTask::wait_condition(const Action& action) } } + // 等待全局计时器 + if (action.elapsed_time > 0) { + if (m_timer_enabled) { + update_image_if_empty(); + while (!need_exit()) { + if (elapsed_time() >= action.elapsed_time) { + break; + } + if (!check_in_battle(image)) { + return false; + } + do_strategy_and_update_image(); + } + } + else { + Log.warn(__FUNCTION__, "| Timer not enabled. Reset required before use."); + } + } + // 部署干员还要额外等待费用够或 CD 转好 if (action.type == ActionType::Deploy) { const std::string& name = get_name_from_group(action.name); diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index d49277e7cf..7d0af649c8 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -1814,6 +1814,12 @@ public class AsstProxy LocalizationHelper.GetString(actionString), DataHelper.GetLocalizedCharacterName(target) ?? target)); + var elapsed_time_str = subTaskDetails!["elapsed_time"]?.ToString(); + if (int.TryParse(elapsed_time_str, out int elapsed_time_int) && elapsed_time_int >= 0) + { + Instances.CopilotViewModel.AddLog(string.Format(LocalizationHelper.GetString("ElapsedTime"), elapsed_time_int), UiLogColor.Message); + } + break; } diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 07b88be943..8d44cabd44 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -1057,6 +1057,7 @@ Right-click to clear inactive tasks {key=Operator} unavailable: {0}, reason: {1} Module unavailable Step: {0} {1} + 当前计时器: {0}ms Deploy Use Skill Retreat diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 60a412d313..4866e6acbf 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -1057,6 +1057,7 @@ C:\\leidian\\LDPlayer9 {key=Operator}が使用できません: {0}、理由: {1} モジュールが使用できません 現在の手順: {0} {1} + 当前计时器: {0}ms 配置 スキル使用 撤退 diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 95a3e3cf61..bc23f4d784 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -1057,6 +1057,7 @@ C:\\leidian\\LDPlayer9 사용 불가 {key=Operator}: {0}, 사유: {1} 모듈 사용 불가 현재 단계: {0} {1} + 当前计时器: {0}ms 배치 스킬 사용 철수 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index b09fef14ea..4f4d17b01c 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -1057,6 +1057,7 @@ C:\\leidian\\LDPlayer9。\n {key=Operator}不可用: {0}, 原因: {1} 模组不可用 当前步骤: {0} {1} + 当前计时器: {0}ms 部署 使用技能 撤退 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index f833d563a3..546be7d30d 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -1055,6 +1055,7 @@ C:\\leidian\\LDPlayer9。\n {key=Operator}不可用: {0}, 原因: {1} 模組不可用 目前步驟: {0} {1} + 当前计时器: {0}ms 部署 使用技能 撤退