From aef3fa913b56cc8255be158d3c54db670acec8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Mon, 23 Mar 2026 02:31:14 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E7=83=B9=E9=A5=AA=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=88=90=E7=8B=AC=E7=AB=8B=E4=BB=BB=E5=8A=A1=20(#2949?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BetterGenshinImpact/Core/Config/AllConfig.cs | 11 +- .../Core/Config/HotKeyConfig.cs | 7 + .../Core/Script/Dependence/Dispatcher.cs | 4 + .../GameTask/AutoCook/AutoCookConfig.cs | 10 +- .../GameTask/AutoCook/AutoCookTask.cs | 161 +++++++ .../GameTask/AutoCook/AutoCookTrigger.cs | 54 --- .../GameTask/GameTaskManager.cs | 4 +- .../View/Pages/TaskSettingsPage.xaml | 418 ++++++++++-------- .../View/Pages/TriggerSettingsPage.xaml | 27 -- .../ViewModel/Pages/HotKeyPageViewModel.cs | 9 +- .../Pages/TaskSettingsPageViewModel.cs | 17 + 11 files changed, 447 insertions(+), 275 deletions(-) create mode 100644 BetterGenshinImpact/GameTask/AutoCook/AutoCookTask.cs delete mode 100644 BetterGenshinImpact/GameTask/AutoCook/AutoCookTrigger.cs diff --git a/BetterGenshinImpact/Core/Config/AllConfig.cs b/BetterGenshinImpact/Core/Config/AllConfig.cs index ad793b3a..086982a1 100644 --- a/BetterGenshinImpact/Core/Config/AllConfig.cs +++ b/BetterGenshinImpact/Core/Config/AllConfig.cs @@ -1,5 +1,4 @@ using BetterGenshinImpact.GameTask; -using BetterGenshinImpact.GameTask.AutoCook; using BetterGenshinImpact.GameTask.AutoDomain; using BetterGenshinImpact.GameTask.AutoFight; using BetterGenshinImpact.GameTask.AutoFishing; @@ -23,6 +22,7 @@ using BetterGenshinImpact.GameTask.AutoStygianOnslaught; using BetterGenshinImpact.GameTask.GetGridIcons; using BetterGenshinImpact.GameTask.AutoEat; using BetterGenshinImpact.GameTask.AutoLeyLineOutcrop; +using BetterGenshinImpact.GameTask.AutoCook; using BetterGenshinImpact.GameTask.MapMask; using BetterGenshinImpact.GameTask.SkillCd; using BetterGenshinImpact.GameTask.UseRedeemCode; @@ -129,11 +129,6 @@ public partial class AllConfig : ObservableObject /// public QuickTeleportConfig QuickTeleportConfig { get; set; } = new(); - /// - /// 自动烹饪配置 - /// - public AutoCookConfig AutoCookConfig { get; set; } = new(); - /// /// 自动打牌配置 /// @@ -179,6 +174,8 @@ public partial class AllConfig : ObservableObject /// 自动地脉花配置 /// public AutoLeyLineOutcropConfig AutoLeyLineOutcropConfig { get; set; } = new(); + + public AutoCookConfig AutoCookConfig { get; set; } = new(); /// /// 地图遮罩 @@ -269,7 +266,6 @@ public partial class AllConfig : ObservableObject AutoSkipConfig.PropertyChanged += OnAnyPropertyChanged; AutoFishingConfig.PropertyChanged += OnAnyPropertyChanged; QuickTeleportConfig.PropertyChanged += OnAnyPropertyChanged; - AutoCookConfig.PropertyChanged += OnAnyPropertyChanged; MacroConfig.PropertyChanged += OnAnyPropertyChanged; HotKeyConfig.PropertyChanged += OnAnyPropertyChanged; AutoWoodConfig.PropertyChanged += OnAnyPropertyChanged; @@ -280,6 +276,7 @@ public partial class AllConfig : ObservableObject AutoRedeemCodeConfig.PropertyChanged += OnAnyPropertyChanged; AutoEatConfig.PropertyChanged += OnAnyPropertyChanged; AutoLeyLineOutcropConfig.PropertyChanged += OnAnyPropertyChanged; + AutoCookConfig.PropertyChanged += OnAnyPropertyChanged; MapMaskConfig.PropertyChanged += OnAnyPropertyChanged; AutoMusicGameConfig.PropertyChanged += OnAnyPropertyChanged; TpConfig.PropertyChanged += OnAnyPropertyChanged; diff --git a/BetterGenshinImpact/Core/Config/HotKeyConfig.cs b/BetterGenshinImpact/Core/Config/HotKeyConfig.cs index c511e611..a174d6e3 100644 --- a/BetterGenshinImpact/Core/Config/HotKeyConfig.cs +++ b/BetterGenshinImpact/Core/Config/HotKeyConfig.cs @@ -163,6 +163,13 @@ public partial class HotKeyConfig : ObservableObject [ObservableProperty] private string _autoFishingGameHotkeyType = HotKeyTypeEnum.KeyboardMonitor.ToString(); + // 自动烹饪开始/停止 + [ObservableProperty] + private string _autoCookGameHotkey = ""; + + [ObservableProperty] + private string _autoCookGameHotkeyType = HotKeyTypeEnum.KeyboardMonitor.ToString(); + // 自动寻路 [ObservableProperty] private string _autoTrackPathHotkey = ""; diff --git a/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs index 5c8f1070..b4a2aca6 100644 --- a/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs +++ b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs @@ -4,6 +4,7 @@ using BetterGenshinImpact.GameTask; using BetterGenshinImpact.GameTask.AutoDomain; using BetterGenshinImpact.GameTask.AutoEat; using BetterGenshinImpact.GameTask.AutoFishing; +using BetterGenshinImpact.GameTask.AutoCook; using BetterGenshinImpact.GameTask.AutoGeniusInvokation; using BetterGenshinImpact.GameTask.AutoPathing.Handler; using BetterGenshinImpact.GameTask.AutoWood; @@ -195,6 +196,9 @@ public class Dispatcher await new AutoFishingTask(AutoFishingTaskParam.BuildFromSoloTaskConfig(soloTask.Config)).Start( cancellationToken); return null; + case "AutoCook": + await new AutoCookTask().Start(cancellationToken); + return null; case "AutoEat": { string? foodName = soloTask.Config == null ? null : ScriptObjectConverter.GetValue((ScriptObject)soloTask.Config, "foodName", (string?)null); diff --git a/BetterGenshinImpact/GameTask/AutoCook/AutoCookConfig.cs b/BetterGenshinImpact/GameTask/AutoCook/AutoCookConfig.cs index 502300ae..9c1e7b3a 100644 --- a/BetterGenshinImpact/GameTask/AutoCook/AutoCookConfig.cs +++ b/BetterGenshinImpact/GameTask/AutoCook/AutoCookConfig.cs @@ -1,17 +1,11 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; using System; namespace BetterGenshinImpact.GameTask.AutoCook; -/// -///自动烹饪配置 -/// [Serializable] public partial class AutoCookConfig : ObservableObject { - /// - /// 触发器是否启用 - /// [ObservableProperty] - private bool _enabled = false; + private int _checkIntervalMs = 10; } diff --git a/BetterGenshinImpact/GameTask/AutoCook/AutoCookTask.cs b/BetterGenshinImpact/GameTask/AutoCook/AutoCookTask.cs new file mode 100644 index 00000000..57cca2fd --- /dev/null +++ b/BetterGenshinImpact/GameTask/AutoCook/AutoCookTask.cs @@ -0,0 +1,161 @@ +using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.GameTask.Common.Element.Assets; +using BetterGenshinImpact.GameTask.Model.Area; +using Microsoft.Extensions.Logging; +using OpenCvSharp; +using System; +using System.Threading; +using System.Threading.Tasks; +using BetterGenshinImpact.GameTask.Common.BgiVision; +using Vanara.PInvoke; +using static BetterGenshinImpact.GameTask.Common.TaskControl; + +namespace BetterGenshinImpact.GameTask.AutoCook; + +public class AutoCookTask : ISoloTask +{ + private readonly ILogger _logger = App.GetLogger(); + private const int UiCheckIntervalMs = 400; + private const int PeakMinCount = 600; // 最小仙跳墙 700 多 + private const int PeakTolerance = 20; + private const int PeakStableFrameCount = 3; + private const int TriggerDropCount = 300; // 正常是 400多 + private static readonly Rect CookColorRect1080P = new(600, 660, 730, 190); + private static readonly Scalar TargetCookColor = new(255, 192, 64); + + public string Name => "自动烹饪"; + + public async Task Start(CancellationToken ct) + { + var assetScale = TaskContext.Instance().SystemInfo.AssetScale; + var checkIntervalMs = Math.Max(1, TaskContext.Instance().Config.AutoCookConfig.CheckIntervalMs); + var peakMinCount = (int)(PeakMinCount * assetScale); + var triggerDropCount = (int)(TriggerDropCount * assetScale); + var cookColorRect = ScaleRect(CookColorRect1080P, assetScale); + _logger.LogInformation("自动烹饪任务启动"); + var lastUiCheckTime = DateTime.MinValue; + var inCookUi = false; + int? peakColorCount = null; + int? peakCandidate = null; + var peakCandidateStableFrames = 0; + + while (!ct.IsCancellationRequested) + { + using var captureRegion = CaptureToRectArea(); + var now = DateTime.UtcNow; + if (!inCookUi || (now - lastUiCheckTime).TotalMilliseconds >= UiCheckIntervalMs) + { + var currentInCookUi = IsInCookUi(captureRegion); + if (currentInCookUi != inCookUi) + { + ResetPeakState(ref peakColorCount, ref peakCandidate, ref peakCandidateStableFrames); + } + + inCookUi = currentInCookUi; + lastUiCheckTime = now; + if (!inCookUi) + { + ResetPeakState(ref peakColorCount, ref peakCandidate, ref peakCandidateStableFrames); + } + else + { + if (Bv.ClickWhiteConfirmButton(captureRegion)) + { + ResetPeakState(ref peakColorCount, ref peakCandidate, ref peakCandidateStableFrames); + _logger.LogInformation("自动烹饪:{Text}", "自动点击确认"); + } + } + } + + if (inCookUi) + { + var currentColorCount = CountTargetColor(captureRegion, cookColorRect); + if (peakColorCount.HasValue) + { + if (currentColorCount <= peakColorCount.Value - triggerDropCount) + { + Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE); + _logger.LogInformation("自动烹饪:{Text}", $"烹饪条像素数量较峰值下降超过{triggerDropCount},按下空格。峰值:{peakColorCount.Value} 当前:{currentColorCount}"); + ResetPeakState(ref peakColorCount, ref peakCandidate, ref peakCandidateStableFrames); + } + } + else if (TryBuildPeak(currentColorCount, peakMinCount, ref peakCandidate, ref peakCandidateStableFrames, out var builtPeak)) + { + peakColorCount = builtPeak; + _logger.LogInformation("自动烹饪:{Text}", $"识别到完美烹饪条峰值像素数:{builtPeak}"); + } + } + + await Delay(checkIntervalMs, ct); + } + } + + private static void ResetPeakState(ref int? peakColorCount, ref int? peakCandidate, ref int peakCandidateStableFrames) + { + peakColorCount = null; + peakCandidate = null; + peakCandidateStableFrames = 0; + } + + private static bool TryBuildPeak(int currentColorCount, int peakMinCount, ref int? peakCandidate, ref int peakCandidateStableFrames, out int builtPeak) + { + builtPeak = 0; + if (currentColorCount <= peakMinCount) + { + peakCandidate = null; + peakCandidateStableFrames = 0; + return false; + } + + if (!peakCandidate.HasValue) + { + peakCandidate = currentColorCount; + peakCandidateStableFrames = 1; + return false; + } + + if (Math.Abs(currentColorCount - peakCandidate.Value) <= PeakTolerance) + { + peakCandidate = Math.Max(peakCandidate.Value, currentColorCount); + peakCandidateStableFrames++; + if (peakCandidateStableFrames >= PeakStableFrameCount && peakCandidate.Value > peakMinCount) + { + builtPeak = peakCandidate.Value; + peakCandidate = null; + peakCandidateStableFrames = 0; + return true; + } + + return false; + } + + peakCandidate = currentColorCount; + peakCandidateStableFrames = 1; + return false; + } + + private static Rect ScaleRect(Rect rect, double scale) + { + return new Rect( + (int)(rect.X * scale), + (int)(rect.Y * scale), + (int)(rect.Width * scale), + (int)(rect.Height * scale)); + } + + private bool IsInCookUi(ImageRegion captureRegion) + { + using var cookIcon = captureRegion.Find(ElementAssets.Instance.UiLeftTopCookIcon); + return cookIcon.IsExist(); + } + + private int CountTargetColor(ImageRegion captureRegion, Rect cookColorRect) + { + using var crop = captureRegion.DeriveCrop(cookColorRect); + using var rgb = new Mat(); + using var mask = new Mat(); + Cv2.CvtColor(crop.SrcMat, rgb, ColorConversionCodes.BGR2RGB); + Cv2.InRange(rgb, TargetCookColor, TargetCookColor, mask); + return Cv2.CountNonZero(mask); + } +} diff --git a/BetterGenshinImpact/GameTask/AutoCook/AutoCookTrigger.cs b/BetterGenshinImpact/GameTask/AutoCook/AutoCookTrigger.cs deleted file mode 100644 index 9079787b..00000000 --- a/BetterGenshinImpact/GameTask/AutoCook/AutoCookTrigger.cs +++ /dev/null @@ -1,54 +0,0 @@ -using BetterGenshinImpact.Core.Recognition.OpenCv; -using BetterGenshinImpact.GameTask.Common.Element.Assets; -using Microsoft.Extensions.Logging; -using OpenCvSharp; -using System.Linq; - -namespace BetterGenshinImpact.GameTask.AutoCook; - -public class AutoCookTrigger : ITaskTrigger -{ - private readonly ILogger _logger = App.GetLogger(); - - public string Name => "自动烹饪"; - public bool IsEnabled { get; set; } - public int Priority => 50; - public bool IsExclusive { get; set; } - - public void Init() - { - IsEnabled = TaskContext.Instance().Config.AutoCookConfig.Enabled; - IsExclusive = false; - } - - public void OnCapture(CaptureContent content) - { - // 判断是否处于烹饪界面 - IsExclusive = false; - content.CaptureRectArea.Find(ElementAssets.Instance.UiLeftTopCookIcon, _ => - { - IsExclusive = true; - var captureRect = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect; - using var region = content.CaptureRectArea.DeriveCrop(0, captureRect.Height / 2, captureRect.Width, captureRect.Height / 2); - var perfectBarRects = ContoursHelper.FindSpecifyColorRects(region.SrcMat, new Scalar(255, 192, 64), 0, 8); - if (perfectBarRects.Count >= 2) - { - // 点击烹饪按钮 - var btnList = ContoursHelper.FindSpecifyColorRects(region.SrcMat, new Scalar(255, 255, 192), 12, 12); - if (btnList.Count >= 1) - { - if (btnList.Count > 1) - { - _logger.LogWarning("自动烹饪:{Text}", "识别到多个结束烹饪按钮"); - btnList = [.. btnList.OrderByDescending(r => r.Width)]; - } - var btn = btnList[0]; - var x = btn.X + btn.Width / 2; - var y = btn.Y + btn.Height / 2; - region.ClickTo(x, y); - _logger.LogInformation("自动烹饪:{Text}", "点击结束按钮"); - } - } - }); - } -} diff --git a/BetterGenshinImpact/GameTask/GameTaskManager.cs b/BetterGenshinImpact/GameTask/GameTaskManager.cs index baff5531..674e6add 100644 --- a/BetterGenshinImpact/GameTask/GameTaskManager.cs +++ b/BetterGenshinImpact/GameTask/GameTaskManager.cs @@ -1,4 +1,4 @@ -using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.Core.Config; using BetterGenshinImpact.Core.Recognition.OpenCv; using BetterGenshinImpact.Core.Script.Dependence.Model.TimerConfig; using BetterGenshinImpact.GameTask.AutoFight.Assets; @@ -47,7 +47,6 @@ internal class GameTaskManager TriggerDictionary.TryAdd("QuickTeleport", new QuickTeleport.QuickTeleportTrigger()); TriggerDictionary.TryAdd("AutoSkip", new AutoSkip.AutoSkipTrigger()); TriggerDictionary.TryAdd("AutoFish", new AutoFishing.AutoFishingTrigger()); - TriggerDictionary.TryAdd("AutoCook", new AutoCook.AutoCookTrigger()); TriggerDictionary.TryAdd("AutoEat", new AutoEat.AutoEatTrigger()); TriggerDictionary.TryAdd("MapMask", new MapMaskTrigger()); TriggerDictionary.TryAdd("SkillCd", new SkillCdTrigger()); @@ -123,7 +122,6 @@ internal class GameTaskManager TriggerDictionary.GetValueOrDefault("AutoFish")?.Init(); TriggerDictionary.GetValueOrDefault("QuickTeleport")?.Init(); // TriggerDictionary.GetValueOrDefault("GameLoading")?.Init(); - TriggerDictionary.GetValueOrDefault("AutoCook")?.Init(); TriggerDictionary.GetValueOrDefault("AutoEat")?.Init(); TriggerDictionary.GetValueOrDefault("MapMask")?.Init(); TriggerDictionary.GetValueOrDefault("SkillCd")?.Init(); diff --git a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml index 9508881b..93052f00 100644 --- a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml @@ -1,4 +1,4 @@ - --> - - - - - - - - - - - - - - - - 可以自动演奏单个,也可以全自动完成整个专辑 - - - 点击查看使用教程 - - - - - - - - - - - - - - - - - - 进入演奏界面使用,下落模式必须选择垂落模式 - - - 点击查看使用教程 - - - - - - - - - - - - - - - - 进入专辑界面使用,自动演奏未完成乐曲 - - - 点击查看使用教程 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -2940,9 +2784,10 @@ - - - + + + @@ -2956,23 +2801,179 @@ - 自动使用输入的兑换码 + 可以自动演奏单个,也可以全自动完成整个专辑 - + + 点击查看使用教程 + + + + + + + + + + + + + + + + + 进入演奏界面使用,下落模式必须选择垂落模式 - + + 点击查看使用教程 + + + + + + + + + + + + + + + + 进入专辑界面使用,自动演奏未完成乐曲 - + + 点击查看使用教程 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EnableCommand="{Binding SwitchAutoCookCommand}" + EnableContent="{Binding SwitchAutoCookButtonText}" + IsChecked="{Binding SwitchAutoCookEnabled}" /> @@ -2988,23 +2989,27 @@ - + - - + + @@ -3232,6 +3237,69 @@ + + + + + + + + + + + + + + + + 自动使用输入的兑换码 + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - 手动烹饪时,自动在完美状态下结束烹饪(不用的时候请关闭) - - - - - -