diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs index 127425f3..815e7fb4 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs @@ -60,6 +60,12 @@ public partial class AutoFightConfig : ObservableObject [ObservableProperty] private bool _fastCheckEnabled = false; + /// + /// 旋转寻找敌人位置 + /// + [ObservableProperty] + private bool _rotateFindEnemyEnabled = false; + /// /// 快速检查战斗结束的参数,可填入数字和人名,多种用分号分隔,例如:15,白术;钟离;,如果是数字(小于等于0则不会根据时间去检查),则指定检查间隔,如果是人名,则该角色执行一轮操作后进行检查。同时每轮结束后检查不变。 /// diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs index 5a5fb090..6ee6ad0e 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs @@ -18,6 +18,7 @@ public class AutoFightParam : BaseTaskParam public string FastCheckParams = ""; public string CheckEndDelay = ""; public string BeforeDetectDelay = ""; + public bool RotateFindEnemyEnabled = false; } public AutoFightParam(string path, AutoFightConfig autoFightConfig) @@ -34,12 +35,17 @@ public class AutoFightParam : BaseTaskParam FinishDetectConfig.FastCheckParams = autoFightConfig.FinishDetectConfig.FastCheckParams; FinishDetectConfig.CheckEndDelay = autoFightConfig.FinishDetectConfig.CheckEndDelay; FinishDetectConfig.BeforeDetectDelay = autoFightConfig.FinishDetectConfig.BeforeDetectDelay; + FinishDetectConfig.RotateFindEnemyEnabled = autoFightConfig.FinishDetectConfig.RotateFindEnemyEnabled; + + KazuhaPartyName = autoFightConfig.KazuhaPartyName; OnlyPickEliteDropsMode = autoFightConfig.OnlyPickEliteDropsMode; BattleThresholdForLoot = autoFightConfig.BattleThresholdForLoot ?? BattleThresholdForLoot; //下面参数固定,只取自动战斗里面的 FinishDetectConfig.BattleEndProgressBarColor = TaskContext.Instance().Config.AutoFightConfig.FinishDetectConfig.BattleEndProgressBarColor; FinishDetectConfig.BattleEndProgressBarColorTolerance = TaskContext.Instance().Config.AutoFightConfig.FinishDetectConfig.BattleEndProgressBarColorTolerance; + + } public FightFinishDetectConfig FinishDetectConfig { get; set; } = new(); @@ -57,5 +63,4 @@ public class AutoFightParam : BaseTaskParam public string KazuhaPartyName; public string OnlyPickEliteDropsMode=""; - } \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightSeek.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightSeek.cs new file mode 100644 index 00000000..b7401e92 --- /dev/null +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightSeek.cs @@ -0,0 +1,365 @@ +using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.Core.Simulator.Extensions; +using Microsoft.Extensions.Logging; +using System.Threading; +using System.Threading.Tasks; +using static BetterGenshinImpact.GameTask.Common.TaskControl; +using OpenCvSharp; +using BetterGenshinImpact.Core.Recognition.OpenCv; + +namespace BetterGenshinImpact.GameTask.AutoFight +{ + public static class MoveForwardTask + { + + public static async Task ExecuteAsync(Scalar scalarLower, Scalar scalarHigher, ILogger logger, CancellationToken ct) + { + await MoveForwardAsync(scalarLower, scalarHigher, logger, ct); + } + + public static Task MoveForwardAsync(Scalar scalarLower, Scalar scalarHigher, ILogger logger, CancellationToken ct) + { + var image2 = CaptureToRectArea(); + Mat mask2 = OpenCvCommonHelper.Threshold( + image2.DeriveCrop(0, 0, image2.Width * 1570 / 1920, image2.Height * 970 / 1080).SrcMat, + scalarLower, + scalarHigher + ); + + Mat labels2 = new Mat(); + Mat stats2 = new Mat(); + Mat centroids2 = new Mat(); + + int numLabels2 = Cv2.ConnectedComponentsWithStats(mask2, labels2, stats2, centroids2, connectivity: PixelConnectivity.Connectivity4, ltype: MatType.CV_32S); + + logger.LogInformation($"检测数量J: {numLabels2 - 1}"); + + if (numLabels2 > 1) + { + // 获取第一个连通对象的统计信息(标签1) + Mat firstRow = stats2.Row(1); // 获取第1行(标签1)的数据 + int[] stats; + bool success = firstRow.GetArray(out stats); // 使用 out 参数来接收数组数据 + + if (success) + { + int x = stats[0]; + int y = stats[1]; + // int width = stats[2]; + int height = stats[3]; + + Point firstPixel = new Point(x, y); + logger.LogInformation($"找到的连通对象的第一个像素坐标: ({firstPixel.X}, {firstPixel.Y}),连通对象的高度: {height}"); + + if (firstPixel.X < 500 || firstPixel.X > 1200 || firstPixel.Y < 300 || firstPixel.Y > 920) + { + // 非中心区域的处理逻辑 + if (firstPixel.X < 500 && firstPixel.Y < 300) + { + // 左上区域 + if (height <= 6) + { + logger.LogInformation("敌人在左上,向前加向左移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); + Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); + Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp); + }, ct); + } + } + else if (firstPixel.X > 1200 && firstPixel.Y < 300) + { + // 右上区域 + if (height <= 6) + { + logger.LogInformation("敌人在右上,向前加向右移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); + Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); + Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp); + }, ct); + } + } + else if (firstPixel.X < 500 && firstPixel.Y > 920) + { + // 左下区域 + if (height <= 6) + { + logger.LogInformation("敌人在左下,向后加向左移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown); + Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); + Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp); + }, ct); + } + } + else if (firstPixel.X > 1200 && firstPixel.Y > 920) + { + // 右下区域 + if (height <= 6) + { + logger.LogInformation("敌人在右下,向后加向右移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown); + Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); + Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp); + }, ct); + } + } + else if (firstPixel.Y < 300) + { + // 上方区域 + if (height <= 6) + { + logger.LogInformation("敌人在上方,向前移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); + }, ct); + } + } + else if (firstPixel.Y > 920) + { + // 下方区域 + if (height <= 6) + { + logger.LogInformation("敌人在下方,向后移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); + }, ct); + } + } + else + { + // 非上述区域且非中心区域,判断左右 + if (firstPixel.X < 920 && height > 6) + { + logger.LogInformation("敌人在左侧,不移动"); + } + else if (firstPixel.X > 920 && height > 6) + { + logger.LogInformation("敌人在右侧,不移动"); + } + } + } + else // 中心区域 + { + if (height > 6) + { + logger.LogInformation("敌人在中心且高度大于6,不移动"); + } + else if (firstPixel.Y < 300) + { + logger.LogInformation("敌人在上方,向前移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); + }, ct); + } + else if (firstPixel.Y > 920) + { + logger.LogInformation("敌人在下方,向后移动"); + Task.Run(() => + { + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); + }, ct); + } + else + { + logger.LogInformation("敌人在中心,不移动"); + } + } + } + else + { + logger.LogError("无法获取统计信息数组"); + } + } + + mask2.Dispose(); + labels2.Dispose(); + return Task.FromResult(null); + } + } + + public class AutoFightSeek + { + public static async Task SeekAndFightAsync(ILogger logger, int detectDelayTime, CancellationToken ct) + { + Scalar bloodLower = new Scalar(255, 90, 90); + int retryCount = 0; + + while (retryCount < 10) + { + var image = CaptureToRectArea(); + Mat mask = OpenCvCommonHelper.Threshold(image.DeriveCrop(0, 0, 1500, 900).SrcMat, bloodLower); + Mat labels = new Mat(); + Mat stats = new Mat(); + Mat centroids = new Mat(); + + int numLabels = Cv2.ConnectedComponentsWithStats(mask, labels, stats, centroids, + connectivity: PixelConnectivity.Connectivity4, ltype: MatType.CV_32S); + logger.LogInformation($"检测数量首次: {numLabels - 1}"); + + if (numLabels > 1) + { + logger.LogInformation("首次检测画面内疑似有怪物,继续战斗..."); + // 获取第一个连通对象的统计信息(标签1) + Mat firstRow = stats.Row(1); // 获取第1行(标签1)的数据 + int[] statsArray; + bool success = firstRow.GetArray(out statsArray); // 使用 out 参数来接收数组数据 + int height = statsArray[3]; + logger.LogInformation($"敌人血量高度:{height}"); + + // //如果不用旋转判断敌人,直接跳过开队伍检测,加快战斗速度 + // return height > 2;//大于2预防误判 + + if (success) + { + + + if (height < 7 && height > 2) + { + logger.LogInformation("敌人血量高度小于7且大于2,向前移动"); + Task.Run(() => + { + MoveForwardTask.MoveForwardAsync(bloodLower, bloodLower, logger, ct); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); + Task.Delay(1000, ct).Wait(); + Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); + }, ct); + } + + mask.Dispose(); + labels.Dispose(); + stats.Dispose(); + centroids.Dispose(); + return height > 2; + } + } + //如果不用旋转判断敌人,直接跳过开队伍检测,加快战斗速度 + // else + // { + // logger.LogInformation("首次检测画面内没有怪物..."); + // return true; + // } + + + mask.Dispose(); + labels.Dispose(); + stats.Dispose(); + centroids.Dispose(); + + logger.LogInformation("画面内没有怪物,旋转寻找..."); + if (retryCount <= 1) + { + Simulation.SendInput.Mouse.MoveMouseBy(image.Width / 2, -image.Height / 3); + } + else + { + Simulation.SendInput.Mouse.MoveMouseBy(image.Width / 2, 0); + } + + await Task.Delay(300,ct); + + image = CaptureToRectArea(); + mask = OpenCvCommonHelper.Threshold(image.DeriveCrop(0, 0, 1500, 900).SrcMat, bloodLower); + labels = new Mat(); + stats = new Mat(); + centroids = new Mat(); + + numLabels = Cv2.ConnectedComponentsWithStats(mask, labels, stats, centroids, + connectivity: PixelConnectivity.Connectivity4, ltype: MatType.CV_32S); + logger.LogInformation($"检测数量第 {retryCount + 1} 次: {numLabels - 1}"); + + if (numLabels > 1) + { + Mat firstRow2 = stats.Row(1); // 获取第1行(标签1)的数据 + int[] statsArray2; + bool success2 = firstRow2.GetArray(out statsArray2); // 使用 out 参数来接收数组数据 + int height2 = statsArray2[3]; + + if (success2) + { + + logger.LogInformation($"敌人血量高度:{height2}"); + if (height2 < 7 && height2 > 2) + { + logger.LogInformation("画面内有找到怪物,继续战斗..."); + Task.Run(() => { MoveForwardTask.MoveForwardAsync(bloodLower, bloodLower, logger, ct); }, ct); + } + mask.Dispose(); + labels.Dispose(); + stats.Dispose(); + centroids.Dispose(); + return height2 > 2; + } + + } + + if (retryCount == 0) + { + Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen); + await Delay(detectDelayTime, ct); + var ra3 = CaptureToRectArea(); + var b33 = ra3.SrcMat.At(50, 790); // 进度条颜色 + var whiteTile3 = ra3.SrcMat.At(50, 768); // 白块 + Simulation.SendInput.SimulateAction(GIActions.Drop); + + if (IsWhite(whiteTile3.Item2, whiteTile3.Item1, whiteTile3.Item0) && + IsYellow(b33.Item2, b33.Item1, b33.Item0)) + { + logger.LogInformation("识别到战斗结束"); + Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen); + return true; + } + } + + logger.LogInformation("画面内没有怪物,尝试重新检测..."); + retryCount++; + } + + return false; + } + + private static bool IsYellow(int r, int g, int b) + { + //Logger.LogInformation($"IsYellow({r},{g},{b})"); + // 黄色范围:R高,G高,B低 + return (r >= 200 && r <= 255) && + (g >= 200 && g <= 255) && + (b >= 0 && b <= 100); + } + + private static bool IsWhite(int r, int g, int b) + { + //Logger.LogInformation($"IsWhite({r},{g},{b})"); + // 白色范围:R高,G高,B低 + return (r >= 240 && r <= 255) && + (g >= 240 && g <= 255) && + (b >= 240 && b <= 255); + } + } + +} diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs index e5426383..ad03c911 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs @@ -18,10 +18,8 @@ using BetterGenshinImpact.GameTask.Common.Job; using OpenCvSharp; using BetterGenshinImpact.Helpers; using Vanara; -using Vanara.PInvoke; using Microsoft.Extensions.DependencyInjection; - namespace BetterGenshinImpact.GameTask.AutoFight; public class AutoFightTask : ISoloTask @@ -49,6 +47,7 @@ public class AutoFightTask : ISoloTask public double CheckTime = 5; public List CheckNames = new(); public bool FastCheckEnabled; + public bool RotateFindEnemyEnabled = false; public TaskFightFinishDetectConfig(AutoFightParam.FightFinishDetectConfig finishDetectConfig) { @@ -61,6 +60,7 @@ public class AutoFightTask : ISoloTask ParseSingleOrCommaSeparated(finishDetectConfig.BattleEndProgressBarColorTolerance, (6, 6, 6)); DetectDelayTime = (int)((double.TryParse(finishDetectConfig.BeforeDetectDelay, out var result) ? result : 0.45) * 1000); + RotateFindEnemyEnabled = finishDetectConfig.RotateFindEnemyEnabled; } public (int, int, int) BattleEndProgressBarColor { get; } @@ -369,8 +369,7 @@ public class AutoFightTask : ISoloTask timeOutFlag = true; break; } - - // 通用化战斗策略 + command.Execute(combatScenes); //统计战斗人次 if (i == combatCommands.Count - 1 || command.Name != combatCommands[i + 1].Name) @@ -401,13 +400,9 @@ public class AutoFightTask : ISoloTask } else { - Logger.LogInformation($"延时检查为{delayTime}毫秒"); + // Logger.LogInformation($"延时检查为{delayTime}毫秒"); } - - /*if (i CheckFightFinish(int delayTime = 1500, int detectDelayTime = 450) + public async Task CheckFightFinish(int delayTime = 1500, int detectDelayTime = 450) { - // YOLO 判断血条和怪物位置 - // if (HasFightFlagByYolo(CaptureToRectArea())) - // { - // _lastFightFlagTime = DateTime.Now; - // return false; - // } - // - - //Random random = new Random(); - //double randomFraction = random.NextDouble(); // 生成 0 到 1 之间的随机小数 - //此处随机数,防止固定招式下,使按L正好处于招式下,导致无法准确判断战斗结束 - // double randomNumber = 1 + (randomFraction * (3 - 1)); - - // 几秒内没有检测到血条和怪物位置,则开始旋转视角重新检测 - //if ((DateTime.Now - _lastFightFlagTime).TotalSeconds > randomNumber) - //{ - // 旋转完毕后都没有检测到血条和怪物位置,则按L键确认战斗结束 - /** - Simulation.SendInput.Mouse.MiddleButtonClick(); - await Delay(300, _ct); - for (var i = 0; i < 8; i++) + if (_finishDetectConfig.RotateFindEnemyEnabled) { - Simulation.SendInput.Mouse.MoveMouseBy((int)(500 * _dpi), 0); - await Delay(800, _ct); // 等待视角稳定 - if (HasFightFlagByYolo(CaptureToRectArea())) + bool? result = null; + try { - _lastFightFlagTime = DateTime.Now; - return false; + result = await AutoFightSeek.SeekAndFightAsync(Logger, detectDelayTime, _ct); } + catch (Exception ex) + { + Logger.LogError(ex, "SeekAndFightAsync 方法发生异常"); + result = false; + } + + if (result == true) + { + return true; + } + + return false; } - **/ - //Simulation.SendInput.SimulateAction(GIActions.Drop);//在换队前取消爬墙状态 - await Delay(delayTime, _ct); + + if (!_finishDetectConfig.RotateFindEnemyEnabled)await Delay(delayTime, _ct); + Logger.LogInformation("打开编队界面检查战斗是否结束,延时{detectDelayTime}毫秒检查", detectDelayTime); // 最终方案确认战斗结束 Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen); await Delay(detectDelayTime, _ct); + var ra = CaptureToRectArea(); + //判断整个界面是否有红色色块,如果有,则战继续,否则战斗结束 + // 只提取橙色 + var b3 = ra.SrcMat.At(50, 790); //进度条颜色 var whiteTile = ra.SrcMat.At(50, 768); //白块 Simulation.SendInput.SimulateAction(GIActions.Drop); @@ -600,14 +589,14 @@ public class AutoFightTask : ISoloTask Logger.LogInformation($"未识别到战斗结束yellow{b3.Item0},{b3.Item1},{b3.Item2}"); Logger.LogInformation($"未识别到战斗结束white{whiteTile.Item0},{whiteTile.Item1},{whiteTile.Item2}"); - /** - if (!Bv.IsInMainUi(ra)) + + Task.Run(() => { - // 如果不在主界面,说明异常,直接结束战斗继续下一步(地图追踪下一步会进入异常处理) - Logger.LogInformation("当前不在主界面,直接结束战斗!"); - return true; - }**/ - + Scalar bloodLower = new Scalar(255, 90, 90); + MoveForwardTask.MoveForwardAsync(bloodLower, bloodLower, Logger, _ct); + } ,_ct); + + _lastFightFlagTime = DateTime.Now; return false; } diff --git a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml index 92fd4862..76b9ac57 100644 --- a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml @@ -569,7 +569,34 @@ - + + + + + + + + + + + + + + diff --git a/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml b/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml index f395f6d5..54177c5b 100644 --- a/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml +++ b/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml @@ -947,8 +947,36 @@ - + + + + + + + + + + + + + + +