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.Core.Recognition.OCR;
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;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoPathing;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Exceptions;
using BetterGenshinImpact.GameTask.Common.Map.Maps;
namespace BetterGenshinImpact.GameTask.AutoPathing;
public class PathExecutor
{
private readonly CameraRotateTask _rotateTask;
private readonly TrapEscaper _trapEscaper;
private readonly BlessingOfTheWelkinMoonTask _blessingOfTheWelkinMoonTask = new();
private AutoSkipTrigger? _autoSkipTrigger;
public int SuccessFight = 0;
//路径追踪完全走完所有路径结束的标识
public bool SuccessEnd = false;
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;
// 最近一次获取派遣奖励的时间
private DateTime _lastGetExpeditionRewardsTime = DateTime.MinValue;
//当到达恢复点位
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, task);
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(); // 异常场景处理
// 如果首个点是非TP点位,强制设置在这个点位附近优先做局部匹配
if (waypoints[0].Type != WaypointType.Teleport.Code)
{
Navigation.SetPrevPosition((float)waypoints[0].X, (float)waypoints[0].Y);
}
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需要接近,但都需要先移动到对应位置
if (waypoint.Type == WaypointType.Orientation.Code)
{
// 方位点,只需要朝向
// 考虑到方位点大概率是作为执行action的最后一个点,所以放在此处处理,不和传送点一样单独处理
await FaceTo(waypoint);
}
else if (waypoint.Action != ActionEnum.UpDownGrabLeaf.Code)
{
await MoveTo(waypoint);
}
await BeforeMoveCloseToTarget(waypoint);
if (IsTargetPoint(waypoint))
{
await MoveCloseTo(waypoint);
}
//skipOtherOperations如果重试,则跳过相关操作,
if ((!string.IsNullOrEmpty(waypoint.Action) && !_skipOtherOperations) ||
waypoint.Action == ActionEnum.CombatScript.Code)
{
// 执行 action
await AfterMoveToTarget(waypoint);
}
}
}
if (waypoints == waypointsList.Last())
{
SuccessEnd = true;
}
break;
}
catch (HandledException handledException)
{
SuccessEnd = true;
break;
}
catch (NormalEndException normalEndException)
{
Logger.LogInformation(normalEndException.Message);
if (!RunnerContext.Instance.isAutoFetchDispatch && RunnerContext.Instance.IsContinuousRunGroup)
{
throw;
}
else
{
break;
}
}
catch (TaskCanceledException e)
{
if (!RunnerContext.Instance.isAutoFetchDispatch && RunnerContext.Instance.IsContinuousRunGroup)
{
throw;
}
else
{
break;
}
}
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 bool IsTargetPoint(WaypointForTrack waypoint)
{
// 方位点不需要接近
if (waypoint.Type == WaypointType.Orientation.Code || waypoint.Action == ActionEnum.UpDownGrabLeaf.Code)
{
return false;
}
var action = ActionEnum.GetEnumByCode(waypoint.Action);
if (action is not null && action.UseWaypointTypeEnum != ActionUseWaypointTypeEnum.Custom)
{
// 强制点位类型的 action,以 action 为准
return action.UseWaypointTypeEnum == ActionUseWaypointTypeEnum.Target;
}
// 其余情况和没有action的情况以点位类型为准
return waypoint.Type == WaypointType.Target.Code;
}
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