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.Model.Questing; namespace Questionable.Controller; internal sealed class SeasonalDutyController : MiniTaskController { private readonly MovementController _movementController; 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, IClientState clientState) : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger) { _movementController = movementController; _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) { SeasonalDutyDefinition currentDuty = _currentDuty; _logger.LogInformation("Enqueuing seasonal duty cycle for {DutyName}", currentDuty.Name); if (_clientState.TerritoryType != currentDuty.TerritoryId && _clientState.TerritoryType != currentDuty.DutyTerritoryId) { _taskQueue.Enqueue(new AetheryteShortcut.Task(null, null, currentDuty.Aetheryte, currentDuty.TerritoryId)); } if (currentDuty.AethernetShortcut != null) { _taskQueue.Enqueue(new AethernetShortcut.Task(currentDuty.AethernetShortcut.From, currentDuty.AethernetShortcut.To)); } _taskQueue.Enqueue(new MoveTask(currentDuty.TerritoryId, currentDuty.NpcPosition, null, 3f)); _taskQueue.Enqueue(new Mount.UnmountTask()); _taskQueue.Enqueue(new Interact.Task(currentDuty.NpcDataId, null, EInteractionType.Interact, SkipMarkerCheck: true)); _taskQueue.Enqueue(new WaitCondition.Task(() => _clientState.TerritoryType == _currentDuty.DutyTerritoryId, $"Wait(territory: {currentDuty.DutyTerritoryId})")); _taskQueue.Enqueue(new ClearObjectsWithAction.ClearTask(currentDuty.DataIds, currentDuty.Action, currentDuty.DutyTerritoryId, currentDuty.StopDistance, currentDuty.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(); } }