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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 自动使用输入的兑换码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
- 手动烹饪时,自动在完美状态下结束烹饪(不用的时候请关闭)
-
-
-
-
-
-