mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-08 00:24:12 +08:00
162 lines
6.2 KiB
C#
162 lines
6.2 KiB
C#
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<AutoCookTask> _logger = App.GetLogger<AutoCookTask>();
|
|
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);
|
|
}
|
|
}
|