From 3c2739c5c54e482fb392209cf24ab5817b8b791b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Sun, 10 Nov 2024 22:49:55 +0800 Subject: [PATCH] add GoToAdventurersGuildTask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化项目结构并新增任务和路径文件 主要更改: - 在 `BetterGenshinImpact.csproj` 文件中,添加了对 `GameTask\Common\Element\Assets\Json\**` 目录的处理,并确保其内容始终复制到输出目录。 - 在 `RecognitionObject.cs` 文件中,新增了多个静态方法 `Ocr` 和一个静态实例 `OcrThis`。 - 在 `PathExecutor.cs` 文件中,优化了路径执行逻辑,新增 `GetPosition` 方法,并调整了超时判断。 - 在 `AutoPickAssets.cs` 文件中,添加了多个 `using` 语句,新增 `_logger` 和 `PickRo` 字段,并在构造函数中添加了自定义拾取按键的处理逻辑。 - 在 `AutoPickTrigger.cs` 文件中,移除了自定义拾取按键的初始化逻辑。 - 在 `OneKeyExpeditionTask.cs` 文件中,注释掉了 `Cv2.ImWrite` 方法的调用。 - 在 `TpTask.cs` 文件中,添加了点位很近时不切换的判断逻辑。 - 在 `BvSimpleOperation.cs` 文件中,新增了多个点击按钮的方法和 `FindF`、`FindFAndPress` 方法。 - 在 `BvStatus.cs` 文件中,新增了多个方法用于判断是否在对话界面并等待对话界面加载完成。 - 在 `GameTaskManager.cs` 文件中,添加了 `MapAssets.DestroyInstance` 方法的调用。 - 在 `HotKeyPageViewModel.cs` 文件中,注释掉了 `ElementalCollectHandler` 的测试代码,并添加了 `GoToAdventurersGuildTask` 的测试代码。 - 在 `TaskSettingsPageViewModel.cs` 文件中,更新了 `OnGoToAutoGeniusInvokationUrlAsync` 方法中的 URL。 - 添加了 `BetterGenshinImpact - Backup.csproj` 文件,配置了项目属性、资源文件和包引用。 - 添加了 `冒险家协会_枫丹.json`、`合成台_枫丹.json` 和 `合成台_璃月.json` 文件,定义了前往相应地点的路径和动作。 - 添加了 `ChooseTalkOptionTask.cs` 文件,实现了选择对话选项的任务。 - 添加了 `GoToAdventurersGuildTask.cs` 文件,实现了前往冒险家协会领取奖励的任务。 --- .../BetterGenshinImpact - Backup.csproj | 148 ++++++++++++ .../BetterGenshinImpact.csproj | 4 + .../Core/Recognition/RecognitionObject.cs | 23 ++ .../GameTask/AutoPathing/PathExecutor.cs | 42 ++-- .../AutoPick/Assets/AutoPickAssets.cs | 33 ++- .../GameTask/AutoPick/AutoPickTrigger.cs | 33 +-- .../GameTask/AutoSkip/OneKeyExpeditionTask.cs | 4 +- .../GameTask/AutoTrackPath/TpTask.cs | 5 + .../Common/BgiVision/BvSimpleOperation.cs | 74 +++++- .../GameTask/Common/BgiVision/BvStatus.cs | 22 ++ .../Assets/Json/冒险家协会_枫丹.json | 28 +++ .../Element/Assets/Json/合成台_枫丹.json | 28 +++ .../Element/Assets/Json/合成台_璃月.json | 36 +++ .../Common/Job/ChooseTalkOptionTask.cs | 218 ++++++++++++++++++ .../Common/Job/GoToAdventurersGuildTask.cs | 134 +++++++++++ .../GameTask/GameTaskManager.cs | 1 + .../ViewModel/Pages/HotKeyPageViewModel.cs | 7 +- .../Pages/TaskSettingsPageViewModel.cs | 2 +- 18 files changed, 791 insertions(+), 51 deletions(-) create mode 100644 BetterGenshinImpact/BetterGenshinImpact - Backup.csproj create mode 100644 BetterGenshinImpact/GameTask/Common/Element/Assets/Json/冒险家协会_枫丹.json create mode 100644 BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_枫丹.json create mode 100644 BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_璃月.json create mode 100644 BetterGenshinImpact/GameTask/Common/Job/ChooseTalkOptionTask.cs create mode 100644 BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs diff --git a/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj b/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj new file mode 100644 index 00000000..1bb2b60c --- /dev/null +++ b/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj @@ -0,0 +1,148 @@ + + + + WinExe + net8.0-windows10.0.22621.0 + enable + true + true + 12.0 + true + Assets\Images\logo.ico + BetterGI + 0.36.2 + x64 + embedded + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + + + + + + \ No newline at end of file diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index dfcd265f..64f717b7 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -122,6 +122,9 @@ Always + + Always + Always @@ -140,6 +143,7 @@ + diff --git a/BetterGenshinImpact/Core/Recognition/RecognitionObject.cs b/BetterGenshinImpact/Core/Recognition/RecognitionObject.cs index 778e6d4a..ce18b5fc 100644 --- a/BetterGenshinImpact/Core/Recognition/RecognitionObject.cs +++ b/BetterGenshinImpact/Core/Recognition/RecognitionObject.cs @@ -144,5 +144,28 @@ public class RecognitionObject /// public List RegexMatchText { get; set; } = []; + public static RecognitionObject Ocr(double x, double y, double w, double h) + { + return new RecognitionObject + { + RecognitionType = RecognitionTypes.Ocr, + RegionOfInterest = new Rect((int)Math.Round(x), (int)Math.Round(y), (int)Math.Round(w), (int)Math.Round(h)) + }; + } + + public static RecognitionObject Ocr(Rect rect) + { + return new RecognitionObject + { + RecognitionType = RecognitionTypes.Ocr, + RegionOfInterest = rect + }; + } + + public static RecognitionObject OcrThis = new() + { + RecognitionType = RecognitionTypes.Ocr + }; + #endregion OCR文字识别 } diff --git a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs index 3c5d537b..688c1790 100644 --- a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs +++ b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs @@ -2,12 +2,17 @@ using BetterGenshinImpact.Core.Simulator; using BetterGenshinImpact.GameTask.AutoFight.Model; using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception; +using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Model; using BetterGenshinImpact.GameTask.AutoPathing.Handler; using BetterGenshinImpact.GameTask.AutoPathing.Model; using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum; +using BetterGenshinImpact.GameTask.AutoSkip; +using BetterGenshinImpact.GameTask.AutoSkip.Assets; using BetterGenshinImpact.GameTask.AutoTrackPath; using BetterGenshinImpact.GameTask.Common.BgiVision; +using BetterGenshinImpact.GameTask.Common.Job; using BetterGenshinImpact.GameTask.Common.Map; +using BetterGenshinImpact.GameTask.Model.Area; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.Extensions.Logging; @@ -18,11 +23,6 @@ using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Model; -using BetterGenshinImpact.GameTask.AutoSkip; -using BetterGenshinImpact.GameTask.AutoSkip.Assets; -using BetterGenshinImpact.GameTask.Common.Job; -using BetterGenshinImpact.GameTask.Model.Area; using Vanara.PInvoke; using static BetterGenshinImpact.GameTask.Common.TaskControl; using ActionEnum = BetterGenshinImpact.GameTask.AutoPathing.Model.Enum.ActionEnum; @@ -99,7 +99,6 @@ public class PathExecutor(CancellationToken ct) { foreach (var waypoint in waypoints) { - await ResolveAnomalies(); await RecoverWhenLowHp(); // 低血量恢复 if (waypoint.Type == WaypointType.Teleport.Code) { @@ -132,7 +131,7 @@ public class PathExecutor(CancellationToken ct) // 不管咋样,松开所有按键 Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W); Simulation.SendInput.Mouse.RightButtonUp(); - await ResolveAnomalies(); + await ResolveAnomalies(); // 异常场景处理 } } } @@ -328,7 +327,7 @@ public class PathExecutor(CancellationToken ct) await SwitchAvatar(PartyConfig.MainAvatarIndex); var screen = CaptureToRectArea(); - var position = Navigation.GetPosition(screen); + var position = await GetPosition(screen); var targetOrientation = Navigation.GetTargetOrientation(waypoint, position); Logger.LogInformation("粗略接近途经点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}"); await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5); @@ -352,7 +351,7 @@ public class PathExecutor(CancellationToken ct) } screen = CaptureToRectArea(); - position = Navigation.GetPosition(screen); + position = await GetPosition(screen); var distance = Navigation.GetDistance(waypoint, position); Debug.WriteLine($"接近目标点中,距离为{distance}"); if (distance < 4) @@ -533,7 +532,7 @@ public class PathExecutor(CancellationToken ct) private async Task MoveCloseTo(WaypointForTrack waypoint) { var screen = CaptureToRectArea(); - var position = Navigation.GetPosition(screen); + var position = await GetPosition(screen); var targetOrientation = Navigation.GetTargetOrientation(waypoint, position); Logger.LogInformation("精确接近目标点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}"); if (waypoint.MoveMode == MoveModeEnum.Fly.Code && waypoint.Action == ActionEnum.StopFlying.Code) @@ -549,14 +548,14 @@ public class PathExecutor(CancellationToken ct) while (!ct.IsCancellationRequested) { stepsTaken++; - if (stepsTaken > 30) + if (stepsTaken > 25) { Logger.LogWarning("精确接近超时"); break; } screen = CaptureToRectArea(); - position = Navigation.GetPosition(screen); + position = await GetPosition(screen); if (Navigation.GetDistance(waypoint, position) < 2) { Logger.LogInformation("已到达路径点"); @@ -626,12 +625,27 @@ public class PathExecutor(CancellationToken ct) return null; } + private async Task GetPosition(ImageRegion imageRegion) + { + var position = Navigation.GetPosition(imageRegion); + + if (position == new Point2f()) + { + if (!Bv.IsInBigMapUi(imageRegion)) + { + await ResolveAnomalies(); + } + } + + return position; + } + /** * 处理各种异常场景 * 需要保证耗时不能太高 */ - public async Task ResolveAnomalies() + private async Task ResolveAnomalies() { // 处理月卡 await _blessingOfTheWelkinMoonTask.Start(ct); @@ -670,7 +684,7 @@ public class PathExecutor(CancellationToken ct) else { noDisabledUiButtonTimes++; - if (noDisabledUiButtonTimes > 50) + if (noDisabledUiButtonTimes > 10) { Logger.LogInformation("自动剧情结束"); break; diff --git a/BetterGenshinImpact/GameTask/AutoPick/Assets/AutoPickAssets.cs b/BetterGenshinImpact/GameTask/AutoPick/Assets/AutoPickAssets.cs index b00d152a..2f6e90ff 100644 --- a/BetterGenshinImpact/GameTask/AutoPick/Assets/AutoPickAssets.cs +++ b/BetterGenshinImpact/GameTask/AutoPick/Assets/AutoPickAssets.cs @@ -1,16 +1,25 @@ -using BetterGenshinImpact.Core.Recognition; +using System; +using BetterGenshinImpact.Core.Recognition; using BetterGenshinImpact.GameTask.Model; +using BetterGenshinImpact.Helpers; using OpenCvSharp; using System.Drawing; +using Vanara.PInvoke; +using Microsoft.Extensions.Logging; namespace BetterGenshinImpact.GameTask.AutoPick.Assets; public class AutoPickAssets : BaseAssets { + private readonly ILogger _logger = App.GetLogger(); + public RecognitionObject FRo; public RecognitionObject ChatIconRo; public RecognitionObject SettingsIconRo; + public User32.VK PickVk = User32.VK.VK_F; + public RecognitionObject PickRo; + private AutoPickAssets() { FRo = new RecognitionObject @@ -41,6 +50,28 @@ public class AutoPickAssets : BaseAssets DrawOnWindow = false, DrawOnWindowPen = new Pen(Color.Chocolate, 2) }.InitTemplate(); + + PickRo = FRo; + var keyName = TaskContext.Instance().Config.AutoPickConfig.PickKey; + if (!string.IsNullOrEmpty(keyName)) + { + try + { + PickRo = LoadCustomPickKey(keyName); + PickVk = User32Helper.ToVk(keyName); + } + catch (Exception e) + { + _logger.LogDebug(e, "加载自定义拾取按键时发生异常"); + _logger.LogError("加载自定义拾取按键失败,继续使用默认的F键"); + TaskContext.Instance().Config.AutoPickConfig.PickKey = "F"; + return; + } + if (keyName != "F") + { + _logger.LogInformation("自定义拾取按键:{Key}", keyName); + } + } } public RecognitionObject LoadCustomPickKey(string key) diff --git a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs index 47b90ea8..30e80911 100644 --- a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs +++ b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs @@ -41,9 +41,6 @@ public partial class AutoPickTrigger : ITaskTrigger /// private List _whiteList = []; - // 自定义拾取按键 - private string _pickKeyName = "F"; - private User32.VK _pickVk = User32.VK.VK_F; private RecognitionObject _pickRo; @@ -53,7 +50,7 @@ public partial class AutoPickTrigger : ITaskTrigger public AutoPickTrigger() { _autoPickAssets = AutoPickAssets.Instance; - _pickRo = _autoPickAssets.FRo; + _pickRo = _autoPickAssets.PickRo; } public AutoPickTrigger(AutoPickExternalConfig? config) : this() @@ -63,28 +60,6 @@ public partial class AutoPickTrigger : ITaskTrigger public void Init() { - var keyName = TaskContext.Instance().Config.AutoPickConfig.PickKey; - if (!string.IsNullOrEmpty(keyName)) - { - try - { - _pickRo = _autoPickAssets.LoadCustomPickKey(keyName); - _pickVk = User32Helper.ToVk(keyName); - _pickKeyName = keyName; - } - catch (Exception e) - { - _logger.LogDebug(e, "加载自定义拾取按键时发生异常"); - _logger.LogError("加载自定义拾取按键失败,继续使用默认的F键"); - TaskContext.Instance().Config.AutoPickConfig.PickKey = "F"; - return; - } - if (_pickKeyName != "F") - { - _logger.LogInformation("自定义拾取按键:{Key}", _pickKeyName); - } - } - IsEnabled = TaskContext.Instance().Config.AutoPickConfig.Enabled; try { @@ -133,7 +108,7 @@ public partial class AutoPickTrigger : ITaskTrigger content.CaptureRectArea.Find(_pickRo, foundRectArea => { - speedTimer.Record($"识别到 {_pickKeyName} 拾取键"); + speedTimer.Record($"识别到拾取键"); if (_externalConfig is { ForceInteraction: true }) { @@ -227,7 +202,7 @@ public partial class AutoPickTrigger : ITaskTrigger if (_whiteList.Contains(text)) { LogPick(content, text); - Simulation.SendInput.Keyboard.KeyPress(_pickVk); + Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk); return; } @@ -247,7 +222,7 @@ public partial class AutoPickTrigger : ITaskTrigger speedTimer.Record("黑名单判断"); LogPick(content, text); - Simulation.SendInput.Keyboard.KeyPress(_pickVk); + Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk); } }); speedTimer.DebugPrint(); diff --git a/BetterGenshinImpact/GameTask/AutoSkip/OneKeyExpeditionTask.cs b/BetterGenshinImpact/GameTask/AutoSkip/OneKeyExpeditionTask.cs index 8a8baad9..4402ed44 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/OneKeyExpeditionTask.cs +++ b/BetterGenshinImpact/GameTask/AutoSkip/OneKeyExpeditionTask.cs @@ -4,9 +4,7 @@ using BetterGenshinImpact.GameTask.AutoSkip.Assets; using BetterGenshinImpact.GameTask.Common; using BetterGenshinImpact.View.Drawable; using Microsoft.Extensions.Logging; -using OpenCvSharp; using System; -using System.Xml.Linq; using static BetterGenshinImpact.GameTask.Common.TaskControl; using static Vanara.PInvoke.User32; @@ -24,7 +22,7 @@ public class OneKeyExpeditionTask { // 1.全部领取 var region = CaptureToRectArea(true); - Cv2.ImWrite($"log/ts.png", region.SrcMat); + // Cv2.ImWrite($"log/ts.png", region.SrcMat); var ra = region.Find(assets.CollectRo); if (!ra.IsEmpty()) { diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs index 9667968b..26a64225 100644 --- a/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs @@ -380,6 +380,11 @@ public class TpTask(CancellationToken ct) var bigMapCenterPoint = bigMapCenterPointNullable.Value; Logger.LogDebug("识别当前大地图位置:{Pos}", bigMapCenterPoint); minDistance = Math.Sqrt(Math.Pow(bigMapCenterPoint.X - x, 2) + Math.Pow(bigMapCenterPoint.Y - y, 2)); + if (minDistance < 50) + { + // 点位很近的情况下不切换 + return false; + } } var minCountry = "当前位置"; diff --git a/BetterGenshinImpact/GameTask/Common/BgiVision/BvSimpleOperation.cs b/BetterGenshinImpact/GameTask/Common/BgiVision/BvSimpleOperation.cs index d64ef49e..f7dc8eeb 100644 --- a/BetterGenshinImpact/GameTask/Common/BgiVision/BvSimpleOperation.cs +++ b/BetterGenshinImpact/GameTask/Common/BgiVision/BvSimpleOperation.cs @@ -1,5 +1,12 @@ -using BetterGenshinImpact.GameTask.Common.Element.Assets; +using System.Windows.Documents; +using BetterGenshinImpact.Core.Recognition; +using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.GameTask.AutoPick.Assets; +using BetterGenshinImpact.GameTask.AutoSkip.Assets; +using BetterGenshinImpact.GameTask.Common.Element.Assets; using BetterGenshinImpact.GameTask.Model.Area; +using OpenCvSharp; +using Vanara.PInvoke; namespace BetterGenshinImpact.GameTask.Common.BgiVision; @@ -24,6 +31,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -40,6 +48,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -56,6 +65,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -72,6 +82,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -88,6 +99,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -104,6 +116,7 @@ public static partial class Bv ra.Click(); return true; } + return false; } @@ -126,4 +139,63 @@ public static partial class Bv { return ClickBlackCancelButton(captureRa) || ClickWhiteCancelButton(captureRa) || ClickOnlineNoButton(captureRa); } + + /// + /// 找到交互按钮 + /// + /// + /// + /// + public static bool FindF(ImageRegion captureRa, params string[] text) + { + using var ra = captureRa.Find(AutoPickAssets.Instance.PickRo); + if (ra.IsExist()) + { + if (text.Length == 0) + { + return true; + } + + var scale = TaskContext.Instance().SystemInfo.AssetScale; + var config = TaskContext.Instance().Config.AutoPickConfig; + var textRect = new Rect(ra.X + (int)(config.ItemTextLeftOffset * scale), ra.Y, + (int)((config.ItemTextRightOffset - config.ItemTextLeftOffset) * scale), ra.Height); + + var textRa = captureRa.DeriveCrop(textRect); + var list = textRa.FindMulti(RecognitionObject.OcrThis); + + foreach (var item in list) + { + // 所有匹配成功才算成功 + var success = true; + foreach (var t in text) + { + if (!item.Text.Contains(t)) + { + success = false; + } + } + return success; + } + } + + return false; + } + + /// + /// 找到交互按钮并点击 + /// + /// + /// + /// + public static bool FindFAndPress(ImageRegion captureRa, params string[] text) + { + if (FindF(captureRa, text)) + { + Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk); + return true; + } + + return false; + } } diff --git a/BetterGenshinImpact/GameTask/Common/BgiVision/BvStatus.cs b/BetterGenshinImpact/GameTask/Common/BgiVision/BvStatus.cs index 934e31f9..aeb39577 100644 --- a/BetterGenshinImpact/GameTask/Common/BgiVision/BvStatus.cs +++ b/BetterGenshinImpact/GameTask/Common/BgiVision/BvStatus.cs @@ -10,6 +10,7 @@ using BetterGenshinImpact.Core.Simulator; using BetterGenshinImpact.GameTask.AutoFight; using Vanara.PInvoke; using System.Threading; +using BetterGenshinImpact.GameTask.AutoSkip.Assets; using BetterGenshinImpact.GameTask.GameLoading.Assets; namespace BetterGenshinImpact.GameTask.Common.BgiVision; @@ -185,6 +186,27 @@ public static partial class Bv { return captureRa.Find(GameLoadingAssets.Instance.WelkinMoonRo).IsExist(); } + + /// + /// 是否在对话界面 + /// + /// + /// + public static bool IsInTalkUi(ImageRegion captureRa) + { + return captureRa.Find(AutoSkipAssets.Instance.DisabledUiButtonRo).IsExist(); + } + + /// + /// 等到对话界面加载完成 + /// + /// + /// + /// + public static async Task WaitAndSkipForTalkUi(CancellationToken ct, int retryTimes = 5) + { + return await NewRetry.WaitForAction(() => IsInTalkUi(TaskControl.CaptureToRectArea()), ct, retryTimes, 500); + } } public enum MotionStatus diff --git a/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/冒险家协会_枫丹.json b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/冒险家协会_枫丹.json new file mode 100644 index 00000000..d9b4b5fd --- /dev/null +++ b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/冒险家协会_枫丹.json @@ -0,0 +1,28 @@ +{ + "info": { + "name": "冒险家协会_枫丹", + "type": "collect", + "author": "yemaxul", + "version": "2.0", + "description": "传送到枫丹冒险家协会", + "bgiVersion": "0.35.0" + }, + "positions": [ + { + "id": 1, + "x": 4508.97509765625, + "y": 3630.557373046875, + "type": "teleport", + "move_mode": "walk", + "action": "" + }, + { + "id": 2, + "x": 4495.0, + "y": 3638.0, + "type": "path", + "move_mode": "walk", + "action": "" + } + ] +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_枫丹.json b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_枫丹.json new file mode 100644 index 00000000..193883cd --- /dev/null +++ b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_枫丹.json @@ -0,0 +1,28 @@ +{ + "info": { + "name": "合成台_枫丹", + "type": "collect", + "author": "huiyadanli", + "version": "1.0", + "description": "枫丹合成台", + "bgiVersion": "0.35.0" + }, + "positions": [ + { + "id": 1, + "x": 4508.97509765625, + "y": 3630.557373046875, + "type": "teleport", + "move_mode": "walk", + "action": "" + }, + { + "id": 3, + "x": 4480.33, + "y": 3628.3, + "type": "target", + "move_mode": "walk", + "action": "" + } + ] +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_璃月.json b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_璃月.json new file mode 100644 index 00000000..72bf9c8d --- /dev/null +++ b/BetterGenshinImpact/GameTask/Common/Element/Assets/Json/合成台_璃月.json @@ -0,0 +1,36 @@ +{ + "info": { + "name": "合成台_璃月", + "type": "collect", + "author": "Yemaxul", + "version": "1.0", + "description": "璃月合成台,搭配树脂自动合成使用", + "bgiVersion": "0.35.0" + }, + "positions": [ + { + "id": 1, + "action": "", + "move_mode": "walk", + "type": "teleport", + "x": 266.86328125, + "y": -652.8173828125 + }, + { + "id": 2, + "x": 258.4609375, + "y": -658.4111328125, + "type": "path", + "move_mode": "walk", + "action": "" + }, + { + "id": 3, + "x": 266.3046875, + "y": -653.61328125, + "type": "target", + "move_mode": "walk", + "action": "" + } + ] +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/Common/Job/ChooseTalkOptionTask.cs b/BetterGenshinImpact/GameTask/Common/Job/ChooseTalkOptionTask.cs new file mode 100644 index 00000000..eb8f8f08 --- /dev/null +++ b/BetterGenshinImpact/GameTask/Common/Job/ChooseTalkOptionTask.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using BetterGenshinImpact.Core.Recognition; +using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.GameTask.AutoSkip.Assets; +using BetterGenshinImpact.GameTask.AutoSkip; +using BetterGenshinImpact.GameTask.Common.BgiVision; +using BetterGenshinImpact.GameTask.Model.Area; +using OpenCvSharp; +using Vanara.PInvoke; +using static BetterGenshinImpact.GameTask.Common.TaskControl; +using System.Text.RegularExpressions; +using Microsoft.Extensions.Logging; +using BetterGenshinImpact.Core.Recognition.OpenCv; + +namespace BetterGenshinImpact.GameTask.Common.Job; + +public partial class ChooseTalkOptionTask +{ + private readonly ILogger _logger = App.GetLogger(); + + public string Name => "持续对话并选择目标选项"; + + // private readonly AutoSkipConfig _config = TaskContext.Instance().Config.AutoSkipConfig; + + /// + /// 单个界面单个选项选择 + /// + /// + /// + /// 200ms一次,点击几次空格 + /// + /// + public async Task SingleSelectText(string option, CancellationToken ct, int skipTimes = 10, bool isOrange = false) + { + if (!await Bv.WaitAndSkipForTalkUi(ct, 10)) + { + Logger.LogError("选项选择:{Text}", "当前界面不在对话选项界面"); + return TalkOptionRes.NotFound; + } + + await Task.Delay(200, ct); + + for (var i = 0; i < skipTimes; i++) // 重试3次 + { + var region = CaptureToRectArea(); + var optionRegions = RecognizeOption(region, ct); + if (optionRegions == null) + { + TaskContext.Instance().PostMessageSimulator.KeyPressBackground(User32.VK.VK_SPACE); + await Delay(200, ct); + continue; // retry + } + + foreach (var optionRa in optionRegions) + { + if (optionRa.Text.Contains(option)) + { + if (isOrange) + { + if (!IsOrangeOption(region.DeriveCrop(optionRa.ToRect()).SrcMat)) + { + return TalkOptionRes.FoundButNotOrange; + } + } + + ClickOcrRegion(optionRa); + await Task.Delay(200, ct); + return TalkOptionRes.FoundAndClick; + } + } + } + + return TalkOptionRes.NotFound; + } + + public async Task SelectLastOptionOnce(CancellationToken ct) + { + var region = CaptureToRectArea(); + if (Bv.IsInTalkUi(region)) + { + var chatOptionResultList = region.FindMulti(AutoSkipAssets.Instance.OptionIconRo); + chatOptionResultList = [.. chatOptionResultList.OrderByDescending(r => r.Y)]; + if (chatOptionResultList.Count > 0) + { + ClickOcrRegion(chatOptionResultList[0]); + await Task.Delay(200, ct); + } + } + } + + public async Task SelectLastOptionUntilEnd(CancellationToken ct) + { + while (true) + { + var region = CaptureToRectArea(); + if (Bv.IsInTalkUi(region)) + { + var chatOptionResultList = region.FindMulti(AutoSkipAssets.Instance.OptionIconRo); + chatOptionResultList = [.. chatOptionResultList.OrderByDescending(r => r.Y)]; + if (chatOptionResultList.Count > 0) + { + ClickOcrRegion(chatOptionResultList[0]); + } + else + { + TaskContext.Instance().PostMessageSimulator.KeyPressBackground(User32.VK.VK_SPACE); + } + } + else if (Bv.IsInMainUi(region)) + { + break; + } + await Task.Delay(200, ct); + } + } + + [GeneratedRegex(@"^[a-zA-Z0-9]+$")] + private static partial Regex EnOrNumRegex(); + + /// + /// 识别当前对话界面的所有选项 + /// + /// + /// + /// + public List? RecognizeOption(ImageRegion region, CancellationToken ct) + { + var assetScale = TaskContext.Instance().SystemInfo.AssetScale; + + // 气泡识别 + var chatOptionResultList = region.FindMulti(AutoSkipAssets.Instance.OptionIconRo); + if (chatOptionResultList.Count > 0) + { + // 第一个元素就是最下面的 + chatOptionResultList = [.. chatOptionResultList.OrderByDescending(r => r.Y)]; + + // 通过最下面的气泡框来文字识别 + var lowest = chatOptionResultList[0]; + var ocrRect = new Rect((int)(lowest.X + lowest.Width + 8 * assetScale), region.Height / 12, + (int)(535 * assetScale), (int)(lowest.Y + lowest.Height + 30 * assetScale - region.Height / 12d)); + var ocrResList = region.FindMulti(RecognitionObject.Ocr(ocrRect)); + + // 删除为空的结果 和 纯英文的结果 + var rs = new List(); + // 按照y坐标排序 + ocrResList = [.. ocrResList.OrderBy(r => r.Y)]; + for (var i = 0; i < ocrResList.Count; i++) + { + var item = ocrResList[i]; + if (string.IsNullOrEmpty(item.Text) || (item.Text.Length < 5 && EnOrNumRegex().IsMatch(item.Text))) + { + continue; + } + + if (i != ocrResList.Count - 1) + { + if (ocrResList[i + 1].Y - ocrResList[i].Y > 150) + { + Debug.WriteLine($"存在Y轴偏差过大的结果,忽略:{item.Text}"); + continue; + } + } + + rs.Add(item); + } + + return ocrResList; + } + + return null; // 没有找到气泡 + } + + private void ClickOcrRegion(Region region) + { + region.Click(); + AutoSkipLog(region.Text); + } + + private void AutoSkipLog(string text) + { + if (!string.IsNullOrEmpty(text)) + { + _logger.LogInformation("对话选项:{Text}", text); + } + } + + private bool IsOrangeOption(Mat textMat) + { + // 只提取橙色 + using var bMat = OpenCvCommonHelper.Threshold(textMat, new Scalar(243, 195, 48), new Scalar(255, 205, 55)); + var whiteCount = OpenCvCommonHelper.CountGrayMatColor(bMat, 255); + var rate = whiteCount * 1.0 / (bMat.Width * bMat.Height); + Debug.WriteLine($"识别到橙色文字区域占比:{rate}"); + if (rate > 0.06) + { + return true; + } + + return false; + } +} + +public enum TalkOptionRes +{ + // 未找到 + NotFound, + + // 找到但不是橙色 + FoundButNotOrange, + + // 找到并点击 + FoundAndClick, +} diff --git a/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs b/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs new file mode 100644 index 00000000..4e82d292 --- /dev/null +++ b/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs @@ -0,0 +1,134 @@ +using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.GameTask.AutoPathing; +using BetterGenshinImpact.GameTask.AutoPathing.Model; +using BetterGenshinImpact.GameTask.AutoSkip.Assets; +using BetterGenshinImpact.GameTask.AutoSkip; +using BetterGenshinImpact.GameTask.Common.BgiVision; +using Microsoft.Extensions.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; +using static BetterGenshinImpact.GameTask.Common.TaskControl; + +namespace BetterGenshinImpact.GameTask.Common.Job; + +public class GoToAdventurersGuildTask +{ + public string Name => "前往冒险家协会领取奖励"; + + private readonly int _retryTimes = 2; + + private readonly ChooseTalkOptionTask _chooseTalkOptionTask = new(); + + public async Task Start(string country, CancellationToken ct) + { + Logger.LogInformation("→ {Name} 开始", Name); + for (int i = 0; i < _retryTimes; i++) + { + try + { + await DoOnce(country, ct); + break; + } + catch (Exception e) + { + Logger.LogError("前往冒险家协会领取奖励执行异常:" + e.Message); + if (i == _retryTimes - 1) + { + // 通知失败 + throw; + } + else + { + await Delay(1000, ct); + Logger.LogInformation("重试前往冒险家协会领取奖励"); + } + } + } + Logger.LogInformation("→ {Name} 结束", Name); + } + + public async Task DoOnce(string country, CancellationToken ct) + { + // 1. 走到冒险家协会 + await GoToAdventurersGuild(country, ct); + + // 2. 交互 + var ra = CaptureToRectArea(); + if (!Bv.FindFAndPress(ra, "凯瑟琳")) + { + throw new Exception("未找与凯瑟琳对话交互按钮"); + } + + // 3. 等待对话界面 + + // 每日 + var res = await _chooseTalkOptionTask.SingleSelectText("每日", ct, 10, true); + if (res == TalkOptionRes.FoundAndClick) + { + Logger.LogInformation("▶ {Text}", "领取『每日委托』奖励!"); + await Delay(1000, ct); + await new ReturnMainUiTask().Start(ct); + + // 结束后重新打开 + await Delay(200, ct); + ra = CaptureToRectArea(); + if (!Bv.FindFAndPress(ra, "凯瑟琳")) + { + throw new Exception("未找与凯瑟琳对话交互按钮"); + } + } + else if (res == TalkOptionRes.FoundButNotOrange) + { + Logger.LogInformation("▶ {Text} 未完成或者已领取", "领取『每日委托』奖励"); + } + else + { + Logger.LogWarning("▶ 未找到 {Text} 选项", "领取『每日委托』奖励"); + } + + // 探索 + res = await _chooseTalkOptionTask.SingleSelectText("探索", ct, 10, true); + if (res == TalkOptionRes.FoundAndClick) + { + new OneKeyExpeditionTask().Run(AutoSkipAssets.Instance); + } + else if (res == TalkOptionRes.FoundButNotOrange) + { + Logger.LogInformation("▶ {Text} 未探索完成或已领取", "探索派遣"); + } + else + { + Logger.LogWarning("▶ 未找到 {Text} 选项", "探索派遣"); + } + + // 如果最后还在对话界面,选择最后一个选项退出 + if (Bv.IsInTalkUi(CaptureToRectArea())) + { + await _chooseTalkOptionTask.SelectLastOptionUntilEnd(ct); + Logger.LogInformation("退出当前对话"); + } + } + + /// + /// 前往冒险家协会 + /// + /// + /// + /// + public async Task GoToAdventurersGuild(string country, CancellationToken ct) + { + var task = PathingTask.BuildFromFilePath(Global.Absolute(@$"GameTask\Common\Element\Assets\Json\冒险家协会_{country}.json")); + var pathingTask = new PathExecutor(ct) + { + PartyConfig = new PathingPartyConfig + { + Enabled = true, + AutoSkipEnabled = true + } + }; + await pathingTask.Pathing(task); + + await Delay(500, ct); + } +} diff --git a/BetterGenshinImpact/GameTask/GameTaskManager.cs b/BetterGenshinImpact/GameTask/GameTaskManager.cs index d14b3c8a..b8588a9e 100644 --- a/BetterGenshinImpact/GameTask/GameTaskManager.cs +++ b/BetterGenshinImpact/GameTask/GameTaskManager.cs @@ -125,6 +125,7 @@ internal class GameTaskManager ElementAssets.DestroyInstance(); QuickSereniteaPotAssets.DestroyInstance(); GameLoadingAssets.DestroyInstance(); + MapAssets.DestroyInstance(); } /// diff --git a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs index 76eba5a6..5b7a50e0 100644 --- a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs @@ -550,11 +550,14 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel Config.HotKeyConfig.Test1HotkeyType, (_, _) => { - var handler = new ElementalCollectHandler(ElementalType.Anemo); - handler.RunAsync(new CancellationToken()); + // var handler = new ElementalCollectHandler(ElementalType.Anemo); + // handler.RunAsync(new CancellationToken()); // SwitchPartyTask switchPartyTask = new SwitchPartyTask(); // Task.Run(async () => { await switchPartyTask.Start("三保一", new CancellationToken()); }); + + GoToAdventurersGuildTask goToAdventurersGuildTask = new GoToAdventurersGuildTask(); + Task.Run(async () => { await goToAdventurersGuildTask.Start("枫丹", new CancellationToken()); }); } )); debugDirectory.Children.Add(new HotKeySettingModel( diff --git a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs index b5405f6d..2d79b2f4 100644 --- a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs @@ -196,7 +196,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw [RelayCommand] public async Task OnGoToAutoGeniusInvokationUrlAsync() { - await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E4%B8%83%E5%9C%A3%E5%8F%AC%E5%94%A4")); + await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/tcg.html")); } [RelayCommand]