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 SeasonalDutyController : MiniTaskController { private readonly MovementController _movementController; private readonly GameFunctions _gameFunctions; private readonly IClientState _clientState; private readonly ILogger _logger; private SeasonalDutyDefinition? _currentDuty; private int _completedCycles; private int? _cycleLimit; private DateTime _startTime; public bool IsRunning => _currentDuty != null; public SeasonalDutyDefinition? CurrentDuty => _currentDuty; public int CompletedCycles => _completedCycles; public int? CycleLimit => _cycleLimit; public TimeSpan ElapsedTime { get { if (!IsRunning) { return TimeSpan.Zero; } return DateTime.UtcNow - _startTime; } } public SeasonalDutyController(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(SeasonalDutyDefinition duty, int? cycleLimit = null) { _currentDuty = duty; _completedCycles = 0; _cycleLimit = cycleLimit; _startTime = DateTime.UtcNow; _logger.LogInformation("Starting seasonal duty farming: {DutyName} (limit: {Limit})", duty.Name, cycleLimit?.ToString(CultureInfo.InvariantCulture) ?? "unlimited"); EnqueueDutyCycle(); } public void Update() { if (_currentDuty == null || _movementController.IsPathfinding || _movementController.IsPathRunning) { return; } if (_taskQueue.AllTasksComplete) { _completedCycles++; if (_cycleLimit.HasValue && _completedCycles >= _cycleLimit.Value) { _logger.LogInformation("Seasonal duty cycle limit reached ({Cycles}/{Limit})", _completedCycles, _cycleLimit.Value); Stop("Cycle limit reached"); return; } EnqueueDutyCycle(); } UpdateCurrentTask(); } private void EnqueueDutyCycle() { if (_currentDuty == null) { return; } SeasonalDutyDefinition duty = _currentDuty; _logger.LogInformation("Enqueuing seasonal duty cycle for {DutyName}", duty.Name); if (_clientState.TerritoryType != duty.DutyTerritoryId) { if (_clientState.TerritoryType != duty.TerritoryId) { _taskQueue.Enqueue(new AetheryteShortcut.Task(null, null, duty.Aetheryte, duty.TerritoryId)); } if (duty.AethernetShortcut != null) { _taskQueue.Enqueue(new AethernetShortcut.Task(duty.AethernetShortcut.From, duty.AethernetShortcut.To)); } _taskQueue.Enqueue(new MoveTask(duty.TerritoryId, duty.NpcPosition, null, 3f)); _taskQueue.Enqueue(new Mount.UnmountTask()); bool interacted = false; _taskQueue.Enqueue(new WaitCondition.Task(delegate { if (_clientState.TerritoryType == _currentDuty.DutyTerritoryId) { return true; } if (!interacted) { interacted = _gameFunctions.InteractWith(duty.NpcDataId); } return false; }, $"InteractAndWait(territory: {duty.DutyTerritoryId})")); } _taskQueue.Enqueue(new ClearObjectsWithAction.ClearTask(duty.DataIds, duty.Action, duty.DutyTerritoryId, duty.StopDistance, duty.WaypointPositions)); _taskQueue.Enqueue(new WaitCondition.Task(() => _clientState.TerritoryType != _currentDuty.DutyTerritoryId, "Wait(duty end)")); _taskQueue.Enqueue(new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(3L))); } public override void Stop(string label) { if (_currentDuty != null) { _logger.LogInformation("Stopping seasonal duty farming: {Label} (completed {Cycles} cycles)", label, _completedCycles); _currentDuty = 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(); } }