Questionable/Questionable/Controller/MiniTaskController.cs

206 lines
7.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
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;
using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Shared;
using Questionable.Functions;
using Questionable.Model.Questing;
using Mount = Questionable.Controller.Steps.Common.Mount;
namespace Questionable.Controller;
internal abstract class MiniTaskController<T>
{
protected readonly TaskQueue _taskQueue = new();
private readonly IChatGui _chatGui;
private readonly ICondition _condition;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<T> _logger;
private readonly string _actionCanceledText;
protected MiniTaskController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider,
IDataManager dataManager, ILogger<T> logger)
{
_chatGui = chatGui;
_logger = logger;
_serviceProvider = serviceProvider;
_condition = condition;
_actionCanceledText = dataManager.GetString<LogMessage>(1314, x => x.Text)!;
}
protected virtual void UpdateCurrentTask()
{
if (_taskQueue.CurrentTaskExecutor == null)
{
if (_taskQueue.TryDequeue(out ITask? upcomingTask))
{
try
{
_logger.LogInformation("Starting task {TaskName}", upcomingTask.ToString());
ITaskExecutor taskExecutor =
_serviceProvider.GetRequiredKeyedService<ITaskExecutor>(upcomingTask.GetType());
if (taskExecutor.Start(upcomingTask))
{
_taskQueue.CurrentTaskExecutor = taskExecutor;
return;
}
else
{
_logger.LogTrace("Task {TaskName} was skipped", upcomingTask.ToString());
return;
}
}
catch (Exception e)
{
_logger.LogError(e, "Failed to start task {TaskName}", upcomingTask.ToString());
_chatGui.PrintError(
$"[Questionable] Failed to start task '{upcomingTask}', please check /xllog for details.");
Stop("Task failed to start");
return;
}
}
else
return;
}
ETaskResult result;
try
{
if (_taskQueue.CurrentTaskExecutor.WasInterrupted())
{
InterruptQueueWithCombat();
return;
}
result = _taskQueue.CurrentTaskExecutor.Update();
}
catch (Exception e)
{
_logger.LogError(e, "Failed to update task {TaskName}",
_taskQueue.CurrentTaskExecutor.CurrentTask.ToString());
_chatGui.PrintError(
$"[Questionable] Failed to update task '{_taskQueue.CurrentTaskExecutor.CurrentTask}', please check /xllog for details.");
Stop("Task failed to update");
return;
}
switch (result)
{
case ETaskResult.StillRunning:
return;
case ETaskResult.SkipRemainingTasksForStep:
_logger.LogInformation("{Task} → {Result}, skipping remaining tasks for step",
_taskQueue.CurrentTaskExecutor.CurrentTask, result);
_taskQueue.CurrentTaskExecutor = null;
while (_taskQueue.TryDequeue(out ITask? nextTask))
{
if (nextTask is ILastTask or Gather.SkipMarker)
{
ITaskExecutor taskExecutor =
_serviceProvider.GetRequiredKeyedService<ITaskExecutor>(nextTask.GetType());
taskExecutor.Start(nextTask);
_taskQueue.CurrentTaskExecutor = taskExecutor;
return;
}
}
return;
case ETaskResult.TaskComplete:
case ETaskResult.CreateNewTasks:
_logger.LogInformation("{Task} → {Result}, remaining tasks: {RemainingTaskCount}",
_taskQueue.CurrentTaskExecutor.CurrentTask, result, _taskQueue.RemainingTasks.Count());
OnTaskComplete(_taskQueue.CurrentTaskExecutor.CurrentTask);
if (result == ETaskResult.CreateNewTasks && _taskQueue.CurrentTaskExecutor is IExtraTaskCreator extraTaskCreator)
_taskQueue.EnqueueAll(extraTaskCreator.CreateExtraTasks());
_taskQueue.CurrentTaskExecutor = null;
// handled in next update
return;
case ETaskResult.NextStep:
_logger.LogInformation("{Task} → {Result}", _taskQueue.CurrentTaskExecutor.CurrentTask, result);
var lastTask = (ILastTask)_taskQueue.CurrentTaskExecutor.CurrentTask;
_taskQueue.CurrentTaskExecutor = null;
OnNextStep(lastTask);
return;
case ETaskResult.End:
_logger.LogInformation("{Task} → {Result}", _taskQueue.CurrentTaskExecutor.CurrentTask, result);
_taskQueue.CurrentTaskExecutor = null;
Stop("Task end");
return;
}
}
protected virtual void OnTaskComplete(ITask task)
{
}
protected virtual void OnNextStep(ILastTask task)
{
}
public abstract void Stop(string label);
public virtual IList<string> GetRemainingTaskNames() =>
_taskQueue.RemainingTasks.Select(x => x.ToString() ?? "?").ToList();
public void InterruptQueueWithCombat()
{
_logger.LogWarning("Interrupted, attempting to resolve (if in combat)");
if (_condition[ConditionFlag.InCombat])
{
List<ITask> tasks = [];
if (_condition[ConditionFlag.Mounted])
tasks.Add(new Mount.UnmountTask());
tasks.Add(Combat.Factory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], [], null));
tasks.Add(new WaitAtEnd.WaitDelay());
_taskQueue.InterruptWith(tasks);
}
else
_taskQueue.InterruptWith([new WaitAtEnd.WaitDelay()]);
_logger.LogInformation("Remaining tasks after interruption:");
foreach (ITask task in _taskQueue.RemainingTasks)
_logger.LogInformation("- {TaskName}", task);
}
public void OnErrorToast(ref SeString message, ref bool isHandled)
{
if (_taskQueue.CurrentTaskExecutor is IToastAware toastAware)
{
if (toastAware.OnErrorToast(message))
{
isHandled = true;
}
}
if (!isHandled)
{
if (GameFunctions.GameStringEquals(_actionCanceledText, message.TextValue) &&
!_condition[ConditionFlag.InFlight])
InterruptQueueWithCombat();
}
}
}