mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-26 09:39:49 +08:00
feat: finsh auto fish
This commit is contained in:
@@ -25,7 +25,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="GameTask\AutoFishing\" />
|
||||
<Folder Include="GameTask\AutoGeniusInvokation\" />
|
||||
<Folder Include="GameTask\QuickEnhanceArtifacts\" />
|
||||
<Folder Include="GameTask\UseActiveCode\" />
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Vision.Recognition.Helper.OpenCv;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
{
|
||||
public class AutoFishingImageRecognition
|
||||
{
|
||||
/// <summary>
|
||||
/// 钓鱼条矩形识别
|
||||
/// </summary>
|
||||
/// <param name="src"></param>
|
||||
/// <returns></returns>
|
||||
public static List<Rect>? GetFishBarRect(Mat src)
|
||||
{
|
||||
using var mask = new Mat();
|
||||
using var rgbMat = new Mat();
|
||||
|
||||
Cv2.CvtColor(src, rgbMat, ColorConversionCodes.BGR2RGB);
|
||||
var lowPurple = new Scalar(255, 255, 192);
|
||||
var highPurple = new Scalar(255, 255, 192);
|
||||
Cv2.InRange(rgbMat, lowPurple, highPurple, mask);
|
||||
Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化
|
||||
|
||||
Cv2.FindContours(mask, out var contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple, null);
|
||||
if (contours.Length > 0)
|
||||
{
|
||||
var boxes = contours.Select(Cv2.BoundingRect).Where(w => w.Height >= 10);
|
||||
return boxes.ToList();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
227
BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
Normal file
227
BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Media3D;
|
||||
using OpenCvSharp;
|
||||
using Vision.Recognition;
|
||||
using Vision.Recognition.Helper.OpenCv;
|
||||
using Vision.Recognition.Task;
|
||||
using WindowsInput;
|
||||
using static Vanara.PInvoke.Kernel32;
|
||||
using static Vanara.PInvoke.User32;
|
||||
using WinRT;
|
||||
using Windows.ApplicationModel.Contacts;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFishing
|
||||
{
|
||||
public class AutoFishingTrigger : ITaskTrigger
|
||||
{
|
||||
private readonly ILogger<AutoFishingTrigger> _logger = App.GetLogger<AutoFishingTrigger>();
|
||||
|
||||
public string Name => "自动钓鱼";
|
||||
public bool IsEnabled { get; set; }
|
||||
public int Priority => 15;
|
||||
|
||||
/// <summary>
|
||||
/// 钓鱼是要独占模式的
|
||||
/// 在钓鱼的时候,不应该有其他任务在执行
|
||||
/// 在触发器发现正在钓鱼的时候,启用独占模式
|
||||
/// </summary>
|
||||
public bool IsExclusive { get; set; }
|
||||
|
||||
public void Init()
|
||||
{
|
||||
IsEnabled = true;
|
||||
IsExclusive = true;
|
||||
|
||||
// 钓鱼变量初始化
|
||||
_findFishBoxTips = false;
|
||||
}
|
||||
|
||||
private Rect _fishBoxRect = new(0, 0, 0, 0);
|
||||
|
||||
public void OnCapture(CaptureContent content)
|
||||
{
|
||||
// TODO 进入独占的判定
|
||||
|
||||
if (_fishBoxRect.Width == 0)
|
||||
{
|
||||
GetFishBoxArea(content.SrcMat);
|
||||
}
|
||||
else
|
||||
{
|
||||
Fishing(content, new Mat(content.SrcMat, _fishBoxRect));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取钓鱼框的位置
|
||||
/// </summary>
|
||||
private void GetFishBoxArea(Mat srcMat)
|
||||
{
|
||||
var rects = AutoFishingImageRecognition.GetFishBarRect(srcMat);
|
||||
if (rects != null && rects.Count == 2)
|
||||
{
|
||||
if (Math.Abs(rects[0].Height - rects[1].Height) > 10)
|
||||
{
|
||||
Debug.WriteLine("两个矩形高度差距过大,未识别到钓鱼框");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rects[0].Width < rects[1].Width)
|
||||
{
|
||||
_cur = rects[0];
|
||||
_left = rects[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
_cur = rects[1];
|
||||
_left = rects[0];
|
||||
}
|
||||
|
||||
// cur 是游标位置, 在初始状态下,cur 一定在left左边
|
||||
if (_left.X < _cur.X)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int hExtra = _cur.Height, vExtra = _cur.Height / 4;
|
||||
_fishBoxRect = new Rect(_cur.X - hExtra, _cur.Y - vExtra,
|
||||
(_left.X + _left.Width / 2 - _cur.X) * 2 + hExtra * 2, _cur.Height + vExtra * 2);
|
||||
VisionContext.Instance().DrawContent.PutRect("FishBox", _fishBoxRect.ToWindowsRectangle());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int _noRectsCount = 0;
|
||||
private bool _isFishingProcess = false; // 提杆后会设置为true
|
||||
private Rect _cur, _left, _right;
|
||||
private MOUSEEVENTF _prevMouseEvent = 0x0;
|
||||
private bool _findFishBoxTips;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 钓鱼拉条
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="fishBarMat"></param>
|
||||
private void Fishing(CaptureContent content, Mat fishBarMat)
|
||||
{
|
||||
List<Rect>? rects = AutoFishingImageRecognition.GetFishBarRect(fishBarMat);
|
||||
if (rects != null && rects.Count > 0)
|
||||
{
|
||||
var simulator = new InputSimulator();
|
||||
if (rects.Count >= 2 && _prevMouseEvent == 0x0 && !_findFishBoxTips)
|
||||
{
|
||||
_findFishBoxTips = true;
|
||||
_logger.LogInformation(" 识别到钓鱼框,自动拉扯中...");
|
||||
}
|
||||
|
||||
// 超过3个矩形是异常情况,取高度最高的三个矩形进行识别
|
||||
if (rects.Count > 3)
|
||||
{
|
||||
rects.Sort((a, b) => b.Height.CompareTo(a.Height));
|
||||
rects.RemoveRange(3, rects.Count - 3);
|
||||
}
|
||||
|
||||
Debug.WriteLine($"识别到{rects.Count} 个矩形");
|
||||
if (rects.Count == 2)
|
||||
{
|
||||
if (rects[0].Width < rects[1].Width)
|
||||
{
|
||||
_cur = rects[0];
|
||||
_left = rects[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
_cur = rects[1];
|
||||
_left = rects[0];
|
||||
}
|
||||
|
||||
PutRects(_left, _cur, new Rect());
|
||||
|
||||
if (_cur.X < _left.X)
|
||||
{
|
||||
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonDown();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
|
||||
Debug.WriteLine("进度不到 左键按下");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonUp();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
|
||||
Debug.WriteLine("进度超出 左键松开");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rects.Count == 3)
|
||||
{
|
||||
rects.Sort((a, b) => a.X.CompareTo(b.X));
|
||||
_left = rects[0];
|
||||
_cur = rects[1];
|
||||
_right = rects[2];
|
||||
PutRects(_left, _cur, _right);
|
||||
|
||||
if (_right.X + _right.Width - (_cur.X + _cur.Width) <= _cur.X - _left.X)
|
||||
{
|
||||
if (_prevMouseEvent == MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonUp();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTUP;
|
||||
Debug.WriteLine("进入框内中间 左键松开");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_prevMouseEvent != MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN)
|
||||
{
|
||||
simulator.Mouse.LeftButtonDown();
|
||||
_prevMouseEvent = MOUSEEVENTF.MOUSEEVENTF_LEFTDOWN;
|
||||
Debug.WriteLine("未到框内中间 左键按下");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PutRects(new Rect(), new Rect(), new Rect());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PutRects(new Rect(), new Rect(), new Rect());
|
||||
_noRectsCount++;
|
||||
// 2s 没有矩形视为已经完成钓鱼
|
||||
if (_noRectsCount >= content.FrameRate * 2 && _prevMouseEvent != 0x0)
|
||||
{
|
||||
_findFishBoxTips = false;
|
||||
_isFishingProcess = false;
|
||||
_prevMouseEvent = 0x0;
|
||||
_logger.LogInformation(" 钓鱼结束");
|
||||
//_logger.LogInformation(@"└------------------------┘");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PutRects(Rect left, Rect cur, Rect right)
|
||||
{
|
||||
var list = new List<(string, System.Windows.Rect)>
|
||||
{
|
||||
("FishingBarLeft", left.ToWindowsRectangleOffset(_fishBoxRect.X, _fishBoxRect.Y)),
|
||||
("FishingBarCur", cur.ToWindowsRectangleOffset(_fishBoxRect.X, _fishBoxRect.Y)),
|
||||
("FishingBarRight", right.ToWindowsRectangleOffset(_fishBoxRect.X, _fishBoxRect.Y))
|
||||
};
|
||||
VisionContext.Instance().DrawContent.PutOrRemoveRectList(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ using Point = OpenCvSharp.Point;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoSkip
|
||||
{
|
||||
/// <summary>
|
||||
/// 自动剧情有选项点击,必须使用BitBlt
|
||||
/// </summary>
|
||||
public class AutoSkipTrigger : ITaskTrigger
|
||||
{
|
||||
private readonly ILogger<AutoSkipTrigger> _logger = App.GetLogger<AutoSkipTrigger>();
|
||||
|
||||
@@ -30,8 +30,11 @@ namespace BetterGenshinImpact.GameTask
|
||||
// .ForEach(type => LoadedTriggers.Add(type.CreateInstance<ITaskTrigger>()));
|
||||
// });
|
||||
|
||||
List<ITaskTrigger> loadedTriggers = new();
|
||||
loadedTriggers.Add(new AutoSkip.AutoSkipTrigger());
|
||||
List<ITaskTrigger> loadedTriggers = new()
|
||||
{
|
||||
new AutoSkip.AutoSkipTrigger(),
|
||||
new AutoFishing.AutoFishingTrigger()
|
||||
};
|
||||
|
||||
loadedTriggers.ForEach(i => i.Init());
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace BetterGenshinImpact.GameTask
|
||||
_timer.Elapsed += Tick;
|
||||
}
|
||||
|
||||
public void Start(CaptureMode mode, int frameRate = 30)
|
||||
public void Start(CaptureMode mode, int frameRate = 60)
|
||||
{
|
||||
IntPtr hWnd = SystemControl.FindGenshinImpactHandle();
|
||||
if (hWnd == IntPtr.Zero)
|
||||
@@ -98,7 +98,7 @@ namespace BetterGenshinImpact.GameTask
|
||||
}
|
||||
|
||||
// 循环执行所有触发器 有独占状态的触发器的时候只执行独占触发器
|
||||
var content = new CaptureContent(bitmap, _frameIndex);
|
||||
var content = new CaptureContent(bitmap, _frameIndex, _frameRate);
|
||||
var exclusiveTrigger = _triggers.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true });
|
||||
if (exclusiveTrigger != null)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:viewModel="clr-namespace:BetterGenshinImpact.ViewModel"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="200">
|
||||
Title="MainWindow" Height="200" Width="300">
|
||||
<Window.DataContext>
|
||||
<viewModel:MainWindowViewModel />
|
||||
</Window.DataContext>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Policy;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Vision.Recognition
|
||||
{
|
||||
@@ -33,6 +35,41 @@ namespace Vision.Recognition
|
||||
MaskWindow.Instance().Refresh();
|
||||
}
|
||||
|
||||
public void PutOrRemoveRectList(List<(string, System.Windows.Rect)> list)
|
||||
{
|
||||
bool changed = false;
|
||||
list.ForEach(item =>
|
||||
{
|
||||
var newRect = item.Item2;
|
||||
if (newRect.IsEmpty)
|
||||
{
|
||||
if (RectList.TryGetValue(item.Item1, out _))
|
||||
{
|
||||
RectList.TryRemove(item.Item1, out _);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RectList.TryGetValue(item.Item1, out var prevRect))
|
||||
{
|
||||
if (newRect == prevRect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
RectList[item.Item1] = newRect;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
if (changed)
|
||||
{
|
||||
MaskWindow.Instance().Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveRect(string key)
|
||||
{
|
||||
if (RectList.TryGetValue(key, out _))
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace Vision.Recognition.Helper.OpenCv
|
||||
return new System.Windows.Rect(rect.X, rect.Y, rect.Width, rect.Height);
|
||||
}
|
||||
|
||||
public static System.Windows.Rect ToWindowsRectangleOffset(this OpenCvSharp.Rect rect, int offsetX, int offsetY)
|
||||
{
|
||||
return new System.Windows.Rect(rect.X + offsetX, rect.Y + offsetY, rect.Width, rect.Height);
|
||||
}
|
||||
|
||||
public static Rectangle ToDrawingRectangle(this OpenCvSharp.Rect rect)
|
||||
{
|
||||
return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
@@ -69,16 +69,23 @@ namespace Vision.Recognition
|
||||
{
|
||||
foreach (var kv in VisionContext.Instance().DrawContent.RectList)
|
||||
{
|
||||
drawingContext.DrawRectangle(Brushes.Transparent, new Pen(Brushes.Red, 2), kv.Value);
|
||||
if (!kv.Value.IsEmpty)
|
||||
{
|
||||
drawingContext.DrawRectangle(Brushes.Transparent, new Pen(Brushes.Red, 2), kv.Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach (var kv in VisionContext.Instance().DrawContent.TextList)
|
||||
{
|
||||
drawingContext.DrawText(new FormattedText(kv.Value.Item2,
|
||||
CultureInfo.GetCultureInfo("zh-cn"),
|
||||
FlowDirection.LeftToRight,
|
||||
MyTypeface,
|
||||
36, Brushes.Black, 1), kv.Value.Item1);
|
||||
if (kv.Value.Item1.X != 0 || kv.Value.Item1.Y != 0)
|
||||
{
|
||||
drawingContext.DrawText(new FormattedText(kv.Value.Item2,
|
||||
CultureInfo.GetCultureInfo("zh-cn"),
|
||||
FlowDirection.LeftToRight,
|
||||
MyTypeface,
|
||||
36, Brushes.Black, 1), kv.Value.Item1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -17,11 +17,13 @@ namespace Vision.Recognition.Task
|
||||
{
|
||||
public Bitmap SrcBitmap { get; }
|
||||
public int FrameIndex { get; private set; }
|
||||
public int FrameRate { get; private set; }
|
||||
|
||||
public CaptureContent(Bitmap srcBitmap, int frameIndex)
|
||||
public CaptureContent(Bitmap srcBitmap, int frameIndex, int frameRate)
|
||||
{
|
||||
SrcBitmap = srcBitmap;
|
||||
FrameIndex = frameIndex;
|
||||
FrameRate = frameRate;
|
||||
}
|
||||
|
||||
private Mat? _srcMat;
|
||||
|
||||
Reference in New Issue
Block a user