mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-04 11:15:18 +08:00
switch avatar
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
48
BetterGenshinImpact/GameTask/AutoFight/AutoFightContext.cs
Normal file
48
BetterGenshinImpact/GameTask/AutoFight/AutoFightContext.cs
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user