using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using BetterGenshinImpact.Core.Recognition.ONNX; using BetterGenshinImpact.Core.Simulator; using BetterGenshinImpact.Core.Simulator.Extensions; using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.View.Drawable; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OpenCvSharp; using Vanara.PInvoke; using static BetterGenshinImpact.GameTask.Common.TaskControl; namespace BetterGenshinImpact.GameTask.Common.Job; /// /// 扫描拾取任务 /// 请在安全地区使用 /// public class ScanPickTask { private readonly BgiYoloPredictor _predictor = App.ServiceProvider.GetRequiredService().CreateYoloPredictor(BgiOnnxModel.BgiWorld); private readonly double _dpi = TaskContext.Instance().DpiScale; private readonly RECT _realCaptureRect = TaskContext.Instance().SystemInfo.CaptureAreaRect; public async Task Start(CancellationToken ct) { try { await DoOnce(ct); } catch (Exception e) { Logger.LogDebug(e, "拾取周边物品异常"); Logger.LogError("拾取周边物品异常: {Msg}", e.Message); } finally { VisionContext.Instance().DrawContent.ClearAll(); } } public async Task DoOnce(CancellationToken ct) { var sec = TaskContext.Instance().Config.AutoFightConfig.PickDropsAfterFightSeconds; Stopwatch timeoutStopwatch = Stopwatch.StartNew(); TimeSpan finishTime = TimeSpan.FromSeconds(sec); Simulation.SendInput.SimulateAction(GIActions.Drop); await ResetCamera(ct); while (!ct.IsCancellationRequested && timeoutStopwatch.Elapsed < finishTime) { var (hasItems, pickItems) = DetectPickableItems(); // Logger.LogInformation("存在可拾取物品: {0}", hasItems); if (!hasItems) { Simulation.ReleaseAllKey(); await ResetCamera(ct); for (var i = 0; i < 10; i++) { Simulation.SendInput.Mouse.MoveMouseBy(400, 0); if (i > 5) //前期不考虑移动扫描 await WalkByDirection(ct, GIActions.MoveForward, 100); Simulation.SendInput.SimulateAction(GIActions.Drop); await Delay(300, ct); (hasItems, pickItems) = DetectPickableItems(); if (hasItems) break; } } if (!hasItems) break; // Assume 1080p resolution // approximate dist=(x-960)**2+14*(y-888.88)**2 pickItems = pickItems.OrderBy(rect => Math.Pow(rect.X - 960, 2) + 14 * Math.Pow(rect.Bottom - 888.88, 2)).ToList(); var toPickItem = pickItems[0]; Logger.LogDebug("Fetching: {0}", toPickItem); Logger.LogDebug("Using coord: {0} {1}", toPickItem.X, toPickItem.Bottom); MoveTowardsItem(toPickItem); await Delay(200, ct); Simulation.SendInput.SimulateAction(GIActions.Drop); } Logger.LogInformation("超时或视野内没有可拾取物品,结束扫描"); Simulation.ReleaseAllKey(); Simulation.SendInput.SimulateAction(GIActions.Drop); } /// /// Moves the character towards the specified item by controlling movement keys /// /// The item to move towards private static void MoveTowardsItem(Rect toPickItem) { // 对于比较远的物品(Y坐标靠上)先用前进靠近 // 需要避免两个对向的键同时按下 if (toPickItem.Bottom > 560) { if (toPickItem.X < 760) { Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown); } else if (toPickItem.X > 1040) { Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyDown); } else { Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveRight, KeyType.KeyUp); } } if (toPickItem.Bottom < 770) { Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); } else if (toPickItem.Bottom > 900) { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown); } else { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp); } } /// /// Detects pickable items in the current view /// /// A tuple containing whether items were found and the list of pickable items private (bool hasItems, List pickItems) DetectPickableItems() { var ra = CaptureToRectArea(); var resultDic = _predictor.Detect(ra); // 过滤出可拾取物品 var pickItems = resultDic.Where(x => x.Key is "drops" or "ore") .SelectMany(x => x.Value).ToList(); return (pickItems.Count > 0, pickItems); } private static async Task WalkByDirection(CancellationToken ct, GIActions act, int ms = 1000) { Simulation.SendInput.SimulateAction(act, KeyType.KeyDown); await Delay(ms, ct); Simulation.SendInput.SimulateAction(act, KeyType.KeyUp); } // 回正 并下移视角 private async Task ResetCamera(CancellationToken ct) { Simulation.SendInput.Keyboard.Mouse.MiddleButtonClick(); await Delay(500, ct); Simulation.SendInput.Keyboard.Mouse.MoveMouseBy(0, (int)(500 * _dpi)); await Delay(100, ct); } }