using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps; using Questionable.Controller.Steps.Common; using Questionable.Controller.Steps.Interactions; using Questionable.Controller.Steps.Movement; using Questionable.Controller.Steps.Shared; using Questionable.Functions; using Questionable.Model.Questing; namespace Questionable.Controller; internal sealed class FateController : MiniTaskController { private readonly MovementController _movementController; private readonly GameFunctions _gameFunctions; private readonly IClientState _clientState; private readonly ILogger _logger; private FateDefinition? _currentFate; private int _completedCycles; private int? _cycleLimit; private DateTime _startTime; public bool IsRunning => _currentFate != null; public FateDefinition? CurrentFate => _currentFate; public int CompletedCycles => _completedCycles; public int? CycleLimit => _cycleLimit; public TimeSpan ElapsedTime { get { if (!IsRunning) { return TimeSpan.Zero; } return DateTime.UtcNow - _startTime; } } public FateController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider, InterruptHandler interruptHandler, IDataManager dataManager, ILogger logger, MovementController movementController, GameFunctions gameFunctions, IClientState clientState) : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger) { _movementController = movementController; _gameFunctions = gameFunctions; _clientState = clientState; _logger = logger; } public void Start(FateDefinition fate, int? cycleLimit = null) { _currentFate = fate; _completedCycles = 0; _cycleLimit = cycleLimit; _startTime = DateTime.UtcNow; _logger.LogInformation("Starting FATE farming: {FateName} (limit: {Limit})", fate.Name, cycleLimit?.ToString(CultureInfo.InvariantCulture) ?? "unlimited"); EnqueueFateCycle(); } public void Update() { if (_currentFate == null || _movementController.IsPathfinding || _movementController.IsPathRunning) { return; } if (_taskQueue.AllTasksComplete) { _completedCycles++; if (_cycleLimit.HasValue && _completedCycles >= _cycleLimit.Value) { _logger.LogInformation("FATE cycle limit reached ({Cycles}/{Limit})", _completedCycles, _cycleLimit.Value); Stop("Cycle limit reached"); return; } EnqueueFateCycle(); } UpdateCurrentTask(); } private void EnqueueFateCycle() { if (_currentFate != null) { _logger.LogInformation("Enqueuing FATE cycle for {FateName}", _currentFate.Name); if (_clientState.TerritoryType != _currentFate.TerritoryId) { _taskQueue.Enqueue(new AetheryteShortcut.Task(null, null, _currentFate.Aetheryte, _currentFate.TerritoryId)); } if (_currentFate.RequiredStatusId.HasValue && _currentFate.TransformNpcDataId.HasValue && _currentFate.TransformNpcPosition.HasValue && !_gameFunctions.HasStatus(_currentFate.RequiredStatusId.Value)) { ILogger logger = _logger; object[] array = new object[1]; EStatus? requiredStatusId = _currentFate.RequiredStatusId; array[0] = requiredStatusId.Value; logger.LogInformation("Player missing required status {StatusId}, enqueuing transform", array); _taskQueue.Enqueue(new MoveTask(_currentFate.TerritoryId, _currentFate.TransformNpcPosition.Value, null, 3f)); _taskQueue.Enqueue(new Mount.UnmountTask()); TaskQueue taskQueue = _taskQueue; uint value = _currentFate.TransformNpcDataId.Value; requiredStatusId = _currentFate.RequiredStatusId.Value; taskQueue.Enqueue(new Interact.Task(value, null, EInteractionType.Interact, SkipMarkerCheck: true, null, null, null, null, requiredStatusId)); _taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(2L))); } _taskQueue.Enqueue(new MoveTask(_currentFate.TerritoryId, _currentFate.Position, null, 5f)); _taskQueue.Enqueue(new Mount.UnmountTask()); _taskQueue.Enqueue(new FateFarming.WaitForFateTargets(_currentFate.Targets)); _taskQueue.Enqueue(new FateFarming.SyncFateLevel()); _taskQueue.Enqueue(new FateFarming.FateActionLoop(_currentFate.Targets)); _taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(3L))); } } public override void Stop(string label) { if (_currentFate != null) { _logger.LogInformation("Stopping FATE farming: {Label} (completed {Cycles} cycles)", label, _completedCycles); _currentFate = null; _completedCycles = 0; _cycleLimit = null; _taskQueue.Reset(); } } public override IList GetRemainingTaskNames() { ITask task = _taskQueue.CurrentTaskExecutor?.CurrentTask; if (task != null) { string text = task.ToString() ?? "?"; IList remainingTaskNames = base.GetRemainingTaskNames(); int num = 1 + remainingTaskNames.Count; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); int num2 = 0; span[num2] = text; num2++; { foreach (string item in remainingTaskNames) { span[num2] = item; num2++; } return list; } } return base.GetRemainingTaskNames(); } }