diff --git a/BetterGenshinImpact/Core/Script/Group/ScriptGroupProject.cs b/BetterGenshinImpact/Core/Script/Group/ScriptGroupProject.cs index 09843a82..84630aad 100644 --- a/BetterGenshinImpact/Core/Script/Group/ScriptGroupProject.cs +++ b/BetterGenshinImpact/Core/Script/Group/ScriptGroupProject.cs @@ -1,6 +1,10 @@ using BetterGenshinImpact.Core.Config; using BetterGenshinImpact.Core.Recorder; using BetterGenshinImpact.Core.Script.Project; +using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.GameTask.AutoPathing; +using BetterGenshinImpact.GameTask.AutoPathing.Model; +using BetterGenshinImpact.ViewModel.Pages; using CommunityToolkit.Mvvm.ComponentModel; using System; using System.Collections.Generic; @@ -64,14 +68,30 @@ public partial class ScriptGroupProject : ObservableObject Type = "Javascript"; } - public ScriptGroupProject(string kmName) + /// + /// + /// + /// + /// + /// KeyMouse|Pathing + public ScriptGroupProject(string name, string folder, string type) { - Name = kmName; - FolderName = kmName; + Name = name; + FolderName = folder; Status = "Enabled"; Schedule = "Daily"; Project = null; // 不是JS脚本 - Type = "KeyMouse"; + Type = type; + } + + public static ScriptGroupProject BuildKeyMouseProject(string name) + { + return new ScriptGroupProject(name, name, "KeyMouse"); + } + + public static ScriptGroupProject BuildPathingProject(string name, string folder) + { + return new ScriptGroupProject(name, folder, "Pathing"); } /// @@ -102,6 +122,13 @@ public partial class ScriptGroupProject : ObservableObject var json = await File.ReadAllTextAsync(Global.Absolute(@$"User\KeyMouseScript\{Name}")); await KeyMouseMacroPlayer.PlayMacro(json, CancellationContext.Instance.Cts.Token, false); } + if (Type == "Pathing") + { + // 加载并执行 + var task = PathingTask.BuildFromFilePath(Path.Combine(MapPathingViewModel.PathJsonPath, FolderName, Name)); + TaskTriggerDispatcher.Instance().AddTrigger("AutoPick", null); + await new PathExecutor(CancellationContext.Instance.Cts).Pathing(task); + } else { //throw new Exception("不支持的脚本类型"); @@ -129,7 +156,8 @@ public class ScriptGroupProjectExtensions public static readonly Dictionary TypeDescriptions = new() { { "Javascript", "JS脚本" }, - { "KeyMouse", "键鼠脚本" } + { "KeyMouse", "键鼠脚本" }, + { "Pathing", "路径追踪" } }; public static readonly Dictionary StatusDescriptions = new() diff --git a/BetterGenshinImpact/GameTask/AutoPathing/Model/PathingTask.cs b/BetterGenshinImpact/GameTask/AutoPathing/Model/PathingTask.cs index d0e6a345..be965284 100644 --- a/BetterGenshinImpact/GameTask/AutoPathing/Model/PathingTask.cs +++ b/BetterGenshinImpact/GameTask/AutoPathing/Model/PathingTask.cs @@ -3,19 +3,28 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; namespace BetterGenshinImpact.GameTask.AutoPathing.Model; [Serializable] public class PathingTask { + /// + /// 实际存储的文件名 + /// + [JsonIgnore] + public string FileName { get; set; } = string.Empty; + public PathingTaskInfo Info { get; set; } = new(); public List Positions { get; set; } = []; public static PathingTask BuildFromFilePath(string filePath) { var json = File.ReadAllText(filePath); - return JsonSerializer.Deserialize(json, PathRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize PathingTask"); + var task = JsonSerializer.Deserialize(json, PathRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize PathingTask"); + task.FileName = Path.GetFileName(filePath); + return task; } public void SaveToFile(string filePath) diff --git a/BetterGenshinImpact/GameTask/AutoPathing/Navigation.cs b/BetterGenshinImpact/GameTask/AutoPathing/Navigation.cs index 9183556a..d3e65d18 100644 --- a/BetterGenshinImpact/GameTask/AutoPathing/Navigation.cs +++ b/BetterGenshinImpact/GameTask/AutoPathing/Navigation.cs @@ -10,16 +10,22 @@ using Microsoft.Extensions.Logging; namespace BetterGenshinImpact.GameTask.AutoPathing; -internal class Navigation +public class Navigation { + private static bool _isWarmUp = false; + public static void WarmUp() { - TaskControl.Logger.LogInformation("地图特征点加载中,由于体积较大,首次加载速度较慢,请耐心等待..."); - EntireMap.Instance.GetFeatureMatcher(); - TaskControl.Logger.LogInformation("地图特征点加载完成!"); + if (!_isWarmUp) + { + TaskControl.Logger.LogInformation("地图特征点加载中,由于体积较大,首次加载速度较慢,请耐心等待..."); + EntireMap.Instance.GetFeatureMatcher(); + TaskControl.Logger.LogInformation("地图特征点加载完成!"); + } + _isWarmUp = true; } - internal static Point2f GetPosition(ImageRegion imageRegion) + public static Point2f GetPosition(ImageRegion imageRegion) { var greyMat = new Mat(imageRegion.SrcGreyMat, new Rect(62, 19, 212, 212)); var p = EntireMap.Instance.GetMiniMapPositionByFeatureMatch(greyMat); @@ -29,7 +35,7 @@ internal class Navigation return p; } - internal static int GetTargetOrientation(Waypoint waypoint, Point2f position) + public static int GetTargetOrientation(Waypoint waypoint, Point2f position) { double deltaX = waypoint.X - position.X; double deltaY = waypoint.Y - position.Y; @@ -48,7 +54,7 @@ internal class Navigation return (int)(angle * (180.0 / Math.PI)); } - internal static double GetDistance(Waypoint waypoint, Point2f position) + public static double GetDistance(Waypoint waypoint, Point2f position) { var x1 = waypoint.X; var y1 = waypoint.Y; diff --git a/BetterGenshinImpact/GameTask/GameTaskManager.cs b/BetterGenshinImpact/GameTask/GameTaskManager.cs index b0328f70..93814954 100644 --- a/BetterGenshinImpact/GameTask/GameTaskManager.cs +++ b/BetterGenshinImpact/GameTask/GameTaskManager.cs @@ -80,7 +80,7 @@ internal class GameTaskManager public static void AddTrigger(string name, object? externalConfig) { TriggerDictionary ??= new ConcurrentDictionary(); - TriggerDictionary.Clear(); + TriggerDictionary.Clear(); //TODO 有问题,不应该清理 if (name == "AutoPick") { diff --git a/BetterGenshinImpact/Service/ScriptService.cs b/BetterGenshinImpact/Service/ScriptService.cs index b49b6013..d5100fc5 100644 --- a/BetterGenshinImpact/Service/ScriptService.cs +++ b/BetterGenshinImpact/Service/ScriptService.cs @@ -80,6 +80,8 @@ public partial class ScriptService(HomePageViewModel homePageViewModel) : IScrip public async Task RunMulti(IEnumerable projectList, string groupName) { + var hasTimer = false; + // 重新加载脚本项目 并放入一个新的列表 var list = new List(); foreach (var project in projectList) @@ -93,14 +95,23 @@ public partial class ScriptService(HomePageViewModel homePageViewModel) : IScrip newProject.JsScriptSettingsObject = project.JsScriptSettingsObject; list.Add(newProject); } - else + else if (project.Type == "KeyMouse") { - var newProject = new ScriptGroupProject(project.FolderName); + var newProject = ScriptGroupProject.BuildKeyMouseProject(project.FolderName); newProject.Status = project.Status; newProject.Schedule = project.Schedule; newProject.RunNum = project.RunNum; list.Add(newProject); } + else if (project.Type == "Pathing") + { + var newProject = ScriptGroupProject.BuildPathingProject(project.Name, project.FolderName); + newProject.Status = project.Status; + newProject.Schedule = project.Schedule; + newProject.RunNum = project.RunNum; + list.Add(newProject); + hasTimer = true; // 路径追踪任务一定有实时任务操作 + } } // 判断其中的 @@ -112,9 +123,8 @@ public partial class ScriptService(HomePageViewModel homePageViewModel) : IScrip jsProjects.Add(project.Project); } } - var hasTimer = false; - if (jsProjects.Count > 0) + if (!hasTimer && jsProjects.Count > 0) { var codeList = await ReadCodeList(jsProjects); hasTimer = HasTimerOperation(codeList); @@ -169,11 +179,16 @@ public partial class ScriptService(HomePageViewModel homePageViewModel) : IScrip _logger.LogInformation("→ 开始执行JS脚本: {Name}", project.Name); await project.Run(); } - else + else if (project.Type == "KeyMouse") { _logger.LogInformation("→ 开始执行键鼠脚本: {Name}", project.Name); await project.Run(); } + else if (project.Type == "Pathing") + { + _logger.LogInformation("→ 开始执行路径追踪任务: {Name}", project.Name); + await project.Run(); + } await Task.Delay(2000); } diff --git a/BetterGenshinImpact/View/Pages/MapPathingPage.xaml b/BetterGenshinImpact/View/Pages/MapPathingPage.xaml index 0a9962df..999bc649 100644 --- a/BetterGenshinImpact/View/Pages/MapPathingPage.xaml +++ b/BetterGenshinImpact/View/Pages/MapPathingPage.xaml @@ -60,13 +60,13 @@ - + - + + diff --git a/BetterGenshinImpact/View/Windows/PromptDialog.xaml.cs b/BetterGenshinImpact/View/Windows/PromptDialog.xaml.cs index 782aea0f..14093048 100644 --- a/BetterGenshinImpact/View/Windows/PromptDialog.xaml.cs +++ b/BetterGenshinImpact/View/Windows/PromptDialog.xaml.cs @@ -43,6 +43,17 @@ public partial class PromptDialog return inst.DialogResult == true ? inst.ResponseText : defaultValue; } + public static string Prompt(string question, string title, UIElement uiElement, Size size) + { + var inst = new PromptDialog(question, title, uiElement, "") + { + Width = size.Width, + Height = size.Height + }; + inst.ShowDialog(); + return inst.DialogResult == true ? inst.ResponseText : ""; + } + public string ResponseText { get @@ -55,7 +66,10 @@ public partial class PromptDialog { return comboBox.Text; } - return string.Empty; + else + { + return "true"; + } } } diff --git a/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs index 10659daa..738957b1 100644 --- a/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs @@ -1,6 +1,11 @@ using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.Core.Script; using BetterGenshinImpact.Core.Script.Project; +using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.GameTask.AutoPathing; using BetterGenshinImpact.GameTask.AutoPathing.Model; +using BetterGenshinImpact.GameTask.Model.Enum; +using BetterGenshinImpact.View.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; @@ -10,15 +15,8 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading.Tasks; -using BetterGenshinImpact.Core.Script; -using BetterGenshinImpact.GameTask; -using BetterGenshinImpact.GameTask.AutoPathing; -using BetterGenshinImpact.GameTask.Model.Enum; -using BetterGenshinImpact.View.Windows; using Wpf.Ui.Controls; using Wpf.Ui.Violeta.Controls; -using BetterGenshinImpact.Core.Script.Dependence.Model; namespace BetterGenshinImpact.ViewModel.Pages; diff --git a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs index 6d280e1b..0eac0653 100644 --- a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs @@ -156,10 +156,106 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware var str = PromptDialog.Prompt("请选择需要添加的键鼠脚本", "请选择需要添加的键鼠脚本", combobox); if (!string.IsNullOrEmpty(str)) { - SelectedScriptGroup?.Projects.Add(new ScriptGroupProject(str)); + SelectedScriptGroup?.Projects.Add(ScriptGroupProject.BuildKeyMouseProject(str)); } } + [RelayCommand] + private void OnAddPathing() + { + var directories = LoadAllPathingScripts(); + var stackPanel = CreatePathingScriptSelectionPanel(directories); + + var result = PromptDialog.Prompt("请选择需要添加的路径追踪任务", "请选择需要添加的路径追踪任务", stackPanel, new Size(500, 600)); + if (!string.IsNullOrEmpty(result)) + { + AddSelectedPathingScripts((StackPanel)stackPanel.Content); + } + } + + private ScrollViewer CreatePathingScriptSelectionPanel(Dictionary> directories) + { + var stackPanel = new StackPanel(); + + foreach (var directory in directories) + { + var parentCheckBox = new CheckBox + { + Content = directory.Key, + Tag = directory.Value + }; + + var childStackPanel = new StackPanel(); + foreach (var fileInfo in directory.Value) + { + var childCheckBox = new CheckBox + { + Content = fileInfo.Name, + Tag = fileInfo, + Margin = new Thickness(30, 0, 0, 0) + }; + childStackPanel.Children.Add(childCheckBox); + } + + parentCheckBox.Checked += (s, e) => SetChildCheckBoxesState(childStackPanel, true); + parentCheckBox.Unchecked += (s, e) => SetChildCheckBoxesState(childStackPanel, false); + + stackPanel.Children.Add(parentCheckBox); + stackPanel.Children.Add(childStackPanel); + } + + var scrollViewer = new ScrollViewer + { + Content = stackPanel, + VerticalScrollBarVisibility = ScrollBarVisibility.Auto, + Height = 435 // 固定高度 + }; + + return scrollViewer; + } + + private void SetChildCheckBoxesState(StackPanel childStackPanel, bool state) + { + foreach (CheckBox child in childStackPanel.Children) + { + child.IsChecked = state; + } + } + + private void AddSelectedPathingScripts(StackPanel stackPanel) + { + foreach (var child in stackPanel.Children) + { + if (child is StackPanel childStackPanel) + { + foreach (var grandChild in childStackPanel.Children) + { + if (grandChild is CheckBox checkBox && checkBox.IsChecked == true) + { + var fileInfo = (FileInfo)checkBox.Tag; + SelectedScriptGroup?.Projects.Add(ScriptGroupProject.BuildPathingProject(fileInfo.Name, fileInfo.DirectoryName)); + } + } + } + } + } + + private Dictionary> LoadAllPathingScripts() + { + var folder = Global.Absolute(@"User\AutoPathing"); + var directories = Directory.GetDirectories(folder); + var result = new Dictionary>(); + + foreach (var directory in directories) + { + var dirInfo = new DirectoryInfo(directory); + var files = dirInfo.GetFiles("*.*", SearchOption.TopDirectoryOnly).ToList(); + result.Add(dirInfo.Name, files); + } + + return result; + } + private List LoadAllJsScriptProjects() { var path = Global.ScriptPath(); @@ -465,6 +561,9 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware return; } - await _scriptService.RunMulti(SelectedScriptGroup.Projects, SelectedScriptGroup.Name); + await Task.Run(async () => + { + await _scriptService.RunMulti(SelectedScriptGroup.Projects, SelectedScriptGroup.Name); + }); } }