using BetterGenshinImpact.Core.Recognition; using BetterGenshinImpact.Core.Recognition.OCR; using BetterGenshinImpact.Core.Recognition.OpenCv; using BetterGenshinImpact.Core.Script; using BetterGenshinImpact.Core.Simulator; using BetterGenshinImpact.Core.Simulator.Extensions; using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception; using BetterGenshinImpact.GameTask.AutoSkip.Model; using BetterGenshinImpact.GameTask.Common; using BetterGenshinImpact.GameTask.Common.BgiVision; using BetterGenshinImpact.GameTask.Common.Element.Assets; using BetterGenshinImpact.GameTask.Model; using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.GameTask.QuickTeleport.Assets; using BetterGenshinImpact.Helpers; using BetterGenshinImpact.View.Drawable; using Microsoft.Extensions.Logging; using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using static BetterGenshinImpact.GameTask.Common.TaskControl; namespace BetterGenshinImpact.GameTask.AutoSkip; public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask { // /// // /// 准备好前进了 // /// // private bool _readyMoveForward = false; /// /// 任务距离 /// private Rect _missionDistanceRect = default; private CancellationToken _ct; public async void Start() { var hasLock = false; try { hasLock = await TaskSemaphore.WaitAsync(0); if (!hasLock) { Logger.LogError("启动自动追踪功能失败:当前存在正在运行中的独立任务,请不要重复执行任务!"); return; } SystemControl.ActivateWindow(); Logger.LogInformation("→ {Text}", "自动追踪,启动!"); _ct = CancellationContext.Instance.Cts.Token; TrackMission(); } catch (NormalEndException e) { Logger.LogInformation("自动追踪中断:" + e.Message); } catch (Exception e) { Logger.LogError(e.Message); Logger.LogDebug(e.StackTrace); } finally { VisionContext.Instance().DrawContent.ClearAll(); Logger.LogInformation("→ {Text}", "自动追踪结束"); if (hasLock) { TaskSemaphore.Release(); } } } private void TrackMission() { // 确认在主界面才会执行跟随任务 var ra = CaptureToRectArea(); var paimonMenuRa = ra.Find(ElementAssets.Instance.PaimonMenuRo); if (!paimonMenuRa.IsExist()) { Sleep(5000, _ct); return; } // 任务文字有动效,等待2s重新截图 Simulation.SendInput.Mouse.MoveMouseBy(0, 7000); Sleep(2000, _ct); // OCR 任务文字 在小地图下方 var textRaList = OcrMissionTextRaList(paimonMenuRa); if (textRaList.Count == 0) { Logger.LogInformation("未找到任务文字"); Sleep(5000, _ct); return; } // 从任务文字中提取距离 var distance = GetDistanceFromMissionText(textRaList); Logger.LogInformation("任务追踪:{Text}", "距离" + distance + "m"); if (distance >= 150) { // 距离大于150米,先传送到最近的传送点 // J 打开任务 切换追踪打开地图 中心点就是任务点 Simulation.SendInput.SimulateAction(GIActions.OpenQuestMenu); Sleep(800, _ct); // TODO 识别是否在任务界面 // 切换追踪 var btn = ra.Derive(CaptureRect.Width - 250, CaptureRect.Height - 60); btn.Click(); Sleep(200, _ct); btn.Click(); Sleep(1500, _ct); // 寻找所有传送点 ra = CaptureToRectArea(); var tpPointList = MatchTemplateHelper.MatchMultiPicForOnePic(ra.CacheGreyMat, QuickTeleportAssets.Instance.MapChooseIconGreyMatList); if (tpPointList.Count > 0) { // 选中离中心点最近的传送点 var centerX = ra.Width / 2; var centerY = ra.Height / 2; var minDistance = double.MaxValue; Rect nearestRect = default; foreach (var tpPoint in tpPointList) { var distanceTp = Math.Sqrt(Math.Pow(Math.Abs(tpPoint.X - centerX), 2) + Math.Pow(Math.Abs(tpPoint.Y - centerY), 2)); if (distanceTp < minDistance) { minDistance = distanceTp; nearestRect = tpPoint; } } ra.Derive(nearestRect).Click(); // 等待自动传送完成 Sleep(2000, _ct); if (Bv.IsInBigMapUi(CaptureToRectArea())) { Logger.LogWarning("仍旧在大地图界面,传送失败"); } else { Sleep(500, _ct); NewRetry.Do(() => { if (!Bv.IsInMainUi(CaptureToRectArea())) { Logger.LogInformation("未进入到主界面,继续等待"); throw new RetryException("未进入到主界面"); } }, TimeSpan.FromSeconds(1), 100); StartTrackPoint(); } } else { Logger.LogWarning("未找到传送点"); } } else { StartTrackPoint(); } } private void StartTrackPoint() { // V键直接追踪 Simulation.SendInput.SimulateAction(GIActions.QuestNavigation); Sleep(3000, _ct); var ra = CaptureToRectArea(); var blueTrackPointRa = ra.Find(ElementAssets.Instance.BlueTrackPoint); if (blueTrackPointRa.IsExist()) { MakeBlueTrackPointDirectlyAbove(); } else { Logger.LogWarning("首次未找到追踪点"); } } /// /// 找到追踪点并调整方向 /// private void MakeBlueTrackPointDirectlyAbove() { // return new Task(() => // { int prevMoveX = 0; bool wDown = false; while (!_ct.IsCancellationRequested) { var ra = CaptureToRectArea(); var blueTrackPointRa = ra.Find(ElementAssets.Instance.BlueTrackPoint); if (blueTrackPointRa.IsExist()) { // 使追踪点位于俯视角上方 var centerY = blueTrackPointRa.Y + blueTrackPointRa.Height / 2; if (centerY > CaptureRect.Height / 2) { Simulation.SendInput.Mouse.MoveMouseBy(-50, 0); if (wDown) { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); wDown = false; } Debug.WriteLine("使追踪点位于俯视角上方"); continue; } // 调整方向 var centerX = blueTrackPointRa.X + blueTrackPointRa.Width / 2; var moveX = (centerX - CaptureRect.Width / 2) / 8; moveX = moveX switch { > 0 and < 10 => 10, > -10 and < 0 => -10, _ => moveX }; if (moveX != 0) { Simulation.SendInput.Mouse.MoveMouseBy(moveX, 0); Debug.WriteLine("调整方向:" + moveX); } if (moveX == 0 || prevMoveX * moveX < 0) { if (!wDown) { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); wDown = true; } } if (Math.Abs(moveX) < 50 && Math.Abs(centerY - CaptureRect.Height / 2) < 200) { if (wDown) { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); wDown = false; } // 识别距离 var text = OcrFactory.Paddle.OcrWithoutDetector(ra.CacheGreyMat[_missionDistanceRect]); if (StringUtils.TryExtractPositiveInt(text) is > -1 and <= 3) { Logger.LogInformation("任务追踪:到达目标,识别结果[{Text}]", text); break; } Logger.LogInformation("任务追踪:到达目标"); break; } prevMoveX = moveX; } else { // 随机移动 Logger.LogInformation("未找到追踪点"); } Simulation.SendInput.Mouse.MoveMouseBy(0, 500); // 保证俯视角 Sleep(100); } // }); } private int GetDistanceFromMissionText(List textRaList) { // 打印所有任务文字 var text = textRaList.Aggregate(string.Empty, (current, textRa) => current + textRa.Text.Trim() + "|"); Logger.LogInformation("任务追踪:{Text}", text); foreach (var textRa in textRaList) { if (textRa.Text.Length < 8 && textRa.Text.Contains('m', StringComparison.OrdinalIgnoreCase)) { _missionDistanceRect = textRa.ConvertSelfPositionToGameCaptureRegion(); return StringUtils.TryExtractPositiveInt(textRa.Text); } } return -1; } private List OcrMissionTextRaList(Region paimonMenuRa) { return CaptureToRectArea().FindMulti(new RecognitionObject { RecognitionType = RecognitionTypes.Ocr, RegionOfInterest = new Rect(paimonMenuRa.X, paimonMenuRa.Y - 15 + 210, (int)(300 * AssetScale), (int)(100 * AssetScale)) }); } }