using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Plugin.Services; using LLib; using Lumina.Excel.Sheets; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps.Common; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Movement; internal sealed class MoveExecutor : TaskExecutor, IToastAware, ITaskExecutor { private readonly string _cannotExecuteAtThisTime; private readonly MovementController _movementController; private readonly GameFunctions _gameFunctions; private readonly ILogger _logger; private readonly IClientState _clientState; private readonly ICondition _condition; private readonly Questionable.Controller.Steps.Common.Mount.MountEvaluator _mountEvaluator; private readonly IServiceProvider _serviceProvider; private System.Action? _startAction; private Vector3 _destination; private bool _canRestart; private (Questionable.Controller.Steps.Common.Mount.MountExecutor Executor, Questionable.Controller.Steps.Common.Mount.MountTask Task)? _mountBeforeMovement; private (Questionable.Controller.Steps.Common.Mount.UnmountExecutor Executor, Questionable.Controller.Steps.Common.Mount.UnmountTask Task)? _unmountBeforeMovement; private (Questionable.Controller.Steps.Common.Mount.MountExecutor Executor, Questionable.Controller.Steps.Common.Mount.MountTask Task)? _mountDuringMovement; public MoveExecutor(MovementController movementController, GameFunctions gameFunctions, ILogger logger, IClientState clientState, ICondition condition, IDataManager dataManager, Questionable.Controller.Steps.Common.Mount.MountEvaluator mountEvaluator, IServiceProvider serviceProvider) { _movementController = movementController; _gameFunctions = gameFunctions; _logger = logger; _clientState = clientState; _condition = condition; _serviceProvider = serviceProvider; _mountEvaluator = mountEvaluator; _cannotExecuteAtThisTime = dataManager.GetString(579u, (LogMessage x) => x.Text); } private void PrepareMovementIfNeeded() { if (!_gameFunctions.IsFlyingUnlocked(base.Task.TerritoryId)) { base.Task = base.Task with { Fly = false, Land = false }; } if (!base.Task.DisableNavmesh) { _startAction = delegate { _movementController.NavigateTo(EMovementType.Quest, base.Task.DataId, _destination, base.Task.Fly, base.Task.Sprint ?? (!_mountDuringMovement.HasValue), base.Task.StopDistance, base.Task.IgnoreDistanceToObject ? new float?(float.MaxValue) : ((float?)null), base.Task.Land); }; return; } _startAction = delegate { MovementController movementController = _movementController; uint? dataId = base.Task.DataId; int num = 1; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); int index = 0; span[index] = _destination; movementController.NavigateTo(EMovementType.Quest, dataId, list, base.Task.Fly, base.Task.Sprint ?? (!_mountDuringMovement.HasValue), base.Task.StopDistance, base.Task.IgnoreDistanceToObject ? new float?(float.MaxValue) : ((float?)null), base.Task.Land); }; } protected override bool Start() { _canRestart = base.Task.RestartNavigation; _destination = base.Task.Destination; float num = base.Task.StopDistance ?? 3f; Vector3? vector = _clientState.LocalPlayer?.Position; float num2 = ((!vector.HasValue) ? float.MaxValue : Vector3.Distance(vector.Value, _destination)); if (num2 > num) { PrepareMovementIfNeeded(); } if (base.Task.Mount == true) { Questionable.Controller.Steps.Common.Mount.MountTask mountTask = new Questionable.Controller.Steps.Common.Mount.MountTask(base.Task.TerritoryId, Questionable.Controller.Steps.Common.Mount.EMountIf.Always); _mountBeforeMovement = (_serviceProvider.GetRequiredService(), mountTask); if (!_mountBeforeMovement.Value.Executor.Start(mountTask)) { _mountBeforeMovement = null; } } else if (base.Task.Mount == false) { Questionable.Controller.Steps.Common.Mount.UnmountTask unmountTask = new Questionable.Controller.Steps.Common.Mount.UnmountTask(); _unmountBeforeMovement = (_serviceProvider.GetRequiredService(), unmountTask); if (!_unmountBeforeMovement.Value.Executor.Start(unmountTask)) { _unmountBeforeMovement = null; } } else if (!base.Task.DisableNavmesh) { Questionable.Controller.Steps.Common.Mount.EMountIf mountIf = ((!(num2 > num) || !base.Task.Fly || !_gameFunctions.IsFlyingUnlocked(base.Task.TerritoryId)) ? Questionable.Controller.Steps.Common.Mount.EMountIf.AwayFromPosition : Questionable.Controller.Steps.Common.Mount.EMountIf.Always); Questionable.Controller.Steps.Common.Mount.MountTask mountTask2 = new Questionable.Controller.Steps.Common.Mount.MountTask(base.Task.TerritoryId, mountIf, _destination); DateTime retryAt = DateTime.Now; (Questionable.Controller.Steps.Common.Mount.MountExecutor, Questionable.Controller.Steps.Common.Mount.MountTask)? tuple; if (_mountEvaluator.EvaluateMountState(mountTask2, dryRun: true, ref retryAt) != Questionable.Controller.Steps.Common.Mount.MountResult.DontMount) { tuple = (_serviceProvider.GetRequiredService(), mountTask2); tuple.Value.Item1.Start(mountTask2); } else { tuple = null; } if (base.Task.Fly) { _mountBeforeMovement = tuple; } else { _mountDuringMovement = tuple; } } if (!_mountBeforeMovement.HasValue && !_unmountBeforeMovement.HasValue && _startAction != null) { _startAction(); } return true; } public override ETaskResult Update() { ETaskResult? eTaskResult = UpdateMountState(); if (eTaskResult.HasValue) { return eTaskResult.GetValueOrDefault(); } if (_startAction == null) { return ETaskResult.TaskComplete; } if (_movementController.IsPathfinding || _movementController.IsPathRunning) { return ETaskResult.StillRunning; } DateTime movementStartedAt = _movementController.MovementStartedAt; if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2.0) >= DateTime.Now) { return ETaskResult.StillRunning; } if (_canRestart && Vector3.Distance(_clientState.LocalPlayer.Position, _destination) > (base.Task.StopDistance ?? 3f) + 5f) { _canRestart = false; if (_clientState.TerritoryType == base.Task.TerritoryId) { _logger.LogInformation("Looks like movement was interrupted, re-attempting to move"); _startAction(); return ETaskResult.StillRunning; } _logger.LogInformation("Looks like movement was interrupted, do nothing since we're in a different territory now"); } return ETaskResult.TaskComplete; } private ETaskResult? UpdateMountState() { (Questionable.Controller.Steps.Common.Mount.MountExecutor, Questionable.Controller.Steps.Common.Mount.MountTask)? mountBeforeMovement = _mountBeforeMovement; if (mountBeforeMovement.HasValue) { Questionable.Controller.Steps.Common.Mount.MountExecutor item = mountBeforeMovement.GetValueOrDefault().Item1; if (item != null) { if (item.Update() == ETaskResult.TaskComplete) { _logger.LogInformation("MountBeforeMovement complete"); _mountBeforeMovement = null; _startAction?.Invoke(); return null; } return ETaskResult.StillRunning; } } (Questionable.Controller.Steps.Common.Mount.UnmountExecutor, Questionable.Controller.Steps.Common.Mount.UnmountTask)? unmountBeforeMovement = _unmountBeforeMovement; if (unmountBeforeMovement.HasValue) { Questionable.Controller.Steps.Common.Mount.UnmountExecutor item2 = unmountBeforeMovement.GetValueOrDefault().Item1; if (item2 != null) { if (item2.Update() == ETaskResult.TaskComplete) { _logger.LogInformation("UnmountBeforeMovement complete"); _unmountBeforeMovement = null; _startAction?.Invoke(); return null; } return ETaskResult.StillRunning; } } mountBeforeMovement = _mountDuringMovement; if (mountBeforeMovement.HasValue) { (Questionable.Controller.Steps.Common.Mount.MountExecutor, Questionable.Controller.Steps.Common.Mount.MountTask) valueOrDefault = mountBeforeMovement.GetValueOrDefault(); var (mountExecutor, _) = valueOrDefault; if (mountExecutor != null) { Questionable.Controller.Steps.Common.Mount.MountTask item3 = valueOrDefault.Item2; if ((object)item3 != null) { if (mountExecutor.Update() == ETaskResult.TaskComplete) { _logger.LogInformation("MountDuringMovement complete (mounted)"); _mountDuringMovement = null; return null; } DateTime retryAt = DateTime.Now; if (_mountEvaluator.EvaluateMountState(item3, dryRun: true, ref retryAt) == Questionable.Controller.Steps.Common.Mount.MountResult.DontMount) { _logger.LogInformation("MountDuringMovement implicitly complete (shouldn't mount anymore)"); _mountDuringMovement = null; return null; } return null; } } } return null; } public override bool WasInterrupted() { DateTime retryAt = DateTime.Now; if (base.Task.Fly && _condition[ConditionFlag.InCombat] && !_condition[ConditionFlag.Mounted]) { (Questionable.Controller.Steps.Common.Mount.MountExecutor, Questionable.Controller.Steps.Common.Mount.MountTask)? mountBeforeMovement = _mountBeforeMovement; if (mountBeforeMovement.HasValue) { Questionable.Controller.Steps.Common.Mount.MountTask item = mountBeforeMovement.GetValueOrDefault().Item2; if ((object)item != null && _mountEvaluator.EvaluateMountState(item, dryRun: true, ref retryAt) == Questionable.Controller.Steps.Common.Mount.MountResult.WhenOutOfCombat) { return true; } } } return base.WasInterrupted(); } public override bool ShouldInterruptOnDamage() { if (!_mountBeforeMovement.HasValue) { return ShouldResolveCombatBeforeNextInteraction(); } return true; } private bool ShouldResolveCombatBeforeNextInteraction() { return base.Task.InteractionType == EInteractionType.Jump; } public bool OnErrorToast(SeString message) { if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue)) { return true; } return false; } }