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 0000000000..b660ef6eef Binary files /dev/null and b/resource/template/SandboxOptionToggle.png differ 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);