From d7250628e1fabe50ea204d1bdc857fdfe9bd6b23 Mon Sep 17 00:00:00 2001 From: Evoltsuki <67886200+Evoltsuki@users.noreply.github.com> Date: Fri, 15 May 2026 12:19:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=B2=99=E7=9B=98?= =?UTF-8?q?=E6=8E=A8=E6=BC=94=E4=BD=9C=E4=B8=9A=E6=94=AF=E6=8C=81=20(#1657?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增沙盘推演相关配置字段与识别逻辑,添加沙盘选项弹窗、确认等任务配置,支持设置两个沙盘选项参数,适配单多编队作业的沙盘模式调用 --- resource/tasks/tasks.json | 53 ++++++++++++++++++ resource/template/SandboxOptionToggle.png | Bin 0 -> 3014 bytes src/MaaCore/Common/AsstBattleDef.h | 3 + .../Config/Miscellaneous/CopilotConfig.cpp | 4 ++ src/MaaCore/Task/Interface/CopilotTask.cpp | 31 +++++++++- src/MaaCore/Task/Interface/CopilotTask.h | 19 +++++-- .../Miscellaneous/MultiCopilotTaskPlugin.cpp | 19 ++++++- .../Miscellaneous/MultiCopilotTaskPlugin.h | 3 + src/MaaWpfGui/Models/Copilot/CopilotModel.cs | 6 ++ .../ViewModels/UI/CopilotViewModel.cs | 4 +- 10 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 resource/template/SandboxOptionToggle.png diff --git a/resource/tasks/tasks.json b/resource/tasks/tasks.json index 77236fb18f..b94d80fb04 100644 --- a/resource/tasks/tasks.json +++ b/resource/tasks/tasks.json @@ -389,6 +389,59 @@ "template": ["RaidDifficulty.png", "RaidDifficulty-Chapter15.png"], "next": [] }, + "SandboxOptionToggle": { + "doc": "沙盘推演选项弹窗按钮", + "template": "SandboxOptionToggle.png", + "action": "ClickSelf", + "roi": [1131, 123, 149, 148], + "preDelay": 500, + "next": [] + }, + "SandboxButton": { + "doc": "沙盘推演按钮", + "algorithm": "OcrDetect", + "action": "ClickSelf", + "text": ["沙盘推演"], + "roi": [910, 128, 244, 138], + "maxTimes": 3, + "next": ["SandboxOptionToggle", "#self"] + }, + "SandboxOption1_1": { + "doc": "沙盘选项1-1", + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [63, 263, 574, 128], + "preDelay": 1000 + }, + "SandboxOption1_2": { + "doc": "沙盘选项1-2", + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [643, 264, 580, 132], + "preDelay": 1000 + }, + "SandboxOption2_1": { + "doc": "沙盘选项2-1", + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [58, 463, 582, 130], + "preDelay": 500 + }, + "SandboxOption2_2": { + "doc": "沙盘选项2-2", + "algorithm": "JustReturn", + "action": "ClickRect", + "specificRect": [641, 466, 580, 126], + "preDelay": 500 + }, + "SandboxConfirm": { + "doc": "沙盘推演确定按钮", + "algorithm": "OcrDetect", + "action": "ClickSelf", + "text": ["确定"], + "roi": [939, 570, 333, 150], + "preDelay": 500 + }, "EpisodeNew": { "Doc": "base_task", "algorithm": "JustReturn", diff --git a/resource/template/SandboxOptionToggle.png b/resource/template/SandboxOptionToggle.png new file mode 100644 index 0000000000000000000000000000000000000000..b660ef6eefad38bc2d7bd047c084a05df071b36b GIT binary patch literal 3014 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 IM6N<$f+X(CHUIzs literal 0 HcmV?d00001 diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index d6bb2e8b7e..54d4d22750 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -441,6 +441,9 @@ struct BasicInfo std::string title_color; std::string details; std::string details_color; + bool is_sandbox = false; + int sandbox_option1 = 1; + int sandbox_option2 = 1; }; struct CombatData // 作业 JSON 数据 diff --git a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp index 8fc295ebd2..3a1ebf4917 100644 --- a/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp +++ b/src/MaaCore/Config/Miscellaneous/CopilotConfig.cpp @@ -40,6 +40,10 @@ asst::battle::copilot::BasicInfo asst::CopilotConfig::parse_basic_info(const jso info.details = json.get("doc", "details", std::string()); info.details_color = json.get("doc", "details_color", std::string()); + info.is_sandbox = json.get("is_sandbox", false); + info.sandbox_option1 = json.get("sandbox_option1", 1); + info.sandbox_option2 = json.get("sandbox_option2", 1); + return info; } diff --git a/src/MaaCore/Task/Interface/CopilotTask.cpp b/src/MaaCore/Task/Interface/CopilotTask.cpp index e6227320e6..1b16cf1784 100644 --- a/src/MaaCore/Task/Interface/CopilotTask.cpp +++ b/src/MaaCore/Task/Interface/CopilotTask.cpp @@ -1,5 +1,7 @@ #include "CopilotTask.h" +#include + #include "Arknights-Tile-Pos/TileCalc2.hpp" #include "Config/Miscellaneous/BattleDataConfig.h" @@ -90,13 +92,37 @@ bool asst::CopilotTask::set_params(const json::value& params) Log.error("Not support stage"); return false; } + if (Copilot.get_data().info.is_sandbox) { + int opt1 = std::clamp(Copilot.get_data().info.sandbox_option1, 1, 2); + int opt2 = std::clamp(Copilot.get_data().info.sandbox_option2, 1, 2); + m_sandbox_tasks.clear(); + auto raid_tp = std::make_shared(m_callback, inst(), TaskType); + raid_tp->set_tasks({ "RaidConfirm", "ChangeToRaidDifficulty" }).set_retry_times(20); + m_sandbox_tasks.emplace_back(raid_tp); + auto sandbox_entry_tp = std::make_shared(m_callback, inst(), TaskType); + sandbox_entry_tp->set_tasks({ "SandboxOptionToggle", "SandboxButton" }).set_retry_times(20); + m_sandbox_tasks.emplace_back(sandbox_entry_tp); + std::vector sandbox_steps = { "SandboxOption1_" + std::to_string(opt1), + "SandboxOption2_" + std::to_string(opt2), + "SandboxConfirm" }; + for (const auto& task_name : sandbox_steps) { + auto tp = std::make_shared(m_callback, inst(), TaskType); + tp->set_tasks({ task_name }).set_retry_times(20); + m_sandbox_tasks.emplace_back(tp); + } + size_t insert_pos = 1; + for (const auto& tp : m_sandbox_tasks) { + m_subtasks.insert(m_subtasks.begin() + insert_pos, tp); + ++insert_pos; + } + } } else if (multi_tasks_opt) { m_multi_copilot_plugin_ptr->set_enable(true); // 启用多任务插件, 自动覆盖Copilot中的配置 m_battle_task_ptr->set_wait_until_end(true); auto configs = static_cast>(*multi_tasks_opt); std::vector configs_cvt; - for (const auto& [id, filename, stage_name, is_raid] : configs) { + for (const auto& [id, filename, stage_name, is_raid, is_sandbox, sandbox_option1, sandbox_option2] : configs) { MultiCopilotTaskPlugin::MultiCopilotConfig config_cvt; auto copilot_opt = parse_copilot_filename(filename); if (!copilot_opt) { @@ -110,6 +136,9 @@ bool asst::CopilotTask::set_params(const json::value& params) config_cvt.nav_name = stage_name; config_cvt.is_raid = is_raid; config_cvt.id = id; // ID 从0开始 + config_cvt.is_sandbox = is_sandbox; + config_cvt.sandbox_option1 = sandbox_option1; + config_cvt.sandbox_option2 = sandbox_option2; configs_cvt.emplace_back(std::move(config_cvt)); } diff --git a/src/MaaCore/Task/Interface/CopilotTask.h b/src/MaaCore/Task/Interface/CopilotTask.h index 579d01c220..535a39771b 100644 --- a/src/MaaCore/Task/Interface/CopilotTask.h +++ b/src/MaaCore/Task/Interface/CopilotTask.h @@ -19,11 +19,21 @@ public: struct MultiCopilotConfig { int id = -1; - std::string filename; // 文件名 - std::string stage_name; // 关卡名 - bool is_raid = false; // 是否是突袭 + std::string filename; // 文件名 + std::string stage_name; // 关卡名 + bool is_raid = false; // 是否是突袭 + bool is_sandbox = false; // 是否是沙盘推演 + int sandbox_option1 = 1; // 沙盘选项1 (1 or 2) + int sandbox_option2 = 1; // 沙盘选项2 (1 or 2) - MEO_JSONIZATION(MEO_OPT id, filename, stage_name, MEO_OPT is_raid); + MEO_JSONIZATION( + MEO_OPT id, + filename, + stage_name, + MEO_OPT is_raid, + MEO_OPT is_sandbox, + MEO_OPT sandbox_option1, + MEO_OPT sandbox_option2); }; public: @@ -40,6 +50,7 @@ private: std::optional parse_copilot_filename(const std::string& name); std::shared_ptr m_multi_copilot_plugin_ptr = nullptr; + std::vector> m_sandbox_tasks; std::shared_ptr m_medicine_task_ptr = nullptr; std::shared_ptr m_formation_task_ptr = nullptr; std::shared_ptr m_battle_task_ptr = nullptr; diff --git a/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.cpp b/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.cpp index 645bdc3c50..fec26875e8 100644 --- a/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.cpp +++ b/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.cpp @@ -1,6 +1,7 @@ #include "MultiCopilotTaskPlugin.h" #include +#include #include "Config/GeneralConfig.h" #include "Config/Miscellaneous/CopilotConfig.h" @@ -47,10 +48,24 @@ bool asst::MultiCopilotTaskPlugin::_run() ret = ret && navigate_to_stage(config.nav_name); ProcessTask(*this, { "NotUsePrts" }).set_ignore_error(true).set_retry_times(0).run(); - if (config.is_raid) { - // 选择突袭模式 + + bool is_sandbox = config.is_sandbox || Copilot.get_data().info.is_sandbox; + int sandbox_option1 = + std::clamp(config.is_sandbox ? config.sandbox_option1 : Copilot.get_data().info.sandbox_option1, 1, 2); + int sandbox_option2 = + std::clamp(config.is_sandbox ? config.sandbox_option2 : Copilot.get_data().info.sandbox_option2, 1, 2); + + if (config.is_raid || is_sandbox) { ret = ret && ProcessTask(*this, { "RaidConfirm", "ChangeToRaidDifficulty" }).set_retry_times(20).run(); } + if (ret && is_sandbox) { + ret = ret && ProcessTask(*this, { "SandboxOptionToggle", "SandboxButton" }).set_retry_times(20).run(); + std::string option1_task = "SandboxOption1_" + std::to_string(sandbox_option1); + ret = ret && ProcessTask(*this, { option1_task }).set_retry_times(3).run(); + std::string option2_task = "SandboxOption2_" + std::to_string(sandbox_option2); + ret = ret && ProcessTask(*this, { option2_task }).set_retry_times(3).run(); + ret = ret && ProcessTask(*this, { "SandboxConfirm" }).set_retry_times(20).run(); + } return ret; } diff --git a/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.h b/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.h index b657337364..76e60bc131 100644 --- a/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.h +++ b/src/MaaCore/Task/Miscellaneous/MultiCopilotTaskPlugin.h @@ -19,6 +19,9 @@ public: std::string nav_name; // 关卡名 bool is_raid = false; // 是否是突袭 int id; + bool is_sandbox = false; // 是否是沙盘推演 + int sandbox_option1 = 1; // 沙盘选项1 (1 or 2) + int sandbox_option2 = 1; // 沙盘选项2 (1 or 2) }; public: diff --git a/src/MaaWpfGui/Models/Copilot/CopilotModel.cs b/src/MaaWpfGui/Models/Copilot/CopilotModel.cs index fb5a39b8c9..6cedc77bca 100644 --- a/src/MaaWpfGui/Models/Copilot/CopilotModel.cs +++ b/src/MaaWpfGui/Models/Copilot/CopilotModel.cs @@ -47,6 +47,12 @@ public class CopilotModel : CopilotBase [JsonProperty("difficulty")] public DifficultyFlags Difficulty { get; set; } + /// + /// Gets or sets a value indicating whether 沙盘推演模式 + /// + [JsonProperty("is_sandbox")] + public bool IsSandbox { get; set; } + public List<(string Output, string? Color)> Output() { var output = new List<(string, string?)>(); diff --git a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs index 7b106f37ab..b4b33b9ad3 100644 --- a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs @@ -1721,13 +1721,13 @@ public partial class CopilotViewModel : Screen } else { - if (flags.HasFlag(CopilotModel.DifficultyFlags.Normal)) + if (flags.HasFlag(CopilotModel.DifficultyFlags.Normal) && !copilot.IsSandbox) { var item = new CopilotItemViewModel(stageCode, cachePath, false, copilotId) { Index = CopilotItemViewModels.Count, }; CopilotItemViewModels.Add(item); } - if (flags.HasFlag(CopilotModel.DifficultyFlags.Raid)) + if (flags.HasFlag(CopilotModel.DifficultyFlags.Raid) || copilot.IsSandbox) { var item = new CopilotItemViewModel(stageCode, cachePath, true, copilotId) { Index = CopilotItemViewModels.Count, }; CopilotItemViewModels.Add(item);