switch avatar

This commit is contained in:
huiyadanli
2023-12-31 14:37:44 +08:00
parent 427ed0045f
commit e2360ca2bd
8 changed files with 282 additions and 38 deletions

View File

@@ -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);

View File

@@ -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);
}
});
}

View File

@@ -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",

View File

@@ -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;
/// <summary>
/// 自动战斗上下文
/// 请在启动BetterGI以后再初始化
/// </summary>
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;
}
/// <summary>
/// find资源
/// </summary>
public readonly AutoFightAssets FightAssets;
/// <summary>
/// 战斗专用的PostMessage模拟键鼠操作
/// </summary>
public readonly PostMessageSimulator Simulator;
}

View File

@@ -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
/// </summary>
public double BurstCd { get; set; }
/// <summary>
/// 元素战技是否就绪
/// </summary>
public bool IsSkillReady { get; set; }
/// <summary>
/// 元素爆发是否就绪
/// </summary>
public bool IsBurstReady { get; set; }
public Avatar(string name, int index)
/// <summary>
/// 名字所在矩形位置
/// </summary>
public Rect NameRect { get; set; }
/// <summary>
/// 名字右边的编号位置
/// </summary>
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;
}
/// <summary>
/// 切换到本角色
/// 切换cd是1秒如果切换失败会尝试再次切换最多尝试5次
/// </summary>
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秒多一点给截图留出时间
}
}
/// <summary>
/// 是否出战状态
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 使用元素战技 E
/// </summary>
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);
}
}
}
/// <summary>
/// 元素战技是否正在CD中
/// 右下 267x132
/// 77x77
/// </summary>
public double GetSkillCurrentCd(CaptureContent content)
{
var eRa = content.CaptureRectArea.Crop(AutoFightContext.Instance().FightAssets.ERect);
return 0;
}
/// <summary>
/// 使用元素爆发 Q
/// </summary>
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);
}
}
}
/// <summary>
/// 元素爆发是否正在CD中
/// 右下 157x165
/// 110x110
/// </summary>
public double GetBurstCurrentCd(CaptureContent content)
{
return 0;
}
}

View File

@@ -21,10 +21,6 @@ public class CombatScenes
/// 当前配队
/// </summary>
public Avatar[] Avatars { get; set; } = new Avatar[5];
/// <summary>
/// find资源
/// </summary>
private readonly AutoFightAssets _assets = new();
/// <summary>
/// 通过OCR识别队伍内角色
@@ -32,11 +28,8 @@ public class CombatScenes
/// <param name="content">完整游戏画面的捕获截图</param>
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<string> names = (from item in result.Regions where DefaultAutoFightConfig.CombatAvatarNames.Contains(item.Text) select item.Text).ToList();
List<string> names = new();
List<Rect> 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<string> names)
private Avatar[] BuildAvatars(List<string> names, List<Rect> 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;

View File

@@ -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
{

View File

@@ -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
{