mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-12 15:33:32 +08:00
196 lines
7.1 KiB
C#
196 lines
7.1 KiB
C#
using BetterGenshinImpact.Core.Simulator;
|
||
using BetterGenshinImpact.GameTask.AutoPathing.Model;
|
||
using BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
|
||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.Extensions.Logging;
|
||
using Vanara.PInvoke;
|
||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||
|
||
namespace BetterGenshinImpact.GameTask.AutoPathing;
|
||
|
||
public class TrapEscaper(CancellationToken ct)
|
||
{
|
||
private readonly CameraRotateTask _rotateTask = new(ct);
|
||
private static readonly Random _random = new Random();
|
||
private int _lastActionIndex = 0;
|
||
public static DateTime LastActionTime = DateTime.UtcNow;
|
||
private static int _randomAngle = 0;
|
||
|
||
private void IncreaseRandomAngle()
|
||
{
|
||
_randomAngle += _random.Next(30, 45);
|
||
}
|
||
|
||
private void ReduceRandomAngle()
|
||
{
|
||
_randomAngle += _random.Next(-45, -30);
|
||
}
|
||
|
||
public async Task MoveTo(WaypointForTrack waypoint)
|
||
{
|
||
var startTime = DateTime.UtcNow;
|
||
bool left = false;
|
||
var screen = CaptureToRectArea();
|
||
var position = Navigation.GetPosition(screen);
|
||
LastActionTime = DateTime.UtcNow;
|
||
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
|
||
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
|
||
|
||
// 按下w,一直走
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
|
||
while (!ct.IsCancellationRequested)
|
||
{
|
||
var now = DateTime.UtcNow;
|
||
if ((now - LastActionTime).TotalSeconds > 5)
|
||
{
|
||
break;
|
||
}
|
||
if ((now - startTime).TotalSeconds > 25)
|
||
{
|
||
Logger.LogError("卡死脱困超时!");
|
||
break;
|
||
}
|
||
|
||
screen = CaptureToRectArea();
|
||
position = Navigation.GetPosition(screen);
|
||
|
||
// 旋转视角
|
||
/* 这里的角度增加了一个randomAngle角度,用来在原角度不适用的情况下修改角度以适应复杂环境
|
||
randomAngle会定期归零,不会任何程度上影响路径追踪的结果(指到达既设点位)
|
||
randomAngle为类变量,会在需要修改角度的情况下进行更改,更改时会附带有重置计时器_lastActionTime的代码
|
||
总体的自动避障逻辑为:
|
||
0. 检测是否卡在障碍物上,如果是则执行大脱困
|
||
1. 检测前面是否有障碍物,如果是则执行小脱困
|
||
2. 重复0和1,角度会一直增加,达到“转一圈”的360度脱困效果,若成功脱困则将randomAngle归零
|
||
*/
|
||
targetOrientation = Navigation.GetTargetOrientation(waypoint, position) + _randomAngle;
|
||
|
||
//执行旋转
|
||
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 5);
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_W);
|
||
//
|
||
//这里是随机角度的归零逻辑,在脱困执行一秒后将randomAngle设为0以将实际角度重置为正面向点位的角度
|
||
//其实就是在一段时间内进行角度的修改以实现自动避障
|
||
if (_randomAngle != 0)
|
||
{
|
||
_randomAngle %= 360; //角度增加到360度时也会归零
|
||
if ((DateTime.UtcNow - LastActionTime).TotalSeconds > 1.5)
|
||
{
|
||
_randomAngle = 0;
|
||
}
|
||
}
|
||
// 设置为非攀爬时误进入攀爬,自动脱离(小脱困)
|
||
// 小脱困逻辑,在进入攀爬时,即后一帧会自动脱离,因此无需再执行脱困代码
|
||
// 进入攀爬就代表前面有较高的物体(障碍物)阻挡,所以必须“旋转角度”以辅助绕过障碍物!!!
|
||
|
||
// 先排除攀爬和飞行的情况
|
||
if (waypoint.MoveMode != MoveModeEnum.Climb.Code &&
|
||
waypoint.MoveMode != MoveModeEnum.Fly.Code)
|
||
if (Bv.GetMotionStatus(screen) == MotionStatus.Climb)
|
||
{
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
|
||
Sleep(75);
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_S);
|
||
Sleep(700);
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_S);
|
||
|
||
LastActionTime = DateTime.UtcNow;
|
||
|
||
//!!!!!!!!这里修改了randomAngle的值,用于在脱困后随机旋转角度!!!!!!!!
|
||
if (!left)
|
||
{
|
||
IncreaseRandomAngle();
|
||
}
|
||
else
|
||
{
|
||
ReduceRandomAngle();
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
await Delay(100, ct);
|
||
}
|
||
|
||
// 抬起w键
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
|
||
}
|
||
|
||
public async Task RotateAndMove()
|
||
{
|
||
IncreaseRandomAngle();
|
||
// 脱离攀爬状态
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
|
||
await Delay(75, ct);
|
||
Simulation.SendInput.Mouse.LeftButtonClick();
|
||
await Delay(500, ct);
|
||
|
||
TimeSpan timeSinceLastAction = DateTime.UtcNow - LastActionTime;
|
||
|
||
if (timeSinceLastAction.TotalSeconds >= 10)
|
||
{
|
||
_lastActionIndex = 0;
|
||
}
|
||
else
|
||
{
|
||
_lastActionIndex++;
|
||
}
|
||
|
||
var difference = _lastActionIndex * 1000;
|
||
|
||
switch (_lastActionIndex % 3)
|
||
{
|
||
case 0:
|
||
// 向后移动
|
||
MoveBackward(1000 + difference);
|
||
break;
|
||
|
||
case 1:
|
||
// 向左移动
|
||
MoveLeft(700 + difference);
|
||
break;
|
||
|
||
case 2:
|
||
// 向右移动
|
||
MoveRight(700 + difference);
|
||
break;
|
||
}
|
||
|
||
LastActionTime = DateTime.UtcNow;
|
||
}
|
||
|
||
private void MoveBackward(int delay)
|
||
{
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_S);
|
||
Sleep(500);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
|
||
Sleep(delay);
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_S);
|
||
}
|
||
|
||
private void MoveLeft(int delay)
|
||
{
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_A);
|
||
Sleep(300);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
|
||
Sleep(delay);
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_A);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
|
||
}
|
||
|
||
private void MoveRight(int delay)
|
||
{
|
||
Simulation.SendInput.Keyboard.KeyDown(User32.VK.VK_D);
|
||
Sleep(300);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE);
|
||
Sleep(delay);
|
||
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_D);
|
||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_X);
|
||
}
|
||
}
|