using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.AutoTrackPath.Model;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.Service;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Common.Map.Maps;
using BetterGenshinImpact.GameTask.Common.Map.Maps.Base;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.AutoTrackPath;
///
/// 坐标计算原则
/// 1. 所有非矩形点位坐标,优先转换为游戏内原神坐标系
/// 2. 所有涉及矩形运算的,优先转换为全地图坐标系
/// 3. 所有涉及小地图视角角度运算的,优先转换为warpPolar所使用的度数标准
///
[Obsolete]
public class AutoTrackPathTask
{
private readonly AutoTrackPathParam _taskParam;
private GiPath _way;
// 视角偏移移动单位
private const int CharMovingUnit = 500;
private CancellationToken _ct;
public AutoTrackPathTask(AutoTrackPathParam taskParam)
{
_taskParam = taskParam;
var wayJson = File.ReadAllText(Global.Absolute(@"log\way\way2.json"));
_way = JsonSerializer.Deserialize(wayJson, ConfigService.JsonOptions) ?? throw new Exception("way json deserialize failed");
}
public async void Start()
{
var hasLock = false;
try
{
hasLock = await TaskSemaphore.WaitAsync(0);
if (!hasLock)
{
Logger.LogError("启动自动路线功能失败:当前存在正在运行中的独立任务,请不要重复执行任务!");
return;
}
_ct = CancellationContext.Instance.Cts.Token;
Init();
// Tp(_tpPositions[260].X, _tpPositions[260].Y);
await DoTask();
}
catch (NormalEndException)
{
Logger.LogInformation("手动中断自动路线");
}
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 Init()
{
SystemControl.ActivateWindow();
Logger.LogInformation("→ {Text}", "自动路线,启动!");
}
public async Task DoTask()
{
// 1. 传送到最近的传送点
var first = _way.WayPointList[0]; // 解析路线,第一个点为起点
await new TpTask(_ct).Tp(first.Pt.X, first.Pt.Y);
// 2. 等待传送完成
Sleep(1000);
NewRetry.Do((Action)(() =>
{
var ra = TaskControl.CaptureToRectArea();
var miniMapMat = GetMiniMapMat(ra) ?? throw new RetryException("等待传送完成");
}), TimeSpan.FromSeconds(1), 100);
Logger.LogInformation("传送完成");
Sleep(1000);
// 3. 横向移动偏移量校准,移动指定偏移、按下W后识别朝向
var angleOffset = GetOffsetAngle();
if (angleOffset == 0)
{
throw new InvalidOperationException("横向移动偏移量校准失败");
}
// 4. 针对点位进行直线追踪
var trackCts = new CancellationTokenSource();
_ct.Register(trackCts.Cancel);
var trackTask = Track(_way.WayPointList, angleOffset, trackCts);
trackTask.Start();
var refreshStatusTask = RefreshStatus(trackCts.Token);
refreshStatusTask.Start();
var jumpTask = Jump(trackCts.Token);
jumpTask.Start();
await Task.WhenAll(trackTask, refreshStatusTask, jumpTask);
}
private MotionStatus _motionStatus = MotionStatus.Normal;
public Task Jump(CancellationToken trackCt)
{
return new Task(() =>
{
while (!_ct.IsCancellationRequested && !trackCt.IsCancellationRequested)
{
if (_motionStatus == MotionStatus.Normal)
{
MovementControl.Instance.SpacePress();
Sleep(300);
if (_motionStatus == MotionStatus.Normal)
{
MovementControl.Instance.SpacePress();
Sleep(3500);
}
else
{
Sleep(1600);
}
}
else
{
Sleep(1600);
}
}
});
}
private double _targetAngle = 0;
public Task RefreshStatus(CancellationToken trackCt)
{
return new Task(() =>
{
while (!_ct.IsCancellationRequested && !trackCt.IsCancellationRequested)
{
using var ra = CaptureToRectArea();
_motionStatus = Bv.GetMotionStatus(ra);
// var miniMapMat = GetMiniMapMat(ra);
// if (miniMapMat == null)
// {
// throw new InvalidOperationException("当前不在主界面");
// }
//
// var angle = CharacterOrientation.Compute(miniMapMat);
// CameraOrientation.DrawDirection(ra, angle, "avatar", new Pen(Color.Blue, 1));
// Debug.WriteLine($"当前人物图像坐标系角度:{angle}");
// var moveAngle = (int)(_targetAngle - angle);
// Debug.WriteLine($"旋转到目标角度:{_targetAngle},鼠标平移{moveAngle}单位");
// Simulation.SendInput.Mouse.MoveMouseBy(moveAngle, 0);
Sleep(60);
}
});
}
public Task Track(List pList, int angleOffsetUnit, CancellationTokenSource trackCts)
{
return new Task(() =>
{
var currIndex = 0;
while (!_ct.IsCancellationRequested)
{
var ra = CaptureToRectArea();
var miniMapMat = GetMiniMapMat(ra) ?? throw new InvalidOperationException("当前不在主界面");
// 注意游戏坐标系的角度是顺时针的
var matchingMethod = TaskContext.Instance().Config.PathingConditionConfig.MapMatchingMethod;
var currMapImageAvatarPos = MapManager.GetMap(MapTypes.Teyvat, matchingMethod).GetMiniMapPosition(miniMapMat);
if (currMapImageAvatarPos.IsEmpty())
{
Debug.WriteLine("识别小地图位置失败");
continue;
}
var (nextMapImagePathPoint, nextPointIndex) = GetNextPoint(currMapImageAvatarPos, pList, currIndex); // 动态计算下个点位
var nextMapImagePathPos = nextMapImagePathPoint.MatchPt;
Logger.LogInformation("下个点位[{Index}]:{nextMapImagePathPos}", nextPointIndex, nextMapImagePathPos);
var angle = CharacterOrientation.Compute(miniMapMat);
CameraOrientation.DrawDirection(ra, angle, "avatar", new Pen(Color.Blue, 1));
Debug.WriteLine($"当前人物图像坐标系角度:{angle},位置:{currMapImageAvatarPos}");
var nextAngle = Math.Round(Math.Atan2(nextMapImagePathPos.Y - currMapImageAvatarPos.Y, nextMapImagePathPos.X - currMapImageAvatarPos.X) * 180 / Math.PI);
var nextDistance = MathHelper.Distance(nextMapImagePathPos, currMapImageAvatarPos);
Debug.WriteLine($"当前目标点图像坐标系角度:{nextAngle},距离:{nextDistance}");
CameraOrientation.DrawDirection(ra, nextAngle, "target", new Pen(Color.Red, 1));
if (nextDistance < 10)
{
Logger.LogInformation("到达目标点位");
currIndex = nextPointIndex;
MovementControl.Instance.WUp();
if (currIndex == pList.Count - 1)
{
Logger.LogInformation("到达终点");
trackCts.Cancel();
break;
}
}
// 转换为鼠标移动单位
_targetAngle = nextAngle;
var moveAngle = (int)(nextAngle - angle);
moveAngle = (int)(moveAngle * 1d / angleOffsetUnit * CharMovingUnit);
Debug.WriteLine($"旋转到目标角度:{nextAngle},鼠标平移{moveAngle}单位");
Simulation.SendInput.Mouse.MoveMouseBy(moveAngle, 0);
Sleep(100);
miniMapMat = GetMiniMapMat(ra);
if (miniMapMat == null)
{
throw new InvalidOperationException("当前不在主界面");
}
angle = CharacterOrientation.Compute(miniMapMat);
CameraOrientation.DrawDirection(ra, angle, "avatar", new Pen(Color.Blue, 1));
Sleep(100);
MovementControl.Instance.WDown();
Sleep(50);
// MovementControl.Instance.WDown();
// Sleep(80);
}
});
}
///
/// 地图图像点位
/// 寻找后面20个点位中,下一个最近点位,关键点必须走到
///
///
///
///
///
public (GiPathPoint, int) GetNextPoint(Point2f currPoint, List pList, int currIndex)
{
var nextNum = Math.Min(currIndex + 20, pList.Count - 1); // 最多找最近20个点
var minDistance = double.MaxValue;
var minDistancePoint = pList[currIndex];
var minDistanceIndex = currIndex;
// var minDistanceButGt = double.MaxValue;
// var minDistancePointButGt = pList[currIndex];
// var minDistanceIndexButGt = currIndex;
for (var i = currIndex; i < nextNum; i++)
{
var nextPoint = pList[i + 1];
var nextMapImagePos = nextPoint.MatchPt;
var distance = MathHelper.Distance(nextMapImagePos, currPoint);
if (distance < minDistance)
{
minDistance = distance;
minDistancePoint = nextPoint;
minDistanceIndex = i + 1;
// if (distance > 5)
// {
// minDistanceButGt = distance;
// minDistancePointButGt = nextPoint;
// minDistanceIndexButGt = i;
// }
}
if (GiPathPoint.IsKeyPoint(nextPoint))
{
break;
}
}
// return minDistanceButGt >= double.MaxValue ? (minDistancePointButGt, minDistanceIndexButGt) : (minDistancePoint, minDistanceIndex);
return (minDistancePoint, minDistanceIndex);
}
public int GetOffsetAngle()
{
var angle1 = GetCharacterOrientationAngle();
Simulation.SendInput.Mouse.MoveMouseBy(CharMovingUnit, 0);
Sleep(500);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Sleep(100);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
// Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W).Sleep(100).KeyUp(User32.VK.VK_W);
Sleep(1000);
var angle2 = GetCharacterOrientationAngle();
var angleOffset = angle2 - angle1;
Logger.LogInformation("横向移动偏移量校准:鼠标平移{CharMovingUnit}单位,角度转动{AngleOffset}", CharMovingUnit, angleOffset);
return angleOffset;
}
public Mat? GetMiniMapMat(ImageRegion ra)
{
var paimon = ra.Find(ElementAssets.Instance.PaimonMenuRo);
if (paimon.IsExist())
{
return new Mat(ra.SrcMat, new Rect(paimon.X + 24, paimon.Y - 15, 210, 210));
}
return null;
}
public int GetCharacterOrientationAngle()
{
var ra = CaptureToRectArea();
var miniMapMat = GetMiniMapMat(ra) ?? throw new InvalidOperationException("当前不在主界面");
var angle = CharacterOrientation.Compute(miniMapMat);
Logger.LogInformation("当前角度:{Angle}", angle);
// CameraOrientation.DrawDirection(ra, angle);
return angle;
}
}