255 lines
8.2 KiB
C#
255 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
|
using LLib.Gear;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Controller.Steps.Shared;
|
|
using Questionable.Controller.Utils;
|
|
using Questionable.Data;
|
|
using Questionable.External;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Interactions;
|
|
|
|
internal static class Duty
|
|
{
|
|
internal sealed class Factory(AutoDutyIpc autoDutyIpc, Configuration configuration) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Duty)
|
|
{
|
|
yield break;
|
|
}
|
|
ArgumentNullException.ThrowIfNull(step.DutyOptions, "step.DutyOptions");
|
|
if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions))
|
|
{
|
|
AutoDutyIpc.DutyMode dutyMode = GetDutyMode(step.DutyOptions.ContentFinderConditionId, step.DutyOptions.DutyMode);
|
|
if (dutyMode == AutoDutyIpc.DutyMode.UnsyncRegular && (step.DutyOptions.DutyMode == EDutyMode.UnsyncParty || (!step.DutyOptions.DutyMode.HasValue && configuration.Duties.DutyModeOverrides.TryGetValue(step.DutyOptions.ContentFinderConditionId, out var value) && value == EDutyMode.UnsyncParty) || (!step.DutyOptions.DutyMode.HasValue && !configuration.Duties.DutyModeOverrides.ContainsKey(step.DutyOptions.ContentFinderConditionId) && configuration.Duties.DefaultDutyMode == EDutyMode.UnsyncParty)))
|
|
{
|
|
yield return new WaitForPartyTask();
|
|
}
|
|
yield return new StartAutoDutyTask(step.DutyOptions.ContentFinderConditionId, dutyMode);
|
|
yield return new WaitAutoDutyTask(step.DutyOptions.ContentFinderConditionId);
|
|
if (!QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
|
{
|
|
yield return new WaitAtEnd.WaitNextStepOrSequence();
|
|
}
|
|
}
|
|
else if (!step.DutyOptions.LowPriority)
|
|
{
|
|
yield return new OpenDutyFinderTask(step.DutyOptions.ContentFinderConditionId);
|
|
}
|
|
}
|
|
|
|
private AutoDutyIpc.DutyMode GetDutyMode(uint cfcId, EDutyMode? stepDutyMode)
|
|
{
|
|
if (stepDutyMode.HasValue)
|
|
{
|
|
return ConvertToAutoDutyMode(stepDutyMode.Value);
|
|
}
|
|
if (configuration.Duties.DutyModeOverrides.TryGetValue(cfcId, out var value))
|
|
{
|
|
return ConvertToAutoDutyMode(value);
|
|
}
|
|
return ConvertToAutoDutyMode(configuration.Duties.DefaultDutyMode);
|
|
}
|
|
|
|
private static AutoDutyIpc.DutyMode ConvertToAutoDutyMode(EDutyMode mode)
|
|
{
|
|
return mode switch
|
|
{
|
|
EDutyMode.Support => AutoDutyIpc.DutyMode.Support,
|
|
EDutyMode.UnsyncSolo => AutoDutyIpc.DutyMode.UnsyncRegular,
|
|
EDutyMode.UnsyncParty => AutoDutyIpc.DutyMode.UnsyncRegular,
|
|
_ => AutoDutyIpc.DutyMode.Support,
|
|
};
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitForPartyTask : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "WaitForParty";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitForPartyExecutor(IChatGui chatGui, ILogger<WaitForPartyExecutor> logger) : TaskExecutor<WaitForPartyTask>()
|
|
{
|
|
private DateTime _lastWarningTime = DateTime.MinValue;
|
|
|
|
protected override bool Start()
|
|
{
|
|
logger.LogInformation("Waiting for party members before starting duty...");
|
|
return true;
|
|
}
|
|
|
|
public unsafe override ETaskResult Update()
|
|
{
|
|
GroupManager* ptr = GroupManager.Instance();
|
|
if (ptr == null)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
byte memberCount = ptr->MainGroup.MemberCount;
|
|
bool isAlliance = ptr->MainGroup.IsAlliance;
|
|
if (memberCount > 1 || isAlliance)
|
|
{
|
|
logger.LogInformation("Party detected with {MemberCount} members, proceeding with duty", memberCount);
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (DateTime.Now - _lastWarningTime > TimeSpan.FromSeconds(10L))
|
|
{
|
|
chatGui.Print("[Questionable] Waiting for party members before starting duty (Unsync Party mode)...", "Questionable", 576);
|
|
_lastWarningTime = DateTime.Now;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record StartAutoDutyTask(uint ContentFinderConditionId, AutoDutyIpc.DutyMode DutyMode) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"StartAutoDuty({ContentFinderConditionId}, {DutyMode})";
|
|
}
|
|
}
|
|
|
|
internal sealed class StartAutoDutyExecutor(GearStatsCalculator gearStatsCalculator, AutoDutyIpc autoDutyIpc, TerritoryData territoryData, IClientState clientState, IChatGui chatGui, SendNotification.Executor sendNotificationExecutor) : TaskExecutor<StartAutoDutyTask>(), IStoppableTaskExecutor, ITaskExecutor
|
|
{
|
|
protected unsafe override bool Start()
|
|
{
|
|
if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData))
|
|
{
|
|
throw new TaskException("Failed to get territory ID for content finder condition");
|
|
}
|
|
InventoryManager* intPtr = InventoryManager.Instance();
|
|
if (intPtr == null)
|
|
{
|
|
throw new TaskException("Inventory unavailable");
|
|
}
|
|
InventoryContainer* inventoryContainer = intPtr->GetInventoryContainer(InventoryType.EquippedItems);
|
|
if (inventoryContainer == null)
|
|
{
|
|
throw new TaskException("Equipped items unavailable");
|
|
}
|
|
short num = gearStatsCalculator.CalculateAverageItemLevel(inventoryContainer);
|
|
if (contentFinderConditionData.RequiredItemLevel > num)
|
|
{
|
|
string text = $"Could not use AutoDuty to queue for {contentFinderConditionData.Name}, required item level: {contentFinderConditionData.RequiredItemLevel}, current item level: {num}.";
|
|
if (!sendNotificationExecutor.Start(new SendNotification.Task(EInteractionType.Duty, text)))
|
|
{
|
|
chatGui.PrintError(text, "Questionable", 576);
|
|
}
|
|
return false;
|
|
}
|
|
autoDutyIpc.StartInstance(base.Task.ContentFinderConditionId, base.Task.DutyMode);
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData))
|
|
{
|
|
throw new TaskException("Failed to get territory ID for content finder condition");
|
|
}
|
|
if (clientState.TerritoryType != contentFinderConditionData.TerritoryId)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public void StopNow()
|
|
{
|
|
autoDutyIpc.Stop();
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Wait(AutoDuty, left instance {ContentFinderConditionId})";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitAutoDutyExecutor(AutoDutyIpc autoDutyIpc, TerritoryData territoryData, IClientState clientState) : TaskExecutor<WaitAutoDutyTask>(), IStoppableTaskExecutor, ITaskExecutor
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!territoryData.TryGetContentFinderCondition(base.Task.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData))
|
|
{
|
|
throw new TaskException("Failed to get territory ID for content finder condition");
|
|
}
|
|
if (clientState.TerritoryType == contentFinderConditionData.TerritoryId || !autoDutyIpc.IsStopped())
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public void StopNow()
|
|
{
|
|
autoDutyIpc.Stop();
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record OpenDutyFinderTask(uint ContentFinderConditionId) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"OpenDutyFinder({ContentFinderConditionId})";
|
|
}
|
|
}
|
|
|
|
internal sealed class OpenDutyFinderExecutor(GameFunctions gameFunctions, ICondition condition) : TaskExecutor<OpenDutyFinderTask>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
if (condition[ConditionFlag.InDutyQueue])
|
|
{
|
|
return false;
|
|
}
|
|
gameFunctions.OpenDutyFinder(base.Task.ContentFinderConditionId);
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|