diff --git a/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs b/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs
index cffcbbc8..cd0eac20 100644
--- a/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs
+++ b/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs
@@ -1,5 +1,4 @@
using System;
-using System.Formats.Asn1;
using System.Threading;
using Vanara.PInvoke;
@@ -65,6 +64,15 @@ public class PostMessageSimulator
return this;
}
+ public PostMessageSimulator LongKeyPress(User32.VK vk)
+ {
+ User32.PostMessage(_hWnd, User32.WindowMessage.WM_KEYDOWN, (nint)vk, 0x1e0001);
+ Thread.Sleep(1000);
+ User32.PostMessage(_hWnd, User32.WindowMessage.WM_CHAR, (nint)vk, 0x1e0001);
+ User32.PostMessage(_hWnd, User32.WindowMessage.WM_KEYUP, (nint)vk, (nint)0xc01e0001);
+ return this;
+ }
+
public PostMessageSimulator KeyDown(User32.VK vk)
{
User32.PostMessage(_hWnd, User32.WindowMessage.WM_KEYDOWN, (nint)vk, 0x1e0001);
diff --git a/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs b/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs
index 8edbf117..27832aa1 100644
--- a/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using static Vanara.PInvoke.User32;
+using BetterGenshinImpact.GameTask.AutoFight;
namespace BetterGenshinImpact.GameTask.AutoDomain;
@@ -16,12 +17,12 @@ public class AutoDomainTask
private AutoDomainParam _taskParam;
- private readonly PostMessageSimulator _postMessage;
+ private readonly PostMessageSimulator _simulator;
public AutoDomainTask(AutoDomainParam taskParam)
{
_taskParam = taskParam;
- _postMessage = Simulation.PostMessage(TaskContext.Instance().GameHandle);
+ _simulator = AutoFightContext.Instance().Simulator;
}
public void Start()
@@ -31,6 +32,16 @@ public class AutoDomainTask
Init();
var combatScenes = new CombatScenes();
combatScenes.InitializeTeam(GetContentFromDispatcher());
+ // test code
+ for (int i = 3 - 1; i >= 0; i--)
+ {
+ combatScenes.Avatars[0].Switch();
+ combatScenes.Avatars[1].Switch();
+ combatScenes.Avatars[2].Switch();
+ combatScenes.Avatars[3].Switch();
+ Sleep(3000);
+ }
+
// 1. 走到钥匙处启动
// WalkToStartDomain();
@@ -61,7 +72,7 @@ public class AutoDomainTask
{
await Task.Run(() =>
{
- _postMessage.KeyDown(VK.VK_W);
+ _simulator.KeyDown(VK.VK_W);
try
{
while (true)
@@ -82,7 +93,7 @@ public class AutoDomainTask
}
finally
{
- _postMessage.KeyUp(VK.VK_W);
+ _simulator.KeyUp(VK.VK_W);
}
});
}
diff --git a/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs b/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs
index b1afeb6b..1b7b1b35 100644
--- a/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs
+++ b/BetterGenshinImpact/GameTask/AutoFight/Assets/AutoFightAssets.cs
@@ -1,14 +1,29 @@
using BetterGenshinImpact.Core.Recognition;
+using OpenCvSharp;
namespace BetterGenshinImpact.GameTask.AutoFight.Assets;
public class AutoFightAssets
{
+ public Rect TeamRect;
+ public Rect ERect;
+ public Rect QRect;
public RecognitionObject WandererIconRa;
+
public AutoFightAssets()
{
var info = TaskContext.Instance().SystemInfo;
+ var captureRect = info.CaptureAreaRect;
+ var assetScale = info.AssetScale;
+
+ TeamRect = new Rect(captureRect.Width - (int)(355 * assetScale), (int)(220 * assetScale),
+ (int)(355 * assetScale), (int)(465 * assetScale));
+ ERect = new Rect(captureRect.Width - (int)(267 * assetScale), captureRect.Height - (int)(132 * assetScale),
+ (int)(77 * assetScale), (int)(77 * assetScale));
+ QRect = new Rect(captureRect.Width - (int)(157 * assetScale), captureRect.Height - (int)(165 * assetScale),
+ (int)(110 * assetScale), (int)(110 * assetScale));
+
WandererIconRa = new RecognitionObject
{
Name = "WandererIcon",
diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightContext.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightContext.cs
new file mode 100644
index 00000000..01871da5
--- /dev/null
+++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightContext.cs
@@ -0,0 +1,48 @@
+using BetterGenshinImpact.Core.Simulator;
+using BetterGenshinImpact.GameTask.AutoFight.Assets;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BetterGenshinImpact.GameTask.AutoFight;
+
+///
+/// 自动战斗上下文
+/// 请在启动BetterGI以后再初始化
+///
+public class AutoFightContext
+{
+
+ private static AutoFightContext? _uniqueInstance;
+ private static readonly object Locker = new();
+
+ private AutoFightContext()
+ {
+ FightAssets = new();
+ Simulator = Simulation.PostMessage(TaskContext.Instance().GameHandle);
+ }
+
+ public static AutoFightContext Instance()
+ {
+ if (_uniqueInstance == null)
+ {
+ lock (Locker)
+ {
+ _uniqueInstance ??= new AutoFightContext();
+ }
+ }
+ return _uniqueInstance;
+ }
+
+ ///
+ /// find资源
+ ///
+ public readonly AutoFightAssets FightAssets;
+
+ ///
+ /// 战斗专用的PostMessage模拟键鼠操作
+ ///
+ public readonly PostMessageSimulator Simulator;
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs
index 19decfc7..c404c8e1 100644
--- a/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs
+++ b/BetterGenshinImpact/GameTask/AutoFight/Model/Avatar.cs
@@ -1,4 +1,11 @@
-using BetterGenshinImpact.GameTask.AutoFight.Config;
+using System.Linq;
+using BetterGenshinImpact.GameTask.AutoFight.Config;
+using OpenCvSharp;
+using System.Threading;
+using Microsoft.Extensions.Logging;
+using Vanara.PInvoke;
+using static BetterGenshinImpact.GameTask.Common.TaskControl;
+using BetterGenshinImpact.Core.Recognition.OpenCv;
namespace BetterGenshinImpact.GameTask.AutoFight.Model;
@@ -42,20 +49,26 @@ public class Avatar
///
public double BurstCd { get; set; }
- ///
- /// 元素战技是否就绪
- ///
- public bool IsSkillReady { get; set; }
-
///
/// 元素爆发是否就绪
///
public bool IsBurstReady { get; set; }
- public Avatar(string name, int index)
+ ///
+ /// 名字所在矩形位置
+ ///
+ public Rect NameRect { get; set; }
+
+ ///
+ /// 名字右边的编号位置
+ ///
+ public Rect IndexRect { get; set; }
+
+ public Avatar(string name, int index, Rect nameRect)
{
Name = name;
Index = index;
+ NameRect = nameRect;
var ca = DefaultAutoFightConfig.CombatAvatarMap[name];
NameEn = ca.NameEn;
@@ -64,4 +77,154 @@ public class Avatar
SkillHoldCd = ca.SkillHoldCd;
BurstCd = ca.BurstCd;
}
+
+ ///
+ /// 切换到本角色
+ /// 切换cd是1秒,如果切换失败,会尝试再次切换,最多尝试5次
+ ///
+ public void Switch()
+ {
+ for (var i = 0; i < 5; i++)
+ {
+ if (IsActive(GetContentFromDispatcher()))
+ {
+ return;
+ }
+
+ AutoFightContext.Instance().Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
+ Thread.Sleep(1050); // 比1秒多一点,给截图留出时间
+ }
+ }
+
+ ///
+ /// 是否出战状态
+ ///
+ ///
+ public bool IsActive(CaptureContent content)
+ {
+ // 通过寻找右侧人物编号来判断是否出战
+ if (IndexRect == Rect.Empty)
+ {
+ var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
+ // 剪裁出队伍区域
+ var teamRa = content.CaptureRectArea.Crop(AutoFightContext.Instance().FightAssets.TeamRect);
+ var blockX = NameRect.X + NameRect.Width * 2 - 10;
+ var block = teamRa.Crop(new Rect(blockX, NameRect.Y, teamRa.Width - blockX, NameRect.Height * 2));
+ // 取白色区域
+ var bMat = OpenCvCommonHelper.Threshold(block.SrcMat, new Scalar(255, 255, 255), new Scalar(255, 255, 255));
+ // 矩形识别
+ Cv2.FindContours(bMat, out var contours, out _, RetrievalModes.External,
+ ContourApproximationModes.ApproxSimple);
+ if (contours.Length > 0)
+ {
+ var boxes = contours.Select(Cv2.BoundingRect).Where(w => w.Width >= 20 * assetScale && w.Height >= 18 * assetScale).OrderByDescending(w => w.Width).ToList();
+ if (boxes.Any())
+ {
+ IndexRect = boxes.First();
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // 剪裁出IndexRect区域
+ var teamRa = content.CaptureRectArea.Crop(AutoFightContext.Instance().FightAssets.TeamRect);
+ var blockX = NameRect.X + NameRect.Width * 2 - 10;
+ var indexBlock = teamRa.Crop(new Rect(blockX + IndexRect.X, NameRect.Y + IndexRect.Y, IndexRect.Width, IndexRect.Height));
+ Cv2.ImWrite("indexBlock_" + Name + ".png", indexBlock.SrcMat);
+ int count = OpenCvCommonHelper.CountGrayMatColor(indexBlock.SrcGreyMat, 255);
+ if (count * 1.0 / (IndexRect.Width * IndexRect.Height) > 0.7)
+ {
+ return false;
+ }
+ }
+
+ Logger.LogInformation("{Name} 当前出战", Name);
+ return true;
+ }
+
+ ///
+ /// 使用元素战技 E
+ ///
+ public void UseSkill(bool hold = false)
+ {
+ var cd = GetSkillCurrentCd(GetContentFromDispatcher());
+ if (cd > 0)
+ {
+ Logger.LogInformation("{Name} 元素战技仍在CD中,还剩{Cd}s,跳过此操作", Name, cd);
+ }
+ else
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ if (hold)
+ {
+ AutoFightContext.Instance().Simulator.KeyPress(User32.VK.VK_E);
+ }
+ else
+ {
+ AutoFightContext.Instance().Simulator.KeyPress(User32.VK.VK_E);
+ }
+
+ cd = GetSkillCurrentCd(GetContentFromDispatcher());
+ if (cd > 0)
+ {
+ Logger.LogInformation(hold ? "{Name} 长按元素战技" : "{Name} 点按元素战技", Name);
+ // todo 把cd加入执行队列
+ return;
+ }
+
+ Thread.Sleep(500);
+ }
+ }
+ }
+
+ ///
+ /// 元素战技是否正在CD中
+ /// 右下 267x132
+ /// 77x77
+ ///
+ public double GetSkillCurrentCd(CaptureContent content)
+ {
+ var eRa = content.CaptureRectArea.Crop(AutoFightContext.Instance().FightAssets.ERect);
+ return 0;
+ }
+
+ ///
+ /// 使用元素爆发 Q
+ ///
+ public void UseBurst()
+ {
+ var cd = GetBurstCurrentCd(GetContentFromDispatcher());
+ if (cd > 0)
+ {
+ Logger.LogInformation("{Name} 元素爆发仍在CD中,还剩{Cd}s,跳过此操作", Name, cd);
+ }
+ else
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ AutoFightContext.Instance().Simulator.KeyPress(User32.VK.VK_Q);
+ cd = GetBurstCurrentCd(GetContentFromDispatcher());
+ if (cd > 0)
+ {
+ Logger.LogInformation("{Name} 释放元素爆发", Name);
+ // todo 把cd加入执行队列
+ return;
+ }
+
+ Thread.Sleep(500);
+ }
+ }
+ }
+
+ ///
+ /// 元素爆发是否正在CD中
+ /// 右下 157x165
+ /// 110x110
+ ///
+ public double GetBurstCurrentCd(CaptureContent content)
+ {
+ return 0;
+ }
}
\ No newline at end of file
diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs
index 3f9aeb67..7d2638a7 100644
--- a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs
+++ b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs
@@ -21,10 +21,6 @@ public class CombatScenes
/// 当前配队
///
public Avatar[] Avatars { get; set; } = new Avatar[5];
- ///
- /// find资源
- ///
- private readonly AutoFightAssets _assets = new();
///
/// 通过OCR识别队伍内角色
@@ -32,11 +28,8 @@ public class CombatScenes
/// 完整游戏画面的捕获截图
public void InitializeTeam(CaptureContent content)
{
- var captureRect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
- var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
// 剪裁出队伍区域
- var teamRa = content.CaptureRectArea.Crop(new Rect(captureRect.Width - (int)(355 * assetScale), (int)(220 * assetScale),
- (int)(355 * assetScale), (int)(465 * assetScale)));
+ var teamRa = content.CaptureRectArea.Crop(AutoFightContext.Instance().FightAssets.TeamRect);
// 识别队伍内角色
var result = OcrFactory.Paddle.OcrResult(teamRa.SrcGreyMat);
@@ -45,7 +38,16 @@ public class CombatScenes
private void ParseTeamOcrResult(PaddleOcrResult result, RectArea rectArea)
{
- List names = (from item in result.Regions where DefaultAutoFightConfig.CombatAvatarNames.Contains(item.Text) select item.Text).ToList();
+ List names = new();
+ List nameRects = new();
+ foreach (var item in result.Regions)
+ {
+ if (DefaultAutoFightConfig.CombatAvatarNames.Contains(item.Text))
+ {
+ names.Add(item.Text);
+ nameRects.Add(item.Rect.BoundingRect());
+ }
+ }
if (names.Count < 3 || names.Count > 5)
{
@@ -56,7 +58,7 @@ public class CombatScenes
if (names.Count == 3)
{
// 4人以上的队伍,不支持流浪者的识别
- var wanderer = rectArea.Find(_assets.WandererIconRa);
+ var wanderer = rectArea.Find(AutoFightContext.Instance().FightAssets.WandererIconRa);
if (wanderer.IsEmpty())
{
Logger.LogWarning("识别到的队伍角色数量不正确,当前识别结果:{Text}", string.Join(",", names));
@@ -69,12 +71,14 @@ public class CombatScenes
if (DefaultAutoFightConfig.CombatAvatarNames.Contains(item.Text))
{
names.Add(item.Text);
+ nameRects.Add(item.Rect.BoundingRect());
}
var rect = item.Rect.BoundingRect();
- if (rect.Y > wanderer.Y && wanderer.Y + wanderer.Height > rect.Y+ rect.Height && StringUtils.IsChinese(item.Text))
+ if (rect.Y > wanderer.Y && wanderer.Y + wanderer.Height > rect.Y + rect.Height && StringUtils.IsChinese(item.Text))
{
names.Add(item.Text);
+ nameRects.Add(item.Rect.BoundingRect());
}
}
@@ -84,16 +88,17 @@ public class CombatScenes
}
}
}
+
Logger.LogInformation("识别到的队伍角色:{Text}", string.Join(",", names));
- Avatars = BuildAvatars(names);
+ Avatars = BuildAvatars(names, nameRects);
}
- private Avatar[] BuildAvatars(List names)
+ private Avatar[] BuildAvatars(List names, List nameRects)
{
var avatars = new Avatar[5];
for (var i = 0; i < names.Count; i++)
{
- avatars[i] = new Avatar(names[i], i);
+ avatars[i] = new Avatar(names[i], i + 1, nameRects[i]);
}
return avatars;
diff --git a/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs b/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
index 930181d6..8a2f27e5 100644
--- a/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
+++ b/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTrigger.cs
@@ -5,6 +5,7 @@ using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
+using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
@@ -17,7 +18,6 @@ using Fischless.GameCapture;
using GeniusInvokationAutoToy.Utils;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
-using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -25,12 +25,10 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
-using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using static Vanara.PInvoke.User32;
using Color = System.Drawing.Color;
using Pen = System.Drawing.Pen;
using Point = OpenCvSharp.Point;
-using System.Runtime.Intrinsics.Arm;
namespace BetterGenshinImpact.GameTask.AutoFishing
{
diff --git a/BetterGenshinImpact/GameTask/TaskContext.cs b/BetterGenshinImpact/GameTask/TaskContext.cs
index e5d9e32c..d5f58f66 100644
--- a/BetterGenshinImpact/GameTask/TaskContext.cs
+++ b/BetterGenshinImpact/GameTask/TaskContext.cs
@@ -1,13 +1,9 @@
-using BetterGenshinImpact.GameTask.Model;
-using System;
-using System.Diagnostics;
-using BetterGenshinImpact.Helpers;
-using Vanara.PInvoke;
-using System.Windows.Interop;
-using BetterGenshinImpact.Core.Config;
-using BetterGenshinImpact.View;
-using BetterGenshinImpact.Service;
+using BetterGenshinImpact.Core.Config;
+using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.Genshin.Settings;
+using BetterGenshinImpact.Helpers;
+using BetterGenshinImpact.Service;
+using System;
namespace BetterGenshinImpact.GameTask
{