qstbak/Questionable/Questionable.Controller.Steps.Interactions/Action.cs
2025-10-09 07:47:19 +10:00

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;
}
}
}