diff --git a/resource/tasks.json b/resource/tasks.json index d62dd92e06..e869d21ff0 100644 --- a/resource/tasks.json +++ b/resource/tasks.json @@ -610,6 +610,27 @@ 50 ] }, + "ChangeToAdverseDifficulty": { + "template": "AdverseDifficulty.png", + "action": "ClickSelf", + "roi": [ + 625, + 640, + 80, + 65 + ], + "maxTimes": 3, + "next": [ + "AdverseConfirm", + "#self" + ] + }, + "AdverseConfirm": { + "baseTask": "ChangeToAdverseDifficulty", + "action": "DoNothing", + "template": "NormalDifficulty.png", + "next": [] + }, "Episode": { "Doc": "base_task", "algorithm": "JustReturn", @@ -7021,6 +7042,12 @@ "Stop" ] }, + "BattleQuickFormationCheck": { + "baseTask": "BattleQuickFormation", + "template": "BattleQuickFormation.png", + "action": "DoNothing", + "next": [] + }, "BattleQuickFormation": { "action": "ClickSelf", "roi": [ @@ -13097,6 +13124,16 @@ "baseTask": "BattleStartPre", "action": "DoNothing" }, + "Copilot@BattleStartPreFlag": { + "baseTask": "BattleStartPre", + "action": "DoNothing", + "roi":[ + 0, + 0, + 1280, + 720 + ] + }, "BattleFormationOCRNameFlag": { "templThreshold": 0.6, "roi": [ diff --git a/resource/template/AdverseDifficulty.png b/resource/template/AdverseDifficulty.png new file mode 100644 index 0000000000..56b49bfa60 Binary files /dev/null and b/resource/template/AdverseDifficulty.png differ diff --git a/resource/template/NormalDifficulty.png b/resource/template/NormalDifficulty.png new file mode 100644 index 0000000000..beeae97437 Binary files /dev/null and b/resource/template/NormalDifficulty.png differ diff --git a/src/MaaCore/MaaCore.vcxproj b/src/MaaCore/MaaCore.vcxproj index 4d63e1a6a0..c1e125718e 100644 --- a/src/MaaCore/MaaCore.vcxproj +++ b/src/MaaCore/MaaCore.vcxproj @@ -73,8 +73,10 @@ + + @@ -234,8 +236,10 @@ + + diff --git a/src/MaaCore/MaaCore.vcxproj.filters b/src/MaaCore/MaaCore.vcxproj.filters index f3a8ed3d92..ae11bef9b1 100644 --- a/src/MaaCore/MaaCore.vcxproj.filters +++ b/src/MaaCore/MaaCore.vcxproj.filters @@ -633,6 +633,12 @@ 源文件\Task\Infrast + + 源文件\Task\Miscellaneous + + + 源文件\Task\Miscellaneous + @@ -1049,5 +1055,11 @@ 源文件\Task\Infrast + + 源文件\Task\Miscellaneous + + + 源文件\Task\Miscellaneous + \ No newline at end of file diff --git a/src/MaaCore/Task/Interface/CopilotTask.cpp b/src/MaaCore/Task/Interface/CopilotTask.cpp index 2fffa38a47..42a5b45590 100644 --- a/src/MaaCore/Task/Interface/CopilotTask.cpp +++ b/src/MaaCore/Task/Interface/CopilotTask.cpp @@ -3,33 +3,48 @@ #include #include "Config/Miscellaneous/CopilotConfig.h" +#include "Config/TaskData.h" #include "Task/Miscellaneous/BattleFormationTask.h" #include "Task/Miscellaneous/BattleProcessTask.h" +#include "Task/Miscellaneous/CopilotListNotificationPlugin.h" +#include "Task/Miscellaneous/TaskFileReloadTask.h" #include "Task/ProcessTask.h" #include "Utils/Logger.hpp" #include "Utils/Platform.hpp" asst::CopilotTask::CopilotTask(const AsstCallback& callback, Assistant* inst) : InterfaceTask(callback, inst, TaskType), + m_task_file_reload_task_ptr(std::make_shared(callback, inst, TaskType)), + m_navigate_task_ptr(std::make_shared(callback, inst, TaskType)), + m_not_use_prts_task_ptr(std::make_shared(callback, inst, TaskType)), + m_adverse_select_task_ptr(std::make_shared(callback, inst, TaskType)), m_formation_task_ptr(std::make_shared(callback, inst, TaskType)), m_battle_task_ptr(std::make_shared(callback, inst, TaskType)), m_stop_task_ptr(std::make_shared(callback, inst, TaskType)) { LogTraceFunction; + m_subtasks.emplace_back(m_task_file_reload_task_ptr); + m_subtasks.emplace_back(m_navigate_task_ptr); + m_subtasks.emplace_back(m_not_use_prts_task_ptr); + + // 选择突袭模式 + m_adverse_select_task_ptr->set_tasks({ "ChangeToAdverseDifficulty", "AdverseConfirm" }); + m_subtasks.emplace_back(m_adverse_select_task_ptr); + auto start_1_tp = std::make_shared(callback, inst, TaskType); start_1_tp->set_tasks({ "BattleStartPre" }).set_retry_times(0).set_ignore_error(true); m_subtasks.emplace_back(start_1_tp); - m_subtasks.emplace_back(m_formation_task_ptr); + m_subtasks.emplace_back(m_formation_task_ptr)->set_retry_times(0); auto start_2_tp = std::make_shared(callback, inst, TaskType); start_2_tp->set_tasks({ "BattleStartAll" }).set_ignore_error(false); + m_copilot_list_notification_ptr = start_2_tp->register_plugin(); m_subtasks.emplace_back(start_2_tp); m_subtasks.emplace_back(m_battle_task_ptr)->set_retry_times(0); - m_stop_task_ptr->set_tasks({ "ClickCornerUntilStartButton" }); m_stop_task_ptr->set_enable(false); m_subtasks.emplace_back(m_stop_task_ptr); } @@ -56,10 +71,14 @@ bool asst::CopilotTask::set_params(const json::value& params) Log.error("CopilotConfig parse failed"); return false; } + m_task_file_reload_task_ptr->set_magic_code(match[1].str()); } - else if (!Copilot.load(utils::path(*filename_opt))) { - Log.error("CopilotConfig parse failed"); - return false; + else { + if (!Copilot.load(utils::path(*filename_opt))) { + Log.error("CopilotConfig parse failed"); + return false; + } + m_task_file_reload_task_ptr->set_path(std::move(*filename_opt)); } m_stage_name = Copilot.get_stage_name(); @@ -70,6 +89,8 @@ bool asst::CopilotTask::set_params(const json::value& params) bool with_formation = params.get("formation", false); m_formation_task_ptr->set_enable(with_formation); + + // 自动补信赖 m_formation_task_ptr->set_add_trust(params.get("add_trust", false)); m_formation_task_ptr->set_add_user_additional(params.get("add_user_additional", false)); auto user_additional_opt = params.find("user_additional"); @@ -88,21 +109,46 @@ bool asst::CopilotTask::set_params(const json::value& params) m_formation_task_ptr->set_user_additional(std::move(user_additional)); } + // 是否在当前页面左右滑动寻找关卡 + // 启用队列的话,这项为true + bool need_navigate = params.get("need_navigate", false); + m_task_file_reload_task_ptr->set_enable(need_navigate); + + // 不使用代理指挥 + m_not_use_prts_task_ptr->set_enable(need_navigate && with_formation); + m_not_use_prts_task_ptr->set_tasks({ "NotUsePrts", "Stop" }); + + bool is_adverse = params.get("is_adverse", false); + m_adverse_select_task_ptr->set_enable(need_navigate && is_adverse); + + // 防止在关卡名展开的情况下无法滑动,调整滑动区域 + std::string m_navigate_name = params.get("navigate_name", std::string()); + Task.get(m_navigate_name + "@Copilot@ClickStageName")->text = { m_navigate_name }; + Task.get(m_navigate_name + "@Copilot@ClickedCorrectStage")->text = { m_navigate_name }; + Task.get(m_navigate_name + "@Copilot@FullStageNavigation")->specific_rect = Rect(600, 100, 20, 20); + m_navigate_task_ptr->set_tasks({ m_navigate_name + "@Copilot@StageNavigationBegin" }); + m_navigate_task_ptr->set_enable(need_navigate); + std::string support_unit_name = params.get("support_unit_name", std::string()); m_formation_task_ptr->set_support_unit_name(std::move(support_unit_name)); size_t loop_times = params.get("loop_times", 1); if (loop_times > 1) { + m_stop_task_ptr->set_tasks({ "ClickCornerUntilStartButton" }); + m_stop_task_ptr->set_enable(true); + + // 追加 m_subtasks.reserve(m_subtasks.size() * loop_times); auto raw_end = m_subtasks.end(); for (size_t i = 1; i < loop_times; ++i) { // FIXME: 如果多次调用 set_params,这里复制的会有问题 m_subtasks.insert(m_subtasks.end(), m_subtasks.begin(), raw_end); } - m_stop_task_ptr->set_enable(true); } - else { - m_stop_task_ptr->set_enable(false); + else if (need_navigate) { + Task.get("Copilot@BattleStartPreFlag")->text.emplace_back(m_navigate_name); + m_stop_task_ptr->set_tasks({ "Copilot@ClickCornerUntilStartButton" }); + m_stop_task_ptr->set_enable(true); } return true; diff --git a/src/MaaCore/Task/Interface/CopilotTask.h b/src/MaaCore/Task/Interface/CopilotTask.h index 323fcb9a46..d7335e55c3 100644 --- a/src/MaaCore/Task/Interface/CopilotTask.h +++ b/src/MaaCore/Task/Interface/CopilotTask.h @@ -5,8 +5,10 @@ namespace asst { + class TaskFileReloadTask; class BattleProcessTask; class BattleFormationTask; + class CopilotListNotificationPlugin; class ProcessTask; // 抄作业任务 @@ -22,8 +24,13 @@ namespace asst std::string get_stage_name() const { return m_stage_name; } private: + std::shared_ptr m_task_file_reload_task_ptr = nullptr; + std::shared_ptr m_navigate_task_ptr = nullptr; + std::shared_ptr m_not_use_prts_task_ptr = nullptr; + std::shared_ptr m_adverse_select_task_ptr = nullptr; std::shared_ptr m_formation_task_ptr = nullptr; std::shared_ptr m_battle_task_ptr = nullptr; + std::shared_ptr m_copilot_list_notification_ptr = nullptr; std::shared_ptr m_stop_task_ptr = nullptr; std::string m_stage_name; }; diff --git a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h index 26ea9d1c88..ca3b07a307 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h @@ -25,8 +25,11 @@ namespace asst void append_additional_formation(AdditionalFormation formation); void set_support_unit_name(std::string name); + // 是否追加自定干员 void set_add_user_additional(bool add_user_additional); + // 设置追加自定干员列表 void set_user_additional(std::vector> user_additional); + // 是否追加低信赖干员 void set_add_trust(bool add_trust); enum class DataResource @@ -45,7 +48,6 @@ namespace asst bool add_additional(); // 补充刷信赖的干员,从最小的开始 bool add_trust_operators(); - bool enter_selection_page(); bool select_opers_in_cur_page(std::vector& groups); void swipe_page(); diff --git a/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.cpp b/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.cpp new file mode 100644 index 0000000000..011f40381c --- /dev/null +++ b/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.cpp @@ -0,0 +1,24 @@ +#include "CopilotListNotificationPlugin.h" + +bool asst::CopilotListNotificationPlugin::verify(AsstMsg msg, const json::value& details) const +{ + if (msg != AsstMsg::SubTaskStart || details.get("subtask", std::string()) != "ProcessTask") { + return false; + } + + const std::string task = details.at("details").at("task").as_string(); + if (task != "BattleStartAll" && task.starts_with("BattleStart")) { + return true; + } + else { + return false; + } +} + +bool asst::CopilotListNotificationPlugin::_run() +{ + json::value info = basic_info_with_what("CopilotListEnterSuccess"); + callback(AsstMsg::SubTaskExtraInfo, info); + + return true; +} diff --git a/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.h b/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.h new file mode 100644 index 0000000000..6c47221d00 --- /dev/null +++ b/src/MaaCore/Task/Miscellaneous/CopilotListNotificationPlugin.h @@ -0,0 +1,16 @@ +#pragma once +#include "Task/AbstractTaskPlugin.h" + +namespace asst +{ + class CopilotListNotificationPlugin : public AbstractTaskPlugin + { + public: + using AbstractTaskPlugin::AbstractTaskPlugin; + virtual ~CopilotListNotificationPlugin() override = default; + virtual bool verify(AsstMsg msg, const json::value& details) const override; + + private: + virtual bool _run() override; + }; +} diff --git a/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.cpp b/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.cpp new file mode 100644 index 0000000000..ee49794fbf --- /dev/null +++ b/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.cpp @@ -0,0 +1,40 @@ +#include "TaskFileReloadTask.h" + +#include "Config/Miscellaneous/CopilotConfig.h" +#include "Utils/Logger.hpp" + +void asst::TaskFileReloadTask::set_magic_code(std::string code) +{ + is_magic_code = true; + m_file = std::move(code); +} + +void asst::TaskFileReloadTask::set_path(std::string path) +{ + is_path = true; + m_file = std::move(path); +} + +bool asst::TaskFileReloadTask::_run() +{ + if (is_magic_code) { + if (!Copilot.parse_magic_code(m_file)) { + Log.error("CopilotConfig parse failed"); + return false; + } + } + else if (is_path) { + if (!Copilot.load(utils::path(m_file))) { + Log.error("CopilotConfig parse failed"); + return false; + } + } + else { + return false; + } + + json::value info = basic_info_with_what("CopilotListLoadTaskFileSuccess"); + info["details"]["stage_name"] = Copilot.get_stage_name(); + callback(AsstMsg::SubTaskExtraInfo, info); + return true; +} diff --git a/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.h b/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.h new file mode 100644 index 0000000000..695f536451 --- /dev/null +++ b/src/MaaCore/Task/Miscellaneous/TaskFileReloadTask.h @@ -0,0 +1,19 @@ +#pragma once +#include "Task/AbstractTaskPlugin.h" + +namespace asst +{ + class TaskFileReloadTask : public AbstractTask + { + public: + using AbstractTask::AbstractTask; + virtual ~TaskFileReloadTask() override = default; + void set_magic_code(std::string code); + void set_path(std::string path); + + private: + virtual bool _run() override; + std::string m_file; + bool is_path = false, is_magic_code = false; + }; +} diff --git a/src/MaaWpfGui/Constants/ConfigurationKeys.cs b/src/MaaWpfGui/Constants/ConfigurationKeys.cs index 4e77ed7760..64def5a771 100644 --- a/src/MaaWpfGui/Constants/ConfigurationKeys.cs +++ b/src/MaaWpfGui/Constants/ConfigurationKeys.cs @@ -127,6 +127,7 @@ namespace MaaWpfGui.Constants public const string CopilotAddUserAdditional = "Copilot.AddUserAdditional"; public const string CopilotUserAdditional = "Copilot.UserAdditional"; public const string CopilotLoopTimes = "Copilot.LoopTimes"; + public const string CopilotTaskList = "Copilot.CopilotTaskList"; public const string UpdateProxy = "VersionUpdate.Proxy"; public const string VersionType = "VersionUpdate.VersionType"; diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index db8e830004..a4dbcfb686 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -15,15 +15,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Imaging; using HandyControl.Data; -using HandyControl.Tools.Extension; using MaaWpfGui.Constants; using MaaWpfGui.Extensions; using MaaWpfGui.Helper; @@ -573,7 +572,11 @@ namespace MaaWpfGui.Main Instances.TaskQueueViewModel.AddLog(LocalizationHelper.GetString("CompleteTask") + taskChain); if (isCoplitTaskChain) { - runningState.SetIdle(true); + if (!Instances.CopilotViewModel.UseCopilotList || Instances.CopilotViewModel.CopilotItemViewModels.All(model => !model.IsChecked)) + { + runningState.SetIdle(true); + } + Instances.CopilotViewModel.AddLog(LocalizationHelper.GetString("CompleteCombat"), UiLogColor.Info); } @@ -1058,6 +1061,12 @@ namespace MaaWpfGui.Main } break; + case "CopilotListLoadTaskFileSuccess": + Instances.CopilotViewModel.AddLog("Parse " + subTaskDetails["stage_name"].ToString() + " Success"); + break; + case "CopilotListEnterSuccess": + Instances.CopilotViewModel.EnterCopilotTask(); + break; case "SSSStage": { @@ -1832,35 +1841,37 @@ namespace MaaWpfGui.Main /// 是否追加信赖干员 /// 是否追加自定干员 /// 自定干员列表 + /// 是否导航至关卡(启用自动战斗序列) + /// 关卡名 + /// 是不是突袭 /// 任务类型 /// 任务重复执行次数 + /// 是否启动战斗 /// 是否成功。 - public bool AsstStartCopilot(string filename, bool formation, bool add_trust, bool add_user_additional, string user_additional, string type, int loop_times) + public bool AsstStartCopilot(string filename, bool formation, bool add_trust, bool add_user_additional, JArray user_additional, bool need_navigate, string navigate_name, bool is_adverse, string type, int loop_times, bool asst_start = true) { - JArray m_user_additional = new JArray(); - Regex regex = new Regex(@"(?<=;)(?[^,;]+)(?:, *(?\d))? *", RegexOptions.Compiled); - MatchCollection matches = regex.Matches(";" + user_additional); - foreach (Match match in matches) - { - m_user_additional.Add(new JObject - { - ["name"] = match.Groups[1].Value.Trim(), - ["skill"] = match.Groups[2].Value.IsNullOrEmpty() ? 0 : int.Parse(match.Groups[2].Value), - }); - } - var task_params = new JObject { ["filename"] = filename, ["formation"] = formation, ["add_trust"] = add_trust, ["add_user_additional"] = add_user_additional, - ["user_additional"] = m_user_additional, + ["user_additional"] = user_additional, + ["need_navigate"] = need_navigate, + ["navigate_name"] = navigate_name, + ["is_adverse"] = is_adverse, ["loop_times"] = loop_times, }; AsstTaskId id = AsstAppendTaskWithEncoding(type, task_params); _latestTaskId[TaskType.Copilot] = id; - return id != 0 && AsstStart(); + if (asst_start) + { + return id != 0 && AsstStart(); + } + else + { + return id != 0; + } } public bool AsstStartVideoRec(string filename) diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index c816728f6c..47412f8525 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -446,6 +446,8 @@ The video aspect ratio needs to be 16:9 without interference factors such as bla Add low-trust operators Add custom operators Use ";" as the separator, "," to separate the operator name and skills, for example: W, 3; Ash, 2 + Battle list (Please switch to the level selection page before starting) + Right click to add raid difficulty Loop Times Start Video Link diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 10fb166a69..b6d39ad072 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -438,6 +438,8 @@ Bilibili: ログイン インターフェイスに表示されるアカウント 信頼性の低いオペレーターを追加する カスタム戦術要員の追加 使用「;」オペレータ名とスキルを区切るには、区切り文字として「,」を使用します。例: W, 3; Ash, 2 + バトルリスト(開始前にレベル選択ページに切り替えてください) + 右クリックしてレイドの難易度を追加します ループ回数 開始 映像へのハイパーリンク diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index d727e678da..7e9bd0f9ef 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -450,6 +450,8 @@ Bilibili: 로그인 인터페이스에 표시되는 계정 이름(예: Zhang San 신뢰도가 낮은 운영자 추가 맞춤 연산자 추가 사용 ";" 운영자 이름과 기술을 구분하기 위해 구분 기호 ","를 사용합니다. 예: W, 3; Ash, 2 + 전투 목록(시작하기 전에 레벨 선택 페이지로 전환하세요) + 공격대 난이도를 추가하려면 마우스 오른쪽 버튼을 클릭하세요. 반복 횟수 시작 영상 링크 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 65075a6c86..6a8415acce 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -468,6 +468,8 @@ 追加自定干员 以英文";"为分隔符,英文","分隔干员名与技能,例: 史尔特尔,3;艾雅法拉,1 循环次数 + 战斗列表 (开始前请切换至关卡选择页面) + 右键添加突袭难度 开始 视频链接 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index ecff9cc6ed..2eaf730d22 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -448,6 +448,8 @@ 補充低信賴幹員 追加自定幹員 使用";"作為分隔符,用","分隔操作員姓名和技能,例:史尔特尔,3;艾雅法拉,1 + 戰鬥列表 (開始前請切換至關卡選擇頁面) + 右鍵添加突襲難度 循環次數 開始 影片連結 diff --git a/src/MaaWpfGui/ViewModels/CopilotItemViewModel.cs b/src/MaaWpfGui/ViewModels/CopilotItemViewModel.cs new file mode 100644 index 0000000000..8c974070e3 --- /dev/null +++ b/src/MaaWpfGui/ViewModels/CopilotItemViewModel.cs @@ -0,0 +1,104 @@ +using System.Security.RightsManagement; +using Stylet; +using Vanara.PInvoke; + +namespace MaaWpfGui.ViewModels +{ + public class CopilotItemViewModel : PropertyChangedBase + { + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The original Name of file + public CopilotItemViewModel(string name, string filePath) + { + Name = name; + FilePath = filePath; + IsChecked = true; + } + + private string _filePath; + + /// + /// Gets or sets the original_name. + /// + public string FilePath + { + get => _filePath; + set => SetAndNotify(ref _filePath, value); + } + + private string _name; + + /// + /// Gets or sets the name. + /// + public string Name + { + get => _name; + set => SetAndNotify(ref _name, value); + } + + private bool _isChecked; + + /// + /// Gets or sets a value indicating whether gets or sets whether the key is checked. + /// + public bool IsChecked + { + get => _isChecked; + set + { + SetAndNotify(ref _isChecked, value); + } + } + + // 换成图标的话要这个,暂时没用 + private string _iconPath; + + /// + /// Gets or sets the icon path. + /// + // ReSharper disable once UnusedMember.Global + public string IconPath + { + get => _iconPath; + set => SetAndNotify(ref _iconPath, value); + } + + private string _token; + + /// + /// Gets or sets the token. + /// + // ReSharper disable once UnusedMember.Global + public string Token + { + get => _token; + set => SetAndNotify(ref _token, value); + } + + private string _runStatus; + + /// + /// Gets or sets the running status. + /// + // ReSharper disable once UnusedMember.Global + public string RunStatus + { + get => _runStatus; + set => SetAndNotify(ref _runStatus, value); + } + + private int _index = 0; + + public int Index + { + get => _index; + set => SetAndNotify(ref _index, value); + } + + public int GetIndex() => Index; + } +} diff --git a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs index c03cfd9bd8..2a01e55948 100644 --- a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs @@ -21,14 +21,17 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using HandyControl.Tools.Extension; using MaaWpfGui.Constants; using MaaWpfGui.Helper; using MaaWpfGui.States; using Microsoft.Win32; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Serilog; using Stylet; using DataFormats = System.Windows.Forms.DataFormats; +using Task = System.Threading.Tasks.Task; namespace MaaWpfGui.ViewModels.UI { @@ -46,6 +49,11 @@ namespace MaaWpfGui.ViewModels.UI /// public ObservableCollection LogItemViewModels { get; } + /// + /// Gets or private sets the view models of Copilot items. + /// + public ObservableCollection CopilotItemViewModels { get; private set; } = new ObservableCollection(); + /// /// Initializes a new instance of the class. /// @@ -56,6 +64,21 @@ namespace MaaWpfGui.ViewModels.UI AddLog(LocalizationHelper.GetString("CopilotTip")); _runningState = RunningState.Instance; _runningState.IdleChanged += RunningState_IdleChanged; + + var copilotTaskList = ConfigurationHelper.GetValue(ConfigurationKeys.CopilotTaskList, string.Empty); + if (!string.IsNullOrEmpty(copilotTaskList)) + { + JArray jArray = JArray.Parse(copilotTaskList); + foreach (var item in jArray) + { + if ((item as JObject).TryGetValue("file_path", out var token) && File.Exists(token.ToString())) + { + CopilotItemViewModels.Add(new CopilotItemViewModel((string)item["name"], (string)item["file_path"]) { IsChecked = (bool)item["is_checked"] }); + } + } + + CopilotItemIndexChanged(); + } } private void RunningState_IdleChanged(object sender, bool e) @@ -236,6 +259,14 @@ namespace MaaWpfGui.ViewModels.UI if (doc != null && doc.TryGetValue("title", out var titleValue)) { title = titleValue.ToString(); + + // 为自动作战列表匹配名字 + var linkParser = new Regex(@"([a-z]{0,3})(\d{0,2})-(EX-)?(\d{1,2})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + foreach (Match match in linkParser.Matches(title)) + { + CopilotTaskName = match.Value; + break; + } } if (title.Length != 0) @@ -503,6 +534,144 @@ namespace MaaWpfGui.ViewModels.UI } } + /// + /// Gets or sets a value indicating whether to use auto-formation. + /// + private bool _useCopilotList = false; + + public bool UseCopilotList + { + get => _useCopilotList; + set + { + SetAndNotify(ref _useCopilotList, value); + } + } + + /// + /// Gets or sets a value indicating whether to use auto-formation. + /// + private string _copilotTaskName = string.Empty; + + public string CopilotTaskName + { + get => _copilotTaskName; + set + { + SetAndNotify(ref _copilotTaskName, value); + } + } + + private static string copilotJsonDir = "cache/copilot"; + + public void AddCopilotTask() + { + var stage_name = CopilotTaskName.Trim().Replace("突袭", "-Adverse"); + if (!stage_name.IsNullOrEmpty()) + { + AddCopilotTaskToList(stage_name); + } + } + + public void AddCopilotTask_Adverse() + { + var stage_name = CopilotTaskName.Trim().Replace("突袭", "-Adverse"); + if (!stage_name.EndsWith("-Adverse")) + { + stage_name += "-Adverse"; + } + + if (!stage_name.IsNullOrEmpty()) + { + AddCopilotTaskToList(stage_name); + } + } + + public void AddCopilotTaskToList(string stage_name) + { + var jsonPath = $"{copilotJsonDir}/{stage_name}.json"; + + try + { + Directory.CreateDirectory(copilotJsonDir); + } + catch + { + } + + try + { + File.Copy(IsDataFromWeb ? TempCopilotFile : Filename, jsonPath, true); + var item = new CopilotItemViewModel(stage_name, jsonPath) + { + Index = CopilotItemViewModels.Count, + }; + CopilotItemViewModels.Add(item); + SaveCopilotTask(); + } + catch (Exception ex) + { + AddLog(LocalizationHelper.GetString("CopilotJsonError"), UiLogColor.Error); + Log.Error(ex.ToString()); + } + } + + public void SaveCopilotTask() + { + JArray jArray = new JArray(CopilotItemViewModels.Select(item => new JObject + { + ["name"] = item.Name, + ["file_path"] = item.FilePath, + ["is_checked"] = item.IsChecked, + }).ToArray()); + ConfigurationHelper.SetValue(ConfigurationKeys.CopilotTaskList, JsonConvert.SerializeObject(jArray)); + } + + public void DeleteCopilotTask(int index) + { + CopilotItemViewModels.RemoveAt(index); + CopilotItemIndexChanged(); + } + + public void ClearCopilotTask() + { + CopilotItemViewModels.Clear(); + SaveCopilotTask(); + } + + public void EnterCopilotTask() + { + Application.Current.Dispatcher.InvokeAsync(() => + { + for (int i = 0; i < CopilotItemViewModels.Count; i++) + { + if (CopilotItemViewModels[i].IsChecked) + { + CopilotItemViewModels[i].IsChecked = false; + break; + } + } + + SaveCopilotTask(); + }); + } + + /// + /// 更新任务顺序 + /// + public void CopilotItemIndexChanged() + { + Application.Current.Dispatcher.InvokeAsync(() => + { + for (int i = 0; i < CopilotItemViewModels.Count; i++) + { + CopilotItemViewModels[i].Index = i; + } + + SaveCopilotTask(); + }); + } + public bool Loop { get; set; } private int _loopTimes = int.Parse(ConfigurationHelper.GetValue(ConfigurationKeys.CopilotLoopTimes, "1")); @@ -558,14 +727,51 @@ namespace MaaWpfGui.ViewModels.UI AddLog(errMsg, UiLogColor.Error); } - bool ret = Instances.AsstProxy.AsstStartCopilot(IsDataFromWeb ? TempCopilotFile : Filename, Form, AddTrust, AddUserAdditional, UserAdditional, _taskType, - Loop ? LoopTimes : 1); + JArray mUserAdditional = new JArray(); + Regex regex = new Regex(@"(?<=;)(?[^,;]+)(?:, *(?\d))? *", RegexOptions.Compiled); + MatchCollection matches = regex.Matches(";" + UserAdditional); + foreach (Match match in matches) + { + mUserAdditional.Add(new JObject + { + ["name"] = match.Groups[1].Value.Trim(), + ["skill"] = match.Groups[2].Value.IsNullOrEmpty() ? 0 : int.Parse(match.Groups[2].Value), + }); + } + + bool ret = true; + if (UseCopilotList) + { + bool startAny = false; + foreach (var model in CopilotItemViewModels) + { + if (model.IsChecked) + { + ret &= Instances.AsstProxy.AsstStartCopilot(model.FilePath, Form, AddTrust, AddUserAdditional, mUserAdditional, UseCopilotList, model.Name.Replace("-Adverse", string.Empty), model.Name.Contains("-Adverse"), _taskType, Loop ? LoopTimes : 1, false); + startAny = true; + } + } + + // 一个都没启动,怎会有如此无聊之人 + if (!startAny) + { + return; + } + + ret &= Instances.AsstProxy.AsstStart(); + } + else + { + ret &= Instances.AsstProxy.AsstStartCopilot(IsDataFromWeb ? TempCopilotFile : Filename, Form, AddTrust, AddUserAdditional, mUserAdditional, UseCopilotList, string.Empty, false, _taskType, Loop ? LoopTimes : 1); + } + if (ret) { AddLog(LocalizationHelper.GetString("Running")); } else { + Instances.AsstProxy.AsstStop(); _runningState.SetIdle(true); AddLog(LocalizationHelper.GetString("CopilotFileReadError"), UiLogColor.Error); } diff --git a/src/MaaWpfGui/Views/UI/CopilotView.xaml b/src/MaaWpfGui/Views/UI/CopilotView.xaml index 759b6674f7..464c955535 100644 --- a/src/MaaWpfGui/Views/UI/CopilotView.xaml +++ b/src/MaaWpfGui/Views/UI/CopilotView.xaml @@ -6,6 +6,7 @@ xmlns:constants="clr-namespace:MaaWpfGui.Constants" xmlns:controls="clr-namespace:MaaWpfGui.Styles.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:dd="urn:gong-wpf-dragdrop" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:helper="clr-namespace:MaaWpfGui.Helper" xmlns:local="clr-namespace:MaaWpfGui" @@ -31,7 +32,7 @@ @@ -62,90 +63,22 @@ Style="{StaticResource ButtonDefault}" ToolTip="{DynamicResource PasteClipboardTip}" /> -