mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-19 09:35:48 +08:00
auto track mission
This commit is contained in:
@@ -232,11 +232,6 @@ public class AutoSkipTrigger : ITaskTrigger
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool AutoTrack(CaptureContent content)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private void HangoutOptionChoose(CaptureContent content)
|
||||
{
|
||||
var selectedRects = MatchTemplateHelper.MatchOnePicForOnePic(content.CaptureRectArea.SrcGreyMat, _autoSkipAssets.HangoutSelectedMat);
|
||||
|
||||
296
BetterGenshinImpact/GameTask/AutoSkip/AutoTrackTask.cs
Normal file
296
BetterGenshinImpact/GameTask/AutoSkip/AutoTrackTask.cs
Normal file
@@ -0,0 +1,296 @@
|
||||
using BetterGenshinImpact.Core.Recognition;
|
||||
using BetterGenshinImpact.Core.Recognition.OCR;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.Core.Simulator;
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
|
||||
using BetterGenshinImpact.GameTask.AutoSkip.Model;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Model;
|
||||
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using BetterGenshinImpact.Service.Notification;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.ViewModel.Pages;
|
||||
using Vanara.PInvoke;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using WinRT;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoSkip;
|
||||
|
||||
public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
|
||||
{
|
||||
// /// <summary>
|
||||
// /// 准备好前进了
|
||||
// /// </summary>
|
||||
// private bool _readyMoveForward = false;
|
||||
|
||||
/// <summary>
|
||||
/// 任务距离
|
||||
/// </summary>
|
||||
private Rect _missionDistanceRect = Rect.Empty;
|
||||
|
||||
public async void Start()
|
||||
{
|
||||
var hasLock = false;
|
||||
try
|
||||
{
|
||||
hasLock = await TaskSemaphore.WaitAsync(0);
|
||||
if (!hasLock)
|
||||
{
|
||||
Logger.LogError("启动自动追踪功能失败:当前存在正在运行中的独立任务,请不要重复执行任务!");
|
||||
return;
|
||||
}
|
||||
|
||||
SystemControl.ActivateWindow();
|
||||
|
||||
TrackMission();
|
||||
}
|
||||
catch (NormalEndException e)
|
||||
{
|
||||
Logger.LogInformation("自动追踪中断:" + e.Message);
|
||||
// NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Cancelled().Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e.Message);
|
||||
Logger.LogDebug(e.StackTrace);
|
||||
// NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Failure().Build());
|
||||
}
|
||||
finally
|
||||
{
|
||||
VisionContext.Instance().DrawContent.ClearAll();
|
||||
TaskSettingsPageViewModel.SetSwitchAutoTrackButtonText(false);
|
||||
Logger.LogInformation("→ {Text}", "自动追踪结束");
|
||||
|
||||
if (hasLock)
|
||||
{
|
||||
TaskSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TrackMission()
|
||||
{
|
||||
// 确认在主界面才会执行跟随任务
|
||||
var ra = GetRectAreaFromDispatcher();
|
||||
var paimonMenuRa = ra.Find(ElementAssets.Instance.PaimonMenuRo);
|
||||
if (!paimonMenuRa.IsExist())
|
||||
{
|
||||
Sleep(5000, param.Cts);
|
||||
return;
|
||||
}
|
||||
|
||||
// 任务文字有动效,等待2s重新截图
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(0, 7000);
|
||||
Sleep(2000, param.Cts);
|
||||
|
||||
// OCR 任务文字 在小地图下方
|
||||
var textRaList = OcrMissionTextRaList(paimonMenuRa);
|
||||
if (textRaList.Count == 0)
|
||||
{
|
||||
Logger.LogInformation("未找到任务文字");
|
||||
Sleep(5000, param.Cts);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从任务文字中提取距离
|
||||
var distance = GetDistanceFromMissionText(textRaList);
|
||||
Logger.LogInformation("任务追踪:{Text}", "距离" + distance + "m");
|
||||
if (distance >= 150)
|
||||
{
|
||||
// 距离大于150米,先传送到最近的传送点
|
||||
// J 打开任务 切换追踪打开地图 中心点就是任务点
|
||||
Simulation.SendInputEx.Keyboard.KeyPress(User32.VK.VK_J);
|
||||
Sleep(800, param.Cts);
|
||||
// TODO 识别是否在任务界面
|
||||
// 切换追踪
|
||||
var btn = ra.DerivePoint(CaptureRect.Width - 250, CaptureRect.Height - 60);
|
||||
btn.ClickCenter();
|
||||
Sleep(200, param.Cts);
|
||||
btn.ClickCenter();
|
||||
Sleep(1500, param.Cts);
|
||||
|
||||
// 寻找所有传送点
|
||||
ra = GetRectAreaFromDispatcher();
|
||||
var tpPointList = MatchTemplateHelper.MatchMultiPicForOnePic(ra.SrcGreyMat, QuickTeleportAssets.Instance.MapChooseIconGreyMatList);
|
||||
if (tpPointList.Count > 0)
|
||||
{
|
||||
// 选中离中心点最近的传送点
|
||||
var centerX = ra.Width / 2;
|
||||
var centerY = ra.Height / 2;
|
||||
var minDistance = double.MaxValue;
|
||||
var nearestRect = Rect.Empty;
|
||||
foreach (var tpPoint in tpPointList)
|
||||
{
|
||||
var distanceTp = Math.Sqrt(Math.Pow(Math.Abs(tpPoint.X - centerX), 2) + Math.Pow(Math.Abs(tpPoint.Y - centerY), 2));
|
||||
if (distanceTp < minDistance)
|
||||
{
|
||||
minDistance = distanceTp;
|
||||
nearestRect = tpPoint;
|
||||
}
|
||||
}
|
||||
|
||||
ra.Derive(nearestRect).ClickCenter();
|
||||
// 等待自动传送完成
|
||||
Sleep(2000, param.Cts);
|
||||
|
||||
if (Bv.IsInBigMapUi(GetRectAreaFromDispatcher()))
|
||||
{
|
||||
Logger.LogWarning("仍旧在大地图界面,传送失败");
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(500, param.Cts);
|
||||
NewRetry.Do(() =>
|
||||
{
|
||||
if (!Bv.IsInMainUi(GetRectAreaFromDispatcher()))
|
||||
{
|
||||
Logger.LogInformation("未进入到主界面,继续等待");
|
||||
throw new RetryException("未进入到主界面");
|
||||
}
|
||||
}, TimeSpan.FromSeconds(1), 100);
|
||||
StartTrackPoint();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("未找到传送点");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StartTrackPoint();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartTrackPoint()
|
||||
{
|
||||
// V键直接追踪
|
||||
Simulation.SendInputEx.Keyboard.KeyPress(User32.VK.VK_V);
|
||||
Sleep(3000, param.Cts);
|
||||
|
||||
var ra = GetRectAreaFromDispatcher();
|
||||
var blueTrackPointRa = ra.Find(ElementAssets.Instance.BlueTrackPoint);
|
||||
if (blueTrackPointRa.IsExist())
|
||||
{
|
||||
MakeBlueTrackPointDirectlyAbove();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("首次未找到追踪点");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 找到追踪点并调整方向
|
||||
/// </summary>
|
||||
private void MakeBlueTrackPointDirectlyAbove()
|
||||
{
|
||||
// return new Task(() =>
|
||||
// {
|
||||
int prevMoveX = 0;
|
||||
bool wDown = false;
|
||||
while (!param.Cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var ra = GetRectAreaFromDispatcher();
|
||||
var blueTrackPointRa = ra.Find(ElementAssets.Instance.BlueTrackPoint);
|
||||
if (blueTrackPointRa.IsExist())
|
||||
{
|
||||
// 调整整体高度
|
||||
var centerY = blueTrackPointRa.Y + blueTrackPointRa.Height / 2;
|
||||
if (centerY > CaptureRect.Height / 2)
|
||||
{
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(-100, 0);
|
||||
if (wDown)
|
||||
{
|
||||
Simulation.SendInputEx.Keyboard.KeyUp(User32.VK.VK_W);
|
||||
wDown = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 调整方向
|
||||
var centerX = blueTrackPointRa.X + blueTrackPointRa.Width / 2;
|
||||
var moveX = centerX - CaptureRect.Width / 2;
|
||||
if (moveX != 0)
|
||||
{
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(moveX, 0);
|
||||
}
|
||||
|
||||
if (moveX == 0 || prevMoveX * moveX < 0)
|
||||
{
|
||||
if (!wDown)
|
||||
{
|
||||
Simulation.SendInputEx.Keyboard.KeyDown(User32.VK.VK_W);
|
||||
wDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.Abs(moveX) < 50 && Math.Abs(centerY - CaptureRect.Height / 2) < 200)
|
||||
{
|
||||
if (wDown)
|
||||
{
|
||||
Simulation.SendInputEx.Keyboard.KeyUp(User32.VK.VK_W);
|
||||
wDown = false;
|
||||
}
|
||||
// 识别距离
|
||||
var text = OcrFactory.Paddle.OcrWithoutDetector(ra.SrcGreyMat[_missionDistanceRect]);
|
||||
if (StringUtils.TryExtractPositiveInt(text) is > -1 and <= 3)
|
||||
{
|
||||
Logger.LogInformation("任务追踪:到达目标,识别结果[{Text}]", text);
|
||||
break;
|
||||
}
|
||||
Logger.LogInformation("任务追踪:到达目标");
|
||||
break;
|
||||
}
|
||||
|
||||
prevMoveX = moveX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 随机移动
|
||||
Logger.LogInformation("未找到追踪点");
|
||||
}
|
||||
|
||||
Simulation.SendInputEx.Mouse.MoveMouseBy(0, 500); // 保证俯视角
|
||||
Sleep(100);
|
||||
}
|
||||
// });
|
||||
}
|
||||
|
||||
private int GetDistanceFromMissionText(List<RectArea> textRaList)
|
||||
{
|
||||
// 打印所有任务文字
|
||||
var text = textRaList.Aggregate(string.Empty, (current, textRa) => current + textRa.Text.Trim() + "|");
|
||||
Logger.LogInformation("任务追踪:{Text}", text);
|
||||
|
||||
foreach (var textRa in textRaList)
|
||||
{
|
||||
if (textRa.Text.Length < 8 && (textRa.Text.Contains("m") || textRa.Text.Contains("M")))
|
||||
{
|
||||
_missionDistanceRect = textRa.ConvertRelativePositionToCaptureArea();
|
||||
return StringUtils.TryExtractPositiveInt(textRa.Text);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private List<RectArea> OcrMissionTextRaList(RectArea paimonMenuRa)
|
||||
{
|
||||
return GetRectAreaFromDispatcher().FindMulti(new RecognitionObject
|
||||
{
|
||||
RecognitionType = RecognitionTypes.Ocr,
|
||||
RegionOfInterest = new Rect(paimonMenuRa.X, paimonMenuRa.Y - 15 + 210,
|
||||
(int)(300 * AssetScale), (int)(100 * AssetScale))
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Threading;
|
||||
using BetterGenshinImpact.GameTask.Model;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoSkip.Model;
|
||||
|
||||
public class AutoTrackParam : BaseTaskParam
|
||||
{
|
||||
public AutoTrackParam(CancellationTokenSource cts) : base(cts)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -10,11 +10,10 @@ public class ElementAssets : BaseAssets<ElementAssets>
|
||||
public RecognitionObject BtnWhiteCancel;
|
||||
public RecognitionObject BtnBlackConfirm;
|
||||
public RecognitionObject PaimonMenuRo;
|
||||
public RecognitionObject BlueTrackPoint;
|
||||
|
||||
private ElementAssets()
|
||||
{
|
||||
var info = TaskContext.Instance().SystemInfo;
|
||||
|
||||
// 按钮
|
||||
BtnWhiteConfirm = new RecognitionObject
|
||||
{
|
||||
@@ -44,8 +43,18 @@ public class ElementAssets : BaseAssets<ElementAssets>
|
||||
Name = "PaimonMenu",
|
||||
RecognitionType = RecognitionTypes.TemplateMatch,
|
||||
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "paimon_menu.png"),
|
||||
RegionOfInterest = new Rect(0, 0, info.CaptureAreaRect.Width / 4, info.CaptureAreaRect.Height / 4),
|
||||
RegionOfInterest = new Rect(0, 0, CaptureRect.Width / 4, CaptureRect.Height / 4),
|
||||
DrawOnWindow = false
|
||||
}.InitTemplate();
|
||||
|
||||
// 任务追踪点位
|
||||
BlueTrackPoint = new RecognitionObject
|
||||
{
|
||||
Name = "BlueTrackPoint",
|
||||
RecognitionType = RecognitionTypes.TemplateMatch,
|
||||
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "blue_track_point_28x.png"),
|
||||
RegionOfInterest = new Rect((int)(300 * AssetScale), 0, CaptureRect.Width - (int)(600 * AssetScale), CaptureRect.Height),
|
||||
DrawOnWindow = true
|
||||
}.InitTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
|
||||
using BetterGenshinImpact.GameTask.Model;
|
||||
using Fischless.GameCapture;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
@@ -132,8 +133,18 @@ public class TaskControl
|
||||
return CaptureToContent(TaskTriggerDispatcher.GlobalGameCapture);
|
||||
}
|
||||
|
||||
public static RectArea CaptureToRectArea()
|
||||
{
|
||||
return CaptureToContent().CaptureRectArea;
|
||||
}
|
||||
|
||||
public static CaptureContent GetContentFromDispatcher()
|
||||
{
|
||||
return TaskTriggerDispatcher.Instance().GetLastCaptureContent();
|
||||
}
|
||||
|
||||
public static RectArea GetRectAreaFromDispatcher()
|
||||
{
|
||||
return GetContentFromDispatcher().CaptureRectArea;
|
||||
}
|
||||
}
|
||||
|
||||
11
BetterGenshinImpact/GameTask/Model/BaseIndependentTask.cs
Normal file
11
BetterGenshinImpact/GameTask/Model/BaseIndependentTask.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Model;
|
||||
|
||||
public class BaseIndependentTask
|
||||
{
|
||||
protected SystemInfo Info => TaskContext.Instance().SystemInfo;
|
||||
protected RECT CaptureRect => TaskContext.Instance().SystemInfo.CaptureAreaRect;
|
||||
protected double AssetScale => TaskContext.Instance().SystemInfo.AssetScale;
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
public enum IndependentTaskEnum
|
||||
{
|
||||
|
||||
AutoGeniusInvokation,
|
||||
AutoWood,
|
||||
AutoFight,
|
||||
AutoDomain,
|
||||
}
|
||||
AutoTrack,
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sdcb.PaddleOCR;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Model;
|
||||
|
||||
@@ -252,6 +254,11 @@ public class RectArea : IDisposable
|
||||
if (ro.RegionOfInterest != Rect.Empty)
|
||||
{
|
||||
// TODO roi 是可以加缓存的
|
||||
if (!(0 <= ro.RegionOfInterest.X && 0 <= ro.RegionOfInterest.Width && ro.RegionOfInterest.X + ro.RegionOfInterest.Width <= roi.Cols
|
||||
&& 0 <= ro.RegionOfInterest.Y && 0 <= ro.RegionOfInterest.Height && ro.RegionOfInterest.Y + ro.RegionOfInterest.Height <= roi.Rows))
|
||||
{
|
||||
Logger.LogError("输入图像{W1}x{H1},模板ROI区域{H2}x{W2},边界溢出!", roi.Width, roi.Height, ro.RegionOfInterest.Width, ro.RegionOfInterest.Height);
|
||||
}
|
||||
roi = new Mat(roi, ro.RegionOfInterest);
|
||||
}
|
||||
|
||||
@@ -453,7 +460,12 @@ public class RectArea : IDisposable
|
||||
|
||||
if (result.Regions.Length > 0)
|
||||
{
|
||||
var resRaList = result.Regions.Select(r => this.Derive(r.Rect.BoundingRect() + new Point(ro.RegionOfInterest.X, ro.RegionOfInterest.Y))).ToList();
|
||||
var resRaList = result.Regions.Select(r =>
|
||||
{
|
||||
var newRa = this.Derive(r.Rect.BoundingRect() + new Point(ro.RegionOfInterest.X, ro.RegionOfInterest.Y));
|
||||
newRa.Text = r.Text;
|
||||
return newRa;
|
||||
}).ToList();
|
||||
if (ro.DrawOnWindow && !string.IsNullOrEmpty(ro.Name))
|
||||
{
|
||||
VisionContext.Instance().DrawContent.PutOrRemoveRectList(ro.Name, result.ToRectDrawableListOffset(ro.RegionOfInterest.X, ro.RegionOfInterest.Y));
|
||||
@@ -531,6 +543,18 @@ public class RectArea : IDisposable
|
||||
return new RectArea(rect, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 派生2x2区域(无图片)
|
||||
/// 方便用于点击
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public RectArea DerivePoint(int x, int y)
|
||||
{
|
||||
return new RectArea(new Rect(x, y, 2, 2), this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OCR识别
|
||||
/// </summary>
|
||||
|
||||
@@ -21,6 +21,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.GameTask.AutoSkip;
|
||||
using BetterGenshinImpact.GameTask.AutoSkip.Model;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask
|
||||
@@ -216,6 +218,10 @@ namespace BetterGenshinImpact.GameTask
|
||||
{
|
||||
Task.Run(() => { new AutoDomainTask((AutoDomainParam)param).Start(); });
|
||||
}
|
||||
else if (taskType == IndependentTaskEnum.AutoTrack)
|
||||
{
|
||||
Task.Run(() => { new AutoTrackTask((AutoTrackParam)param).Start(); });
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => Stop();
|
||||
|
||||
Reference in New Issue
Block a user