diff --git a/.gitignore b/.gitignore index d76d5aa3..48efc0af 100644 --- a/.gitignore +++ b/.gitignore @@ -31,10 +31,6 @@ github_actions_cache/ # Rider .idea -.claude -CLAUDE.md -AGENTS.md - __pycache__/ diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md index 7f731689..ca9aa16c 100644 --- a/.trae/rules/project_rules.md +++ b/.trae/rules/project_rules.md @@ -14,7 +14,8 @@ - 请尽量使用 Behaviors 库来实现交互,避免不符合 MVVM 规范的交互事件触发方式。 ### 其他框架使用要求 -请优先使用 Newtonsoft.Json 作为json序列化工具,但是如果这个模型已经被System.Text.Json序列化过了,那么就直接使用System.Text.Json反序列化。 +1. 请优先使用 Newtonsoft.Json 作为json序列化工具,但是如果这个模型已经被System.Text.Json序列化过了,那么就直接使用System.Text.Json反序列化。 +2. 所有简单的对话框弹出需求优先使用 ThemedMessageBox 弹出。而不是 WPF 自带的 MessageBox。 ## MVVM 架构规则 @@ -77,4 +78,9 @@ services.AddSingleton(); ``` -最后,程序能够编译就认为成功,无需实际运行程序。 \ No newline at end of file +最后,程序能够编译就认为成功,无需实际运行程序。 + +编译指令参考,如果出现程序占用场景,直接放弃编译验证即可 +``` +dotnet build BetterGenshinImpact.sln -c Debug +``` \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ca9aa16c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,86 @@ +本项目使用了 WPF-UI、 CommunityToolkit.Mvvm、Microsoft.Xaml.Behaviors.Wpf 来实现 MVVM 架构。在编写代码的时候请注意: + +### 主要依赖框架 +#### UI 框架 +- **WPF-UI (4.0.2)** - 现代化 WPF UI 框架 +- **gong-wpf-dragdrop(3.2.1)** - 拖拽框架 + +#### MVVM 框架 +- **CommunityToolkit.Mvvm (8.2.2)** - 微软官方 MVVM 工具包 + - 所有 ViewModel 必须继承自 `ObservableObject` + - 使用 `[ObservableProperty]` 特性自动生成属性 + - 使用 `[RelayCommand]` 特性自动生成命令 +- **Microsoft.Xaml.Behaviors.Wpf(1.1.122)** - WPF 行为扩展库 + - 请尽量使用 Behaviors 库来实现交互,避免不符合 MVVM 规范的交互事件触发方式。 + +### 其他框架使用要求 +1. 请优先使用 Newtonsoft.Json 作为json序列化工具,但是如果这个模型已经被System.Text.Json序列化过了,那么就直接使用System.Text.Json反序列化。 +2. 所有简单的对话框弹出需求优先使用 ThemedMessageBox 弹出。而不是 WPF 自带的 MessageBox。 + +## MVVM 架构规则 + +### 基础架构 + +### ViewModel 编写规范 + +1. **继承规则** + ```csharp + public partial class ExampleViewModel : ViewModel + { + [ObservableProperty] + private string _title = ""; + + [RelayCommand] + private void DoSomething() + { + // 实现逻辑 + } + } + ``` + +2. **属性命名** + - 私有字段使用下划线前缀: `_fieldName` + - 公共属性使用 PascalCase: `PropertyName` + - 使用 `[ObservableProperty]` 自动生成属性 + +3. **命令实现** + - 使用 `[RelayCommand]` 特性 + - 异步命令使用 `[RelayCommand]` + `async Task` + +### View 编写规范 + +1. **代码后置** + ```csharp + public partial class ExamplePage : UserControl + { + public ExampleViewModel ViewModel { get; } + + public ExamplePage(ExampleViewModel viewModel) + { + ViewModel = viewModel; + DataContext = this; + InitializeComponent(); + } + } + ``` + +2. **XAML 绑定** + - 使用 `{Binding}` 语法绑定 ViewModel 属性 + - 命令绑定: `Command="{Binding ExampleCommand}"` + - 避免在 XAML 中编写复杂逻辑 + +### 依赖注入规范 + +1. **服务注册** + ```csharp + // 在 App.xaml.cs 中注册 + services.AddView(); + services.AddSingleton(); + ``` + +最后,程序能够编译就认为成功,无需实际运行程序。 + +编译指令参考,如果出现程序占用场景,直接放弃编译验证即可 +``` +dotnet build BetterGenshinImpact.sln -c Debug +``` \ No newline at end of file diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 3452512a..fb32daa1 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -2,7 +2,7 @@ BetterGI - 0.60.2-alpha.3 + 0.60.2-alpha.4 false WinExe net8.0-windows10.0.22621.0 diff --git a/BetterGenshinImpact/Core/BgiVision/BvLocator.cs b/BetterGenshinImpact/Core/BgiVision/BvLocator.cs index f23e8037..abedab3c 100644 --- a/BetterGenshinImpact/Core/BgiVision/BvLocator.cs +++ b/BetterGenshinImpact/Core/BgiVision/BvLocator.cs @@ -208,4 +208,16 @@ public class BvLocator RetryAction = action; return this; } + + /// + /// 为 JavaScript 提供的动态参数重载 + /// 解决 ClearScript 无法将 JS 函数隐式转换为 Action 委托的问题 + /// + /// JS 回调函数 + /// + public BvLocator WithRetryAction(dynamic action) + { + RetryAction = (results) => action(results); + return this; + } } \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs b/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs index 519c48e8..badcda76 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs @@ -22,6 +22,7 @@ public class AutoFightAssets : BaseAssets public Rect ERect; public Rect ECooldownRect; public Rect QRect; + public Rect QRectForClassify; public Rect ZCooldownRect; public Rect EndTipsUpperRect; // 挑战达成提示 public Rect EndTipsRect; @@ -78,6 +79,8 @@ public class AutoFightAssets : BaseAssets (int)(41 * AssetScale), (int)(18 * AssetScale)); QRect = new Rect(CaptureRect.Width - (int)(157 * AssetScale), CaptureRect.Height - (int)(165 * AssetScale), (int)(110 * AssetScale), (int)(110 * AssetScale)); + QRectForClassify = new Rect(CaptureRect.Width - (int)(172 * AssetScale), CaptureRect.Height - (int)(166 * AssetScale), + (int)(137 * AssetScale), (int)(137 * AssetScale)); ZCooldownRect = new Rect(CaptureRect.Width - (int)(130 * AssetScale), (int)(814 * AssetScale), (int)(60 * AssetScale), (int)(24 * AssetScale)); // 小道具位置 1920-133,800,60,50 diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs index 14a4bb35..6774c762 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs @@ -25,6 +25,7 @@ using BetterGenshinImpact.GameTask.AutoPathing; using BetterGenshinImpact.GameTask.AutoPathing.Model; using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum; using BetterGenshinImpact.Core.Recognition.ONNX; +using BetterGenshinImpact.GameTask.Common; using Compunet.YoloSharp; using Compunet.YoloSharp.Data; using Microsoft.Extensions.DependencyInjection; @@ -600,8 +601,9 @@ public class Avatar { // CD 中立即返回,其余场景尝试释放 using var region1 = CaptureToRectArea(); - if (IsBurstReadyByClassify(region1) == BurstReadyState.Cooldown) + if (IsBurstReadyByClassify(region1) != BurstReadyState.Ready) { + // Logger.LogInformation("Q在CD,跳过"); return; } @@ -612,6 +614,7 @@ public class Avatar return; } + // Logger.LogInformation("释放Q"); Simulation.SendInput.SimulateAction(GIActions.ElementalBurst); Sleep(200, Ct); @@ -627,8 +630,9 @@ public class Avatar else { // 找到编号块判断是否进入了CD,四星角色没有大招动画 - if (IsBurstReadyByClassify(region) == BurstReadyState.Cooldown) + if (IsBurstReadyByClassify(region) != BurstReadyState.Ready) { + // Logger.LogInformation("释放Q后检查到CD"); Sleep(1500, Ct); return; } @@ -638,29 +642,29 @@ public class Avatar private static BurstReadyState IsBurstReadyByClassify(ImageRegion imageRegion) { - using var qRa = imageRegion.DeriveCrop(AutoFightAssets.Instance.QRect); + using var qRa = imageRegion.DeriveCrop(AutoFightAssets.Instance.QRectForClassify); var result = QBurstClassifierLazy.Value.Predictor.Classify(qRa.CacheImage); var topClass = result.GetTopClass(); var topClassName = topClass.Name.Name; - + // Logger.LogInformation("Q技能冷却分类:{ClassName},置信度:{Confidence:F2}", topClassName, topClass.Confidence); + // 置信度不足时,直接返回未知,避免误判导致漏放/乱放 if (topClass.Confidence <= 0.7) { - Logger.LogDebug("Q技能冷却分类置信度不足:{Confidence:F2},类别:{ClassName}", topClass.Confidence, topClassName); + // Logger.LogInformation("Q技能冷却分类置信度不足:{Confidence:F2},类别:{ClassName}", topClass.Confidence, topClassName); return BurstReadyState.Unknown; } - if (topClassName.Contains("cd_1", StringComparison.OrdinalIgnoreCase)) + if (topClassName.Contains("cd 1", StringComparison.OrdinalIgnoreCase)) { return BurstReadyState.Cooldown; } - if (topClassName.Contains("cd_0", StringComparison.OrdinalIgnoreCase)) + if (topClassName.Contains("energy 1 cd 0", StringComparison.OrdinalIgnoreCase)) { return BurstReadyState.Ready; } - Logger.LogDebug("Q技能冷却分类出现未知类别:{ClassName},置信度:{Confidence:F2}", topClassName, topClass.Confidence); return BurstReadyState.Unknown; }