277 lines
7.3 KiB
C#
277 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using Dalamud.Game.ClientState.Objects.Types;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Controller.Utils;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Interactions;
|
|
|
|
internal static class Action
|
|
{
|
|
internal sealed class Factory : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.InteractionType != EInteractionType.Action)
|
|
{
|
|
return Array.Empty<ITask>();
|
|
}
|
|
ArgumentNullException.ThrowIfNull(step.Action, "step.Action");
|
|
ITask task = OnObject(step.DataId, quest, step.Action.Value, step.CompletionQuestVariablesFlags);
|
|
if (step.Action.Value.RequiresMount())
|
|
{
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(task);
|
|
}
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[2]
|
|
{
|
|
new Mount.UnmountTask(),
|
|
task
|
|
});
|
|
}
|
|
|
|
public static ITask OnObject(uint? dataId, Quest quest, EAction action, List<QuestWorkValue?>? completionQuestVariablesFlags)
|
|
{
|
|
if ((uint)(action - 2265) <= 2u)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(dataId, "dataId");
|
|
return new UseMudraOnObject(dataId.Value, action);
|
|
}
|
|
return new UseOnObject(dataId, quest, action, completionQuestVariablesFlags);
|
|
}
|
|
}
|
|
|
|
internal sealed record UseOnObject(uint? DataId, Quest? Quest, EAction Action, List<QuestWorkValue?>? CompletionQuestVariablesFlags) : ITask
|
|
{
|
|
public bool ShouldRedoOnInterrupt()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"Action({Action})";
|
|
}
|
|
}
|
|
|
|
internal sealed class UseOnObjectExecutor(GameFunctions gameFunctions, QuestFunctions questFunctions, ILogger<UseOnObject> logger) : TaskExecutor<UseOnObject>()
|
|
{
|
|
private bool _usedAction;
|
|
|
|
private DateTime _continueAt = DateTime.MinValue;
|
|
|
|
protected override bool Start()
|
|
{
|
|
if (base.Task.DataId.HasValue)
|
|
{
|
|
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value);
|
|
if (gameObject == null)
|
|
{
|
|
logger.LogWarning("No game object with dataId {DataId}", base.Task.DataId);
|
|
return false;
|
|
}
|
|
if (gameObject.IsTargetable)
|
|
{
|
|
if (base.Task.Action == EAction.Diagnosis && gameFunctions.HasStatus(EStatus.Eukrasia) && GameFunctions.RemoveStatus(EStatus.Eukrasia))
|
|
{
|
|
_continueAt = DateTime.Now.AddSeconds(2.0);
|
|
return true;
|
|
}
|
|
_usedAction = gameFunctions.UseAction(gameObject, base.Task.Action);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
_usedAction = gameFunctions.UseAction(base.Task.Action);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (DateTime.Now <= _continueAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!_usedAction)
|
|
{
|
|
if (base.Task.DataId.HasValue)
|
|
{
|
|
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId.Value);
|
|
if (gameObject == null || !gameObject.IsTargetable)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
_usedAction = gameFunctions.UseAction(gameObject, base.Task.Action);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
}
|
|
else
|
|
{
|
|
_usedAction = gameFunctions.UseAction(base.Task.Action);
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (base.Task.Quest != null && base.Task.CompletionQuestVariablesFlags != null && QuestWorkUtils.HasCompletionFlags(base.Task.CompletionQuestVariablesFlags))
|
|
{
|
|
QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(base.Task.Quest.Id);
|
|
if (questProgressInfo == null || !QuestWorkUtils.MatchesQuestWork(base.Task.CompletionQuestVariablesFlags, questProgressInfo))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal sealed record UseMudraOnObject(uint DataId, EAction Action) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Mudra({Action})";
|
|
}
|
|
}
|
|
|
|
internal sealed class UseMudraOnObjectExecutor(GameFunctions gameFunctions, ILogger<UseMudraOnObject> logger) : TaskExecutor<UseMudraOnObject>()
|
|
{
|
|
private static readonly ReadOnlyDictionary<EAction, Dictionary<EAction, EAction>> Combos = new Dictionary<EAction, Dictionary<EAction, EAction>>
|
|
{
|
|
{
|
|
EAction.FumaShuriken,
|
|
new Dictionary<EAction, EAction> {
|
|
{
|
|
EAction.Ninjutsu,
|
|
EAction.Ten
|
|
} }
|
|
},
|
|
{
|
|
EAction.Raiton,
|
|
new Dictionary<EAction, EAction>
|
|
{
|
|
{
|
|
EAction.Ninjutsu,
|
|
EAction.Ten
|
|
},
|
|
{
|
|
EAction.FumaShuriken,
|
|
EAction.Chi
|
|
}
|
|
}
|
|
},
|
|
{
|
|
EAction.Katon,
|
|
new Dictionary<EAction, EAction>
|
|
{
|
|
{
|
|
EAction.Ninjutsu,
|
|
EAction.Chi
|
|
},
|
|
{
|
|
EAction.FumaShuriken,
|
|
EAction.Ten
|
|
}
|
|
}
|
|
}
|
|
}.AsReadOnly();
|
|
|
|
private DateTime _continueAt = DateTime.MinValue;
|
|
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public unsafe override ETaskResult Update()
|
|
{
|
|
if (DateTime.Now < _continueAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
EAction adjustedActionId = (EAction)ActionManager.Instance()->GetAdjustedActionId(2260u);
|
|
if (adjustedActionId == EAction.RabbitMedium)
|
|
{
|
|
_continueAt = DateTime.Now.AddSeconds(1.0);
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
IGameObject gameObject = gameFunctions.FindObjectByDataId(base.Task.DataId);
|
|
if (gameObject == null || !gameObject.IsTargetable)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (adjustedActionId == base.Task.Action)
|
|
{
|
|
_continueAt = DateTime.Now.AddSeconds(0.25);
|
|
if (!gameFunctions.UseAction(gameObject, base.Task.Action))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
if (Combos.TryGetValue(base.Task.Action, out Dictionary<EAction, EAction> value))
|
|
{
|
|
if (value.TryGetValue(adjustedActionId, out var value2))
|
|
{
|
|
_continueAt = DateTime.Now.AddSeconds(0.25);
|
|
gameFunctions.UseAction(value2);
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
_continueAt = DateTime.Now.AddSeconds(0.25);
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
logger.LogError("Unable to find relevant combo for {Action}", base.Task.Action);
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record TriggerStatusIfMissing(EStatus Status, EAction Action) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"TriggerStatus({Status})";
|
|
}
|
|
}
|
|
|
|
internal sealed class TriggerStatusIfMissingExecutor(GameFunctions gameFunctions) : TaskExecutor<TriggerStatusIfMissing>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
if (gameFunctions.HasStatus(base.Task.Status))
|
|
{
|
|
return false;
|
|
}
|
|
gameFunctions.UseAction(base.Task.Action);
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!gameFunctions.HasStatus(base.Task.Status))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|