mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-27 09:49:49 +08:00
修复错误信息,改进联机状态处理 更新了 `BetterGenshinImpact.csproj` 文件中的程序集版本号,从 `0.35.2` 更新为 `0.35.4`。 修正了 `ScriptProject.cs` 文件中抛出 `FileNotFoundException` 异常时的错误信息,将 "manifest.json文件存在" 改为 "manifest.json文件不存在"。 在 `AutoFightAssets.cs` 文件中: - 为 `AvatarSideIconRectList` 和 `AvatarIndexRectList` 添加了注释,解释其在非联机状态下的用途。 - 添加了多个新的属性和注释,用于处理联机状态下的角色头像和对应的白色块位置。 - 初始化了 `OnePRa` 和 `PRa` 两个识别对象,用于识别联机状态下的1P和P图标。 在 `Avatar.cs` 文件中: - 修改了角色切换逻辑,使用 `CombatScenes.ExpectedTeamAvatarNum` 替代硬编码的数字。 - 在 `TrySwitch` 方法中添加了 `needLog` 参数,并在切换成功时记录日志。 - 移除了部分注释代码,并在日志中保存了角色切换和索引区域的截图。 - 添加了 `System.Diagnostics` 的引用。 在 `CombatScenes.cs` 文件中: - 将 `Avatars` 初始化为空数组。 - 添加了 `ExpectedTeamAvatarNum` 属性,默认值为4。 - 在 `InitializeTeam` 方法中添加了联机状态的判断和处理逻辑。 - 修改了队伍识别逻辑,使用动态数组替代固定长度的数组。 - 修改了 `CheckTeamInitialized` 方法,使用 `ExpectedTeamAvatarNum` 替代硬编码的数字。 - 修改了 `BuildAvatars` 方法,添加了对联机状态下角色编号位置信息的处理。 - 修改了 `SelectAvatar` 方法,使用 `GetValueOrDefault` 替代 `TryGetValue`。 在 `ScriptControlViewModel.cs` 文件中,设置 `WindowStartupLocation` 为 `WindowStartupLocation.CenterOwner`。 添加了 `1p.png` 和 `p.png` 两个新图像文件,用于识别联机状态下的1P和P图标。
625 lines
18 KiB
C#
625 lines
18 KiB
C#
using BetterGenshinImpact.Core.Config;
|
|
using BetterGenshinImpact.Core.Script.Group;
|
|
using BetterGenshinImpact.Core.Script.Project;
|
|
using BetterGenshinImpact.GameTask;
|
|
using BetterGenshinImpact.GameTask.AutoPathing.Model;
|
|
using BetterGenshinImpact.Helpers.Ui;
|
|
using BetterGenshinImpact.Model;
|
|
using BetterGenshinImpact.Service.Interface;
|
|
using BetterGenshinImpact.View.Controls;
|
|
using BetterGenshinImpact.View.Pages.View;
|
|
using BetterGenshinImpact.View.Windows;
|
|
using BetterGenshinImpact.View.Windows.Editable;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Dynamic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using Wpf.Ui;
|
|
using Wpf.Ui.Controls;
|
|
using Wpf.Ui.Violeta.Controls;
|
|
using StackPanel = Wpf.Ui.Controls.StackPanel;
|
|
|
|
namespace BetterGenshinImpact.ViewModel.Pages;
|
|
|
|
public partial class ScriptControlViewModel : ObservableObject, INavigationAware, IViewModel
|
|
{
|
|
private readonly ISnackbarService _snackbarService;
|
|
|
|
private readonly ILogger<ScriptControlViewModel> _logger = App.GetLogger<ScriptControlViewModel>();
|
|
|
|
private readonly IScriptService _scriptService;
|
|
|
|
/// <summary>
|
|
/// 配置组配置
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ObservableCollection<ScriptGroup> _scriptGroups = [];
|
|
|
|
/// <summary>
|
|
/// 当前选中的配置组
|
|
/// </summary>
|
|
[ObservableProperty]
|
|
private ScriptGroup? _selectedScriptGroup;
|
|
|
|
public readonly string ScriptGroupPath = Global.Absolute(@"User\ScriptGroup");
|
|
|
|
public void OnNavigatedFrom()
|
|
{
|
|
}
|
|
|
|
public void OnNavigatedTo()
|
|
{
|
|
ReadScriptGroup();
|
|
}
|
|
|
|
public ScriptControlViewModel(ISnackbarService snackbarService, IScriptService scriptService)
|
|
{
|
|
_snackbarService = snackbarService;
|
|
_scriptService = scriptService;
|
|
ScriptGroups.CollectionChanged += ScriptGroupsCollectionChanged;
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void OnAddScriptGroup()
|
|
{
|
|
var str = PromptDialog.Prompt("请输入配置组名称", "新增配置组");
|
|
if (!string.IsNullOrEmpty(str))
|
|
{
|
|
// 检查是否已存在
|
|
if (ScriptGroups.Any(x => x.Name == str))
|
|
{
|
|
_snackbarService.Show(
|
|
"配置组已存在",
|
|
$"配置组 {str} 已经存在,请勿重复添加",
|
|
ControlAppearance.Caution,
|
|
null,
|
|
TimeSpan.FromSeconds(2)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ScriptGroups.Add(new ScriptGroup { Name = str });
|
|
}
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnDeleteScriptGroup(ScriptGroup? item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
ScriptGroups.Remove(item);
|
|
File.Delete(Path.Combine(ScriptGroupPath, $"{item.Name}.json"));
|
|
_snackbarService.Show(
|
|
"配置组删除成功",
|
|
$"配置组 {item.Name} 已经被删除",
|
|
ControlAppearance.Success,
|
|
null,
|
|
TimeSpan.FromSeconds(2)
|
|
);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogDebug(e, "删除配置组配置时失败");
|
|
_snackbarService.Show(
|
|
"删除配置组配置失败",
|
|
$"配置组 {item.Name} 删除失败!",
|
|
ControlAppearance.Danger,
|
|
null,
|
|
TimeSpan.FromSeconds(3)
|
|
);
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void OnAddJsScript()
|
|
{
|
|
var list = LoadAllJsScriptProjects();
|
|
var combobox = new ComboBox();
|
|
|
|
foreach (var scriptProject in list)
|
|
{
|
|
combobox.Items.Add(scriptProject.FolderName + " - " + scriptProject.Manifest.Name);
|
|
}
|
|
|
|
var str = PromptDialog.Prompt("请选择需要添加的JS脚本", "请选择需要添加的JS脚本", combobox);
|
|
if (!string.IsNullOrEmpty(str))
|
|
{
|
|
var folderName = str.Split(" - ")[0];
|
|
SelectedScriptGroup?.AddProject(new ScriptGroupProject(new ScriptProject(folderName)));
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void OnAddKmScript()
|
|
{
|
|
var list = LoadAllKmScripts();
|
|
var combobox = new ComboBox();
|
|
|
|
foreach (var fileInfo in list)
|
|
{
|
|
combobox.Items.Add(fileInfo.Name);
|
|
}
|
|
|
|
var str = PromptDialog.Prompt("请选择需要添加的键鼠脚本", "请选择需要添加的键鼠脚本", combobox);
|
|
if (!string.IsNullOrEmpty(str))
|
|
{
|
|
SelectedScriptGroup?.AddProject(ScriptGroupProject.BuildKeyMouseProject(str));
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void OnAddPathing()
|
|
{
|
|
var root = FileTreeNodeHelper.LoadDirectory<PathingTask>(MapPathingViewModel.PathJsonPath);
|
|
var stackPanel = CreatePathingScriptSelectionPanel(root.Children);
|
|
|
|
var result = PromptDialog.Prompt("请选择需要添加的路径追踪任务", "请选择需要添加的路径追踪任务", stackPanel, new Size(500, 600));
|
|
if (!string.IsNullOrEmpty(result))
|
|
{
|
|
AddSelectedPathingScripts((StackPanel)stackPanel.Content);
|
|
}
|
|
}
|
|
|
|
private ScrollViewer CreatePathingScriptSelectionPanel(IEnumerable<FileTreeNode<PathingTask>> list)
|
|
{
|
|
var stackPanel = new StackPanel();
|
|
AddNodesToPanel(stackPanel, list, 0);
|
|
|
|
var scrollViewer = new ScrollViewer
|
|
{
|
|
Content = stackPanel,
|
|
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
|
Height = 435 // 固定高度
|
|
};
|
|
|
|
return scrollViewer;
|
|
}
|
|
|
|
private void AddNodesToPanel(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, int depth)
|
|
{
|
|
foreach (var node in nodes)
|
|
{
|
|
var checkBox = new CheckBox
|
|
{
|
|
Content = node.FileName,
|
|
Tag = node.FilePath,
|
|
Margin = new Thickness(depth * 30, 0, 0, 0) // 根据深度计算Margin
|
|
};
|
|
|
|
if (node.IsDirectory)
|
|
{
|
|
var childPanel = new StackPanel();
|
|
AddNodesToPanel(childPanel, node.Children, depth + 1); // 递归调用时深度加1
|
|
checkBox.Checked += (s, e) => SetChildCheckBoxesState(childPanel, true);
|
|
checkBox.Unchecked += (s, e) => SetChildCheckBoxesState(childPanel, false);
|
|
parentPanel.Children.Add(checkBox);
|
|
parentPanel.Children.Add(childPanel);
|
|
}
|
|
else
|
|
{
|
|
parentPanel.Children.Add(checkBox);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetChildCheckBoxesState(StackPanel childStackPanel, bool state)
|
|
{
|
|
foreach (var child in childStackPanel.Children)
|
|
{
|
|
if (child is CheckBox checkBox)
|
|
{
|
|
checkBox.IsChecked = state;
|
|
}
|
|
else if (child is StackPanel nestedStackPanel)
|
|
{
|
|
SetChildCheckBoxesState(nestedStackPanel, 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 { IsChecked: true } checkBox)
|
|
{
|
|
var fileInfo = new FileInfo((string)checkBox.Tag);
|
|
SelectedScriptGroup?.AddProject(ScriptGroupProject.BuildPathingProject(fileInfo.Name, fileInfo.Directory!.Name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dictionary<string, List<FileInfo>> LoadAllPathingScripts()
|
|
{
|
|
var folder = Global.Absolute(@"User\AutoPathing");
|
|
var directories = Directory.GetDirectories(folder);
|
|
var result = new Dictionary<string, List<FileInfo>>();
|
|
|
|
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<ScriptProject> LoadAllJsScriptProjects()
|
|
{
|
|
var path = Global.ScriptPath();
|
|
// 获取所有脚本项目
|
|
var projects = Directory.GetDirectories(path)
|
|
.Select(x => new ScriptProject(Path.GetFileName(x)))
|
|
.ToList();
|
|
return projects;
|
|
}
|
|
|
|
private List<FileInfo> LoadAllKmScripts()
|
|
{
|
|
var folder = Global.Absolute(@"User\KeyMouseScript");
|
|
// 获取所有脚本项目
|
|
var files = Directory.GetFiles(folder, "*.*",
|
|
SearchOption.AllDirectories);
|
|
|
|
return files.Select(file => new FileInfo(file)).ToList();
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnEditScriptCommon(ScriptGroupProject? item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ShowEditWindow(item);
|
|
|
|
// foreach (var group in ScriptGroups)
|
|
// {
|
|
// WriteScriptGroup(group);
|
|
// }
|
|
}
|
|
|
|
public static void ShowEditWindow(object viewModel)
|
|
{
|
|
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
|
|
{
|
|
Title = "修改通用设置",
|
|
Content = new ScriptGroupProjectEditor { DataContext = viewModel },
|
|
CloseButtonText = "关闭",
|
|
Owner = Application.Current.MainWindow,
|
|
};
|
|
uiMessageBox.ShowDialogAsync();
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnEditJsScriptSettings(ScriptGroupProject? item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (item.Project == null)
|
|
{
|
|
item.BuildScriptProjectRelation();
|
|
}
|
|
|
|
if (item.Project == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (item.Type == "Javascript")
|
|
{
|
|
if (item.JsScriptSettingsObject == null)
|
|
{
|
|
item.JsScriptSettingsObject = new ExpandoObject();
|
|
}
|
|
|
|
var ui = item.Project.LoadSettingUi(item.JsScriptSettingsObject);
|
|
if (ui == null)
|
|
{
|
|
Toast.Warning("此脚本未提供自定义配置");
|
|
return;
|
|
}
|
|
|
|
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
|
|
{
|
|
Title = "修改JS脚本自定义设置 ",
|
|
Content = ui,
|
|
CloseButtonText = "关闭",
|
|
Owner = Application.Current.MainWindow,
|
|
};
|
|
uiMessageBox.ShowDialogAsync();
|
|
|
|
// 由于 JsScriptSettingsObject 的存在,这里只能手动再次保存配置
|
|
foreach (var group in ScriptGroups)
|
|
{
|
|
WriteScriptGroup(group);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Toast.Warning("只有JS脚本才有自定义配置");
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnDeleteScript(ScriptGroupProject? item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SelectedScriptGroup?.Projects.Remove(item);
|
|
_snackbarService.Show(
|
|
"脚本配置移除成功",
|
|
$"{item.Name} 的关联配置已经移除",
|
|
ControlAppearance.Success,
|
|
null,
|
|
TimeSpan.FromSeconds(2)
|
|
);
|
|
}
|
|
|
|
private void ScriptGroupsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e.NewItems != null)
|
|
{
|
|
foreach (ScriptGroup newItem in e.NewItems)
|
|
{
|
|
newItem.Projects.CollectionChanged += ScriptProjectsCollectionChanged;
|
|
foreach (var project in newItem.Projects)
|
|
{
|
|
project.PropertyChanged += ScriptProjectsPChanged;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (e.OldItems != null)
|
|
{
|
|
foreach (ScriptGroup oldItem in e.OldItems)
|
|
{
|
|
foreach (var project in oldItem.Projects)
|
|
{
|
|
project.PropertyChanged -= ScriptProjectsPChanged;
|
|
}
|
|
|
|
oldItem.Projects.CollectionChanged -= ScriptProjectsCollectionChanged;
|
|
}
|
|
}
|
|
|
|
// 补充排序字段
|
|
var i = 1;
|
|
foreach (var group in ScriptGroups)
|
|
{
|
|
group.Index = i++;
|
|
}
|
|
|
|
// 保存配置组配置
|
|
foreach (var group in ScriptGroups)
|
|
{
|
|
WriteScriptGroup(group);
|
|
}
|
|
}
|
|
|
|
private void ScriptProjectsPChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
foreach (var group in ScriptGroups)
|
|
{
|
|
WriteScriptGroup(group);
|
|
}
|
|
}
|
|
|
|
private void ScriptProjectsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
// 补充排序字段
|
|
if (SelectedScriptGroup is { Projects.Count: > 0 })
|
|
{
|
|
var i = 1;
|
|
foreach (var project in SelectedScriptGroup.Projects)
|
|
{
|
|
project.Index = i++;
|
|
}
|
|
}
|
|
|
|
// 保存配置组配置
|
|
if (SelectedScriptGroup != null)
|
|
{
|
|
WriteScriptGroup(SelectedScriptGroup);
|
|
}
|
|
}
|
|
|
|
private void WriteScriptGroup(ScriptGroup scriptGroup)
|
|
{
|
|
try
|
|
{
|
|
if (!Directory.Exists(ScriptGroupPath))
|
|
{
|
|
Directory.CreateDirectory(ScriptGroupPath);
|
|
}
|
|
|
|
var file = Path.Combine(ScriptGroupPath, $"{scriptGroup.Name}.json");
|
|
File.WriteAllText(file, scriptGroup.ToJson());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogDebug(e, "保存配置组配置时失败");
|
|
_snackbarService.Show(
|
|
"保存配置组配置失败",
|
|
$"{scriptGroup.Name} 保存失败!",
|
|
ControlAppearance.Danger,
|
|
null,
|
|
TimeSpan.FromSeconds(3)
|
|
);
|
|
}
|
|
}
|
|
|
|
private void ReadScriptGroup()
|
|
{
|
|
try
|
|
{
|
|
if (!Directory.Exists(ScriptGroupPath))
|
|
{
|
|
Directory.CreateDirectory(ScriptGroupPath);
|
|
}
|
|
|
|
ScriptGroups.Clear();
|
|
var files = Directory.GetFiles(ScriptGroupPath, "*.json");
|
|
List<ScriptGroup> groups = [];
|
|
foreach (var file in files)
|
|
{
|
|
try
|
|
{
|
|
var json = File.ReadAllText(file);
|
|
var group = ScriptGroup.FromJson(json);
|
|
groups.Add(group);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogDebug(e, "读取单个配置组配置时失败");
|
|
_snackbarService.Show(
|
|
"读取配置组配置失败",
|
|
"读取配置组配置失败:" + e.Message,
|
|
ControlAppearance.Danger,
|
|
null,
|
|
TimeSpan.FromSeconds(3)
|
|
);
|
|
}
|
|
}
|
|
|
|
// 按index排序
|
|
groups.Sort((a, b) => a.Index.CompareTo(b.Index));
|
|
foreach (var group in groups)
|
|
{
|
|
ScriptGroups.Add(group);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogDebug(e, "读取配置组配置时失败");
|
|
_snackbarService.Show(
|
|
"读取配置组配置失败",
|
|
"读取配置组配置失败!",
|
|
ControlAppearance.Danger,
|
|
null,
|
|
TimeSpan.FromSeconds(3)
|
|
);
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnGoToScriptGroupUrl()
|
|
{
|
|
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/autos/dispatcher.html") { UseShellExecute = true });
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnImportScriptGroup(string scriptGroupExample)
|
|
{
|
|
ScriptGroup group = new();
|
|
if ("AutoCrystalflyExampleGroup" == scriptGroupExample)
|
|
{
|
|
group.Name = "晶蝶示例组";
|
|
group.AddProject(new ScriptGroupProject(new ScriptProject("AutoCrystalfly")));
|
|
}
|
|
|
|
if (ScriptGroups.Any(x => x.Name == group.Name))
|
|
{
|
|
_snackbarService.Show(
|
|
"配置组已存在",
|
|
$"配置组 {group.Name} 已经存在,请勿重复添加",
|
|
ControlAppearance.Caution,
|
|
null,
|
|
TimeSpan.FromSeconds(2)
|
|
);
|
|
return;
|
|
}
|
|
|
|
ScriptGroups.Add(group);
|
|
}
|
|
|
|
[RelayCommand]
|
|
public async Task OnStartScriptGroupAsync()
|
|
{
|
|
if (SelectedScriptGroup == null)
|
|
{
|
|
_snackbarService.Show(
|
|
"未选择配置组",
|
|
"请先选择一个配置组",
|
|
ControlAppearance.Caution,
|
|
null,
|
|
TimeSpan.FromSeconds(2)
|
|
);
|
|
return;
|
|
}
|
|
|
|
await _scriptService.RunMulti(SelectedScriptGroup.Projects, SelectedScriptGroup.Name);
|
|
}
|
|
|
|
[RelayCommand]
|
|
public void OnOpenScriptGroupSettings()
|
|
{
|
|
if (SelectedScriptGroup == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// var uiMessageBox = new Wpf.Ui.Controls.MessageBox
|
|
// {
|
|
// Content = new ScriptGroupConfigView(SelectedScriptGroup.Config),
|
|
// Title = "配置组设置"
|
|
// };
|
|
//
|
|
// await uiMessageBox.ShowDialogAsync();
|
|
|
|
var dialogWindow = new Window
|
|
{
|
|
Title = "配置组设置",
|
|
Content = new ScriptGroupConfigView(SelectedScriptGroup.Config),
|
|
SizeToContent = SizeToContent.WidthAndHeight,
|
|
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
|
};
|
|
|
|
// var dialogWindow = new WpfUiWindow(new ScriptGroupConfigView(SelectedScriptGroup.Config))
|
|
// {
|
|
// Title = "配置组设置"
|
|
// };
|
|
|
|
// 显示对话框
|
|
var result = dialogWindow.ShowDialog();
|
|
|
|
// if (result == true)
|
|
// {
|
|
// // 用户点击了确定或关闭
|
|
// }
|
|
|
|
WriteScriptGroup(SelectedScriptGroup);
|
|
}
|
|
}
|