using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
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;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoPathing.Suspend;
using BetterGenshinImpact.GameTask.Common;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using static BetterGenshinImpact.GameTask.SystemControl;
using ActionEnum = BetterGenshinImpact.GameTask.AutoPathing.Model.Enum.ActionEnum;
namespace BetterGenshinImpact.GameTask.AutoPathing;
public class PathExecutor
{
private readonly CameraRotateTask _rotateTask;
private readonly TrapEscaper _trapEscaper;
private readonly BlessingOfTheWelkinMoonTask _blessingOfTheWelkinMoonTask = new();
private AutoSkipTrigger? _autoSkipTrigger;
private PathingPartyConfig? _partyConfig;
private CancellationToken ct;
private PathExecutorSuspend pathExecutorSuspend;
public PathExecutor(CancellationToken ct)
{
_trapEscaper = new(ct);
_rotateTask = new(ct);
this.ct = ct;
pathExecutorSuspend = new PathExecutorSuspend(this);
}
public PathingPartyConfig PartyConfig
{
get => _partyConfig ?? PathingPartyConfig.BuildDefault();
set => _partyConfig = value;
}
///
/// 判断是否中止路径追踪的条件
///
public Func? EndAction { get; set; }
private CombatScenes? _combatScenes;
// private readonly Dictionary _actionAvatarIndexMap = new();
private DateTime _elementalSkillLastUseTime = DateTime.MinValue;
private DateTime _useGadgetLastUseTime = DateTime.MinValue;
private const int RetryTimes = 2;
private int _inTrap = 0;
//记录当前相关点位数组
public (int, List) CurWaypoints { get; set; }
//记录当前点位
public (int, WaypointForTrack) CurWaypoint { get; set; }
//记录恢复点位数组
private (int, List) RecordWaypoints { get; set; }
//记录恢复点位
private (int, WaypointForTrack) RecordWaypoint { get; set; }
//跳过除走路径以外的操作
private bool _skipOtherOperations = false;
//当到达恢复点位
public void TryCloseSkipOtherOperations()
{
// Logger.LogWarning("判断是否跳过路径追踪:" + (CurWaypoint.Item1 < RecordWaypoint.Item1));
if (RecordWaypoints == CurWaypoints && CurWaypoint.Item1 < RecordWaypoint.Item1)
{
return;
}
if (_skipOtherOperations)
{
Logger.LogWarning("已到达上次点位,路径追踪功能恢复");
}
_skipOtherOperations = false;
}
//记录点位,方便后面恢复
public void StartSkipOtherOperations()
{
Logger.LogWarning("记录恢复点位,路径追踪将到达上次点位之前将跳过走路之外的操作");
_skipOtherOperations = true;
RecordWaypoints = CurWaypoints;
RecordWaypoint = CurWaypoint;
}
public async Task Pathing(PathingTask task)
{
// SuspendableDictionary;
const string sdKey = "PathExecutor";
var sd = RunnerContext.Instance.SuspendableDictionary;
sd.Remove(sdKey);
RunnerContext.Instance.SuspendableDictionary.TryAdd(sdKey, pathExecutorSuspend);
if (!task.Positions.Any())
{
Logger.LogWarning("没有路径点,寻路结束");
return;
}
// 切换队伍
if (!await SwitchPartyBefore(task))
{
return;
}
// 校验路径是否可以执行
if (!await ValidateGameWithTask(task))
{
return;
}
InitializePathing(task);
// 转换、按传送点分割路径
var waypointsList = ConvertWaypointsForTrack(task.Positions);
await Delay(100, ct);
Navigation.WarmUp(); // 提前加载地图特征点
foreach (var waypoints in waypointsList)
{
CurWaypoints = (waypointsList.FindIndex(wps => wps == waypoints), waypoints);
for (var i = 0; i < RetryTimes; i++)
{
try
{
await ResolveAnomalies(); // 异常场景处理
foreach (var waypoint in waypoints)
{
CurWaypoint = (waypoints.FindIndex(wps => wps == waypoint), waypoint);
TryCloseSkipOtherOperations();
await RecoverWhenLowHp(waypoint); // 低血量恢复
if (waypoint.Type == WaypointType.Teleport.Code)
{
await HandleTeleportWaypoint(waypoint);
}
else
{
await BeforeMoveToTarget(waypoint);
// Path不用走得很近,Target需要接近,但都需要先移动到对应位置
await MoveTo(waypoint);
if (waypoint.Type == WaypointType.Target.Code
// 除了 fight mining 之外的 action 都需要接近
|| (!string.IsNullOrEmpty(waypoint.Action)
&& waypoint.Action != ActionEnum.NahidaCollect.Code
&& waypoint.Action != ActionEnum.Fight.Code
&& waypoint.Action != ActionEnum.CombatScript.Code
&& waypoint.Action != ActionEnum.Mining.Code))
{
await MoveCloseTo(waypoint);
}
//skipOtherOperations如果重试,则跳过相关操作
if (!string.IsNullOrEmpty(waypoint.Action) && !_skipOtherOperations)
{
// 执行 action
await AfterMoveToTarget(waypoint);
}
}
}
break;
}
catch (NormalEndException normalEndException)
{
Logger.LogInformation(normalEndException.Message);
throw;
}
catch (TaskCanceledException e)
{
throw;
}
catch (RetryException retryException)
{
StartSkipOtherOperations();
Logger.LogWarning(retryException.Message);
}
catch (RetryNoCountException retryException)
{
//特殊情况下,重试不消耗次数
i--;
StartSkipOtherOperations();
Logger.LogWarning(retryException.Message);
}
finally
{
// 不管咋样,松开所有按键
Simulation.SendInput.Keyboard.KeyUp(User32.VK.VK_W);
Simulation.SendInput.Mouse.RightButtonUp();
}
}
}
}
private async Task SwitchPartyBefore(PathingTask task)
{
var ra = CaptureToRectArea();
// 切换队伍前判断是否全队死亡 // 可能队伍切换失败导致的死亡
if (Bv.ClickIfInReviveModal(ra))
{
await Bv.WaitForMainUi(ct); // 等待主界面加载完成
Logger.LogInformation("复苏完成");
await Delay(4000, ct);
// 血量肯定不满,直接去七天神像回血
await TpStatueOfTheSeven();
}
var pRaList = ra.FindMulti(AutoFightAssets.Instance.PRa); // 判断是否联机
if (pRaList.Count > 0)
{
Logger.LogInformation("处于联机状态下,不切换队伍");
}
else
{
if (PartyConfig is { Enabled: false })
{
// 调度器未配置的情况下,根据路径追踪条件配置切换队伍
var partyName = FilterPartyNameByConditionConfig(task);
if (!await SwitchParty(partyName))
{
Logger.LogError("切换队伍失败,无法执行此路径!请检查路径追踪设置!");
return false;
}
}
else if (!string.IsNullOrEmpty(PartyConfig.PartyName))
{
if (!await SwitchParty(PartyConfig.PartyName))
{
Logger.LogError("切换队伍失败,无法执行此路径!请检查配置组中的路径追踪配置!");
return false;
}
}
}
return true;
}
private void InitializePathing(PathingTask task)
{
LogScreenResolution();
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage