From a9466ec991efdbaa1bde52f00e745c118f395149 Mon Sep 17 00:00:00 2001 From: status102 Date: Sun, 10 Sep 2023 19:18:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E6=88=98=E6=96=97-?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E7=BC=96=E9=98=9F=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=BF=BD=E5=8A=A0=E8=87=AA=E5=AE=9A=E5=B9=B2?= =?UTF-8?q?=E5=91=98=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MaaCore/Common/AsstBattleDef.h | 10 +- src/MaaCore/Task/Interface/CopilotTask.cpp | 17 +++ .../Miscellaneous/BattleFormationTask.cpp | 106 +++++++++++++----- .../Task/Miscellaneous/BattleFormationTask.h | 13 ++- src/MaaWpfGui/Constants/ConfigurationKeys.cs | 2 + src/MaaWpfGui/Main/AsstProxy.cs | 21 +++- src/MaaWpfGui/Res/Localizations/en-us.xaml | 2 + src/MaaWpfGui/Res/Localizations/ja-jp.xaml | 2 + src/MaaWpfGui/Res/Localizations/ko-kr.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-cn.xaml | 2 + src/MaaWpfGui/Res/Localizations/zh-tw.xaml | 2 + .../ViewModels/UI/CopilotViewModel.cs | 32 +++++- src/MaaWpfGui/Views/UI/CopilotView.xaml | 32 +++++- 13 files changed, 200 insertions(+), 43 deletions(-) diff --git a/src/MaaCore/Common/AsstBattleDef.h b/src/MaaCore/Common/AsstBattleDef.h index 62b1331dd7..e04f0b4680 100644 --- a/src/MaaCore/Common/AsstBattleDef.h +++ b/src/MaaCore/Common/AsstBattleDef.h @@ -17,15 +17,15 @@ namespace asst::battle { NotUse = 0, // 不自动使用 Possibly = 1, // 有就用,例如干员 棘刺 3 技能 - Times = 2, // 用 X 次,例如干员 山 2 技能用 1 次、重岳 3 技能用 5 次,由 "skill_times" 字段控制 - InTime = 3, // 自动判断使用时机,画饼.jpg - TimesUsed // 已经使用了 X 次 + Times = 2, // 用 X 次,例如干员 山 2 技能用 1 次、重岳 3 技能用 5 次,由 "skill_times" 字段控制 + InTime = 3, // 自动判断使用时机,画饼.jpg + TimesUsed // 已经使用了 X 次 }; struct OperUsage // 干员用法 { std::string name; - int skill = 1; // 技能序号,取值范围 [1, 3] + int skill = 0; // 技能序号,取值范围 [0, 3],0时使用默认技能 或 上次编队时使用的技能 SkillUsage skill_usage = SkillUsage::NotUse; int skill_times = 1; // 使用技能的次数,默认为 1,兼容曾经的作业 }; @@ -80,7 +80,7 @@ namespace asst::battle { "法师", Role::Caster }, { "support", Role::Support }, { "SUPPORT", Role::Support }, { "Support", Role::Support }, - { "supporter", Role::Support }, { "SUPPORTER", Role::Support }, { "Supporter", Role::Support }, + { "supporter", Role::Support }, { "SUPPORTER", Role::Support }, { "Supporter", Role::Support }, { "辅助", Role::Support }, { "支援", Role::Support }, { "special", Role::Special }, { "SPECIAL", Role::Special }, { "Special", Role::Special }, diff --git a/src/MaaCore/Task/Interface/CopilotTask.cpp b/src/MaaCore/Task/Interface/CopilotTask.cpp index e1a4ba1215..2fffa38a47 100644 --- a/src/MaaCore/Task/Interface/CopilotTask.cpp +++ b/src/MaaCore/Task/Interface/CopilotTask.cpp @@ -71,6 +71,23 @@ 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"); + if (!user_additional_opt) { + Log.error("add_user_additional not found"); + } + else { + std::vector> user_additional; + for (const auto& op : user_additional_opt.value()) { + std::string name = op.get("name", std::string()); + if (name.empty()) { + continue; + } + user_additional.emplace_back(std::pair { std::move(name), op.get("skill", 1) }); + } + m_formation_task_ptr->set_user_additional(std::move(user_additional)); + } + 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)); diff --git a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp index b7216c0734..2001befb92 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp +++ b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.cpp @@ -23,6 +23,16 @@ void asst::BattleFormationTask::set_support_unit_name(std::string name) m_support_unit_name = std::move(name); } +void asst::BattleFormationTask::set_add_user_additional(bool add_user_additional) +{ + m_add_user_additional = add_user_additional; +} + +void asst::BattleFormationTask::set_user_additional(std::vector> user_additional) +{ + m_user_additional = std::move(user_additional); +} + void asst::BattleFormationTask::set_add_trust(bool add_trust) { m_add_trust = add_trust; @@ -46,33 +56,26 @@ bool asst::BattleFormationTask::_run() } for (auto& [role, oper_groups] : m_formation) { - click_role_table(role); - bool has_error = false; - int swipe_times = 0; - while (!need_exit()) { - if (select_opers_in_cur_page(oper_groups)) { - has_error = false; - if (oper_groups.empty()) { - break; - } - swipe_page(); - ++swipe_times; + add_formation(role, oper_groups); + } + if (m_add_user_additional) { + + for (auto& [name, skill] : m_user_additional) { + if (m_operators_in_formation.contains(name)) { + continue; } - else if (has_error) { - swipe_to_the_left(swipe_times); - // reset page - click_role_table(role == battle::Role::Unknown ? battle::Role::Pioneer : battle::Role::Unknown); - click_role_table(role); - swipe_to_the_left(swipe_times); - swipe_times = 0; - has_error = false; - } - else { - has_error = true; - swipe_to_the_left(swipe_times); - swipe_times = 0; + if (m_size_of_operators_in_formation >= 12) { + break; } + asst::battle::OperUsage oper; + oper.name = name; + oper.skill = skill; + std::vector usage { std::move(oper) }; + m_user_formation.emplace_back(std::move(usage)); + ++m_size_of_operators_in_formation; } + m_size_of_operators_in_formation -= (int)m_user_formation.size(); + add_formation(battle::Role::Unknown, m_user_formation); } add_additional(); @@ -91,6 +94,40 @@ bool asst::BattleFormationTask::_run() return true; } +bool asst::BattleFormationTask::add_formation(battle::Role role, std::vector oper_group) +{ + LogTraceFunction; + + click_role_table(role); + bool has_error = false; + int swipe_times = 0; + while (!need_exit()) { + if (select_opers_in_cur_page(oper_group)) { + has_error = false; + if (oper_group.empty()) { + break; + } + swipe_page(); + ++swipe_times; + } + else if (has_error) { + swipe_to_the_left(swipe_times); + // reset page + click_role_table(role == battle::Role::Unknown ? battle::Role::Pioneer : battle::Role::Unknown); + click_role_table(role); + swipe_to_the_left(swipe_times); + swipe_times = 0; + has_error = false; + } + else { + has_error = true; + swipe_to_the_left(swipe_times); + swipe_times = 0; + } + } + return true; +} + bool asst::BattleFormationTask::add_additional() { // (但是干员名在除开获取时间的情况下都会被遮挡,so ? @@ -145,15 +182,20 @@ bool asst::BattleFormationTask::add_trust_operators() return false; } + // 需要追加的信赖干员数量 + int append_count = 12 - m_size_of_operators_in_formation; + if (append_count == 0) { + return true; + } + ProcessTask(*this, { "BattleQuickFormationFilter" }).run(); // 双击信赖 ProcessTask(*this, { "BattleQuickFormationFilter-Trust" }).run(); ProcessTask(*this, { "BattleQuickFormationFilterClose" }).run(); - // 重置职能,保证处于最左 + // 重置职业选择,保证处于最左 click_role_table(battle::Role::Caster); click_role_table(battle::Role::Unknown); - int append_count = 12 - m_operators_in_formation; int failed_count = 0; while (!need_exit() && append_count > 0 && failed_count < 3) { MultiMatcher matcher(ctrler()->get_image()); @@ -250,17 +292,19 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& } int delay = Task.get("BattleQuickFormationOCR")->post_delay; - - int skill = 1; + int skill = 0; for (const auto& res : opers_result) { const std::string& name = res.text; - auto iter = groups.begin(); bool found = false; + auto iter = groups.begin(); for (; iter != groups.end(); ++iter) { for (const auto& oper : *iter) { if (oper.name == name) { found = true; skill = oper.skill; + + m_operators_in_formation.emplace(name); + ++m_size_of_operators_in_formation; break; } } @@ -272,6 +316,7 @@ bool asst::BattleFormationTask::select_opers_in_cur_page(std::vector& if (iter == groups.end()) { continue; } + ctrler()->click(res.rect); sleep(delay); if (1 <= skill && skill <= 3) { @@ -338,13 +383,13 @@ bool asst::BattleFormationTask::parse_formation() groups = &SSSCopilot.get_data().groups; } - m_operators_in_formation = 0; for (const auto& [name, opers_vec] : *groups) { if (opers_vec.empty()) { continue; } formation.array_emplace(name); + // 判断干员/干员组的职业,放进对应的分组 bool same_role = true; battle::Role role = BattleData.get_role(opers_vec.front().name); for (const auto& oper : opers_vec) { @@ -353,7 +398,6 @@ bool asst::BattleFormationTask::parse_formation() // for unknown, will use { "BattleQuickFormationRole-All", "BattleQuickFormationRole-All-OCR" } m_formation[same_role ? role : battle::Role::Unknown].emplace_back(opers_vec); - m_operators_in_formation++; } callback(AsstMsg::SubTaskExtraInfo, info); diff --git a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h index 1112b40026..26ea9d1c88 100644 --- a/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h +++ b/src/MaaCore/Task/Miscellaneous/BattleFormationTask.h @@ -25,6 +25,8 @@ 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 @@ -38,6 +40,7 @@ namespace asst using OperGroup = std::vector; virtual bool _run() override; + bool add_formation(battle::Role role, std::vector oper_group); // 追加附加干员(按部署费用等小分类) bool add_additional(); // 补充刷信赖的干员,从最小的开始 @@ -56,8 +59,14 @@ namespace asst std::string m_stage_name; std::unordered_map> m_formation; + std::vector m_user_formation; // 编队中干员个数 - int m_operators_in_formation = 0; + int m_size_of_operators_in_formation = 0; + // 编队中的干员名称,用来判断能不能追加某个干员 + std::set m_operators_in_formation; + bool m_add_user_additional = false; + // 追加干员表,从头往后加 + std::vector> m_user_additional; // 是否需要追加信赖干员 bool m_add_trust = false; std::string m_support_unit_name; @@ -65,4 +74,4 @@ namespace asst std::vector m_additional; std::string m_last_oper_name; }; -} +} // namespace asst diff --git a/src/MaaWpfGui/Constants/ConfigurationKeys.cs b/src/MaaWpfGui/Constants/ConfigurationKeys.cs index 3aa28897f8..b4c18223d8 100644 --- a/src/MaaWpfGui/Constants/ConfigurationKeys.cs +++ b/src/MaaWpfGui/Constants/ConfigurationKeys.cs @@ -133,6 +133,8 @@ namespace MaaWpfGui.Constants public const string CreditBlackListNew = "Mall.CreditBlackListNew"; public const string CreditForceShoppingIfCreditFull = "Mall.CreditForceShoppingIfCreditFull"; + public const string CopilotAddUserAdditional = "Copilot.AddUserAdditional"; + public const string CopilotUserAdditional = "Copilot.UserAdditional"; public const string CopilotLoopTimes = "Copilot.LoopTimes"; public const string UpdateProxy = "VersionUpdate.Proxy"; diff --git a/src/MaaWpfGui/Main/AsstProxy.cs b/src/MaaWpfGui/Main/AsstProxy.cs index a465231f7a..a68282acf1 100644 --- a/src/MaaWpfGui/Main/AsstProxy.cs +++ b/src/MaaWpfGui/Main/AsstProxy.cs @@ -18,10 +18,12 @@ using System.IO; 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; @@ -1149,7 +1151,6 @@ namespace MaaWpfGui.Main private static readonly bool ForcedReloadResource = File.Exists("DEBUG") || File.Exists("DEBUG.txt"); - /// /// 检查端口是否有效。 /// @@ -1793,16 +1794,32 @@ namespace MaaWpfGui.Main /// 作业 JSON 的文件路径,绝对、相对路径均可。 /// 是否进行 “快捷编队”。 /// 是否追加信赖干员 + /// 是否追加自定干员 + /// 自定干员列表 /// 任务类型 /// 任务重复执行次数 /// 是否成功。 - public bool AsstStartCopilot(string filename, bool formation, bool add_trust, string type, int loop_times) + public bool AsstStartCopilot(string filename, bool formation, bool add_trust, bool add_user_additional, string user_additional, string type, int loop_times) { + 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, ["loop_times"] = loop_times, }; AsstTaskId id = AsstAppendTaskWithEncoding(type, task_params); diff --git a/src/MaaWpfGui/Res/Localizations/en-us.xaml b/src/MaaWpfGui/Res/Localizations/en-us.xaml index 5fb22f19a3..13476c8204 100644 --- a/src/MaaWpfGui/Res/Localizations/en-us.xaml +++ b/src/MaaWpfGui/Res/Localizations/en-us.xaml @@ -440,6 +440,8 @@ The video aspect ratio needs to be 16:9 without interference factors such as bla Auto Squad 「Favourite」 marked operators cannot be recognized at this time Add low-trust operators + Add custom operators + Use ";" as the separator, "," to separate the operator name and skills, for example: W, 3; Ash, 2 Loop Times Start Video Link diff --git a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml index 0b530d0563..195c496554 100644 --- a/src/MaaWpfGui/Res/Localizations/ja-jp.xaml +++ b/src/MaaWpfGui/Res/Localizations/ja-jp.xaml @@ -431,6 +431,8 @@ 自動編成 自動編成では、一時的に「戦術要員」オペレーターを認識できません 信頼性の低いオペレーターを追加する + カスタム戦術要員の追加 + 使用「;」オペレータ名とスキルを区切るには、区切り文字として「,」を使用します。例: W, 3; Ash, 2 ループ回数 開始 映像へのハイパーリンク diff --git a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml index 728849fe9d..ba7c0bf574 100644 --- a/src/MaaWpfGui/Res/Localizations/ko-kr.xaml +++ b/src/MaaWpfGui/Res/Localizations/ko-kr.xaml @@ -443,6 +443,8 @@ OF-1을 해금하지 않았다면 선택하지 말아 주세요. 자동 편성 자동 편성은 '선호' 오퍼레이터를 인식할 수 없습니다 신뢰도가 낮은 운영자 추가 + 맞춤 연산자 추가 + 사용 ";" 운영자 이름과 기술을 구분하기 위해 구분 기호 ","를 사용합니다. 예: W, 3; Ash, 2 반복 횟수 시작 영상 링크 diff --git a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml index 095f85f5c0..cfe9c11075 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-cn.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-cn.xaml @@ -444,6 +444,8 @@ 自动编队 自动编队暂时无法识别「特别关注」的干员 补充低信赖干员 + 追加自定干员 + 以英文";"为分隔符,英文","分隔干员名与技能,例: 史尔特尔,3;艾雅法拉,1 循环次数 开始 视频链接 diff --git a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml index a0a4238abc..7d37bf9506 100644 --- a/src/MaaWpfGui/Res/Localizations/zh-tw.xaml +++ b/src/MaaWpfGui/Res/Localizations/zh-tw.xaml @@ -443,6 +443,8 @@ 自動編隊 自動編隊暫時無法辨識「特別關注」的幹員 補充低信賴幹員 + 追加自定幹員 + 使用";"作為分隔符,用","分隔操作員姓名和技能,例:史尔特尔,3;艾雅法拉,1 循環次數 開始 影片連結 diff --git a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs index 857797c0f2..c03cfd9bd8 100644 --- a/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs +++ b/src/MaaWpfGui/ViewModels/UI/CopilotViewModel.cs @@ -473,6 +473,36 @@ namespace MaaWpfGui.ViewModels.UI set => SetAndNotify(ref _addTrust, value); } + private bool _addUserAdditional = bool.Parse(ConfigurationHelper.GetValue(ConfigurationKeys.CopilotAddUserAdditional, false.ToString())); + + /// + /// Gets or sets a value indicating whether to use auto-formation. + /// + public bool AddUserAdditional + { + get => _addUserAdditional; + set + { + SetAndNotify(ref _addUserAdditional, value); + ConfigurationHelper.SetValue(ConfigurationKeys.CopilotAddUserAdditional, value.ToString()); + } + } + + private string _userAdditional = ConfigurationHelper.GetValue(ConfigurationKeys.CopilotUserAdditional, "W,2;Friston-3,1"); + + /// + /// Gets or sets a value indicating whether to use auto-formation. + /// + public string UserAdditional + { + get => _userAdditional; + set + { + SetAndNotify(ref _userAdditional, value); + ConfigurationHelper.SetValue(ConfigurationKeys.CopilotUserAdditional, value); + } + } + public bool Loop { get; set; } private int _loopTimes = int.Parse(ConfigurationHelper.GetValue(ConfigurationKeys.CopilotLoopTimes, "1")); @@ -528,7 +558,7 @@ namespace MaaWpfGui.ViewModels.UI AddLog(errMsg, UiLogColor.Error); } - bool ret = Instances.AsstProxy.AsstStartCopilot(IsDataFromWeb ? TempCopilotFile : Filename, Form, AddTrust, _taskType, + bool ret = Instances.AsstProxy.AsstStartCopilot(IsDataFromWeb ? TempCopilotFile : Filename, Form, AddTrust, AddUserAdditional, UserAdditional, _taskType, Loop ? LoopTimes : 1); if (ret) { diff --git a/src/MaaWpfGui/Views/UI/CopilotView.xaml b/src/MaaWpfGui/Views/UI/CopilotView.xaml index b10520d883..3d2024baa2 100644 --- a/src/MaaWpfGui/Views/UI/CopilotView.xaml +++ b/src/MaaWpfGui/Views/UI/CopilotView.xaml @@ -71,7 +71,7 @@ IsEnabled="{Binding Idle}" ToolTip="{DynamicResource SelectTheFileTip}" /> @@ -90,9 +90,37 @@ HorizontalAlignment="Center" Content="{DynamicResource AddTrust}" IsChecked="{Binding AddTrust}" - Visibility="{c:Binding '(Form) and (Idle)'}" + Visibility="{c:Binding Form}" + IsEnabled="{Binding Idle}" IsHitTestVisible="{Binding Idle}" Margin="10,0,0,0"/> + + + + + + + + + +