405 lines
10 KiB
C#
405 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Plugin.Services;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Controller.Utils;
|
|
using Questionable.Data;
|
|
using Questionable.External;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Shared;
|
|
|
|
internal static class WaitAtEnd
|
|
{
|
|
internal sealed class Factory(IClientState clientState, ICondition condition, TerritoryData territoryData, AutoDutyIpc autoDutyIpc, BossModIpc bossModIpc) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.CompletionQuestVariablesFlags.Count == 6 && QuestWorkUtils.HasCompletionFlags(step.CompletionQuestVariablesFlags))
|
|
{
|
|
WaitForCompletionFlags waitForCompletionFlags = new WaitForCompletionFlags((QuestId)quest.Id, step);
|
|
WaitDelay waitDelay = new WaitDelay();
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[3]
|
|
{
|
|
waitForCompletionFlags,
|
|
waitDelay,
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
ITask task;
|
|
switch (step.InteractionType)
|
|
{
|
|
case EInteractionType.Combat:
|
|
{
|
|
if (step.EnemySpawnType == EEnemySpawnType.FinishCombatIfAny)
|
|
{
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(Next(quest, sequence));
|
|
}
|
|
WaitCondition.Task task2 = new WaitCondition.Task(() => !condition[ConditionFlag.InCombat], "Wait(not in combat)");
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[4]
|
|
{
|
|
new WaitDelay(),
|
|
task2,
|
|
new WaitDelay(),
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
case EInteractionType.WaitForManualProgress:
|
|
case EInteractionType.Snipe:
|
|
case EInteractionType.Instruction:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(new WaitNextStepOrSequence());
|
|
case EInteractionType.Duty:
|
|
if (autoDutyIpc.IsConfiguredToRunContent(step.DutyOptions))
|
|
{
|
|
break;
|
|
}
|
|
goto IL_019d;
|
|
case EInteractionType.SinglePlayerDuty:
|
|
if (bossModIpc.IsConfiguredToRunSoloInstance(quest.Id, step.SinglePlayerDutyOptions))
|
|
{
|
|
break;
|
|
}
|
|
goto IL_019d;
|
|
case EInteractionType.WalkTo:
|
|
case EInteractionType.Jump:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(Next(quest, sequence));
|
|
case EInteractionType.WaitForObjectAtPosition:
|
|
ArgumentNullException.ThrowIfNull(step.DataId, "step.DataId");
|
|
ArgumentNullException.ThrowIfNull(step.Position, "step.Position");
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[3]
|
|
{
|
|
new WaitObjectAtPosition(step.DataId.Value, step.Position.Value, step.NpcWaitDistance ?? 0.5f),
|
|
new WaitDelay(),
|
|
Next(quest, sequence)
|
|
});
|
|
case EInteractionType.Interact:
|
|
if (!step.TargetTerritoryId.HasValue)
|
|
{
|
|
break;
|
|
}
|
|
goto IL_0284;
|
|
case EInteractionType.UseItem:
|
|
if (!step.TargetTerritoryId.HasValue)
|
|
{
|
|
break;
|
|
}
|
|
goto IL_0284;
|
|
case EInteractionType.AcceptQuest:
|
|
{
|
|
WaitQuestAccepted waitQuestAccepted = new WaitQuestAccepted(step.PickUpQuestId ?? quest.Id);
|
|
WaitDelay waitDelay3 = new WaitDelay();
|
|
if (step.PickUpQuestId != null)
|
|
{
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[3]
|
|
{
|
|
waitQuestAccepted,
|
|
waitDelay3,
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[2] { waitQuestAccepted, waitDelay3 });
|
|
}
|
|
case EInteractionType.CompleteQuest:
|
|
{
|
|
WaitQuestCompleted waitQuestCompleted = new WaitQuestCompleted(step.TurnInQuestId ?? quest.Id);
|
|
WaitDelay waitDelay2 = new WaitDelay();
|
|
if (step.TurnInQuestId != null)
|
|
{
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[3]
|
|
{
|
|
waitQuestCompleted,
|
|
waitDelay2,
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[2] { waitQuestCompleted, waitDelay2 });
|
|
}
|
|
IL_019d:
|
|
return new global::_003C_003Ez__ReadOnlySingleElementList<ITask>(new EndAutomation());
|
|
IL_0284:
|
|
if (step.TerritoryId != step.TargetTerritoryId)
|
|
{
|
|
task = new WaitCondition.Task(() => clientState.TerritoryType == step.TargetTerritoryId, "Wait(tp to territory: " + territoryData.GetNameAndId(step.TargetTerritoryId.Value) + ")");
|
|
}
|
|
else
|
|
{
|
|
Vector3 lastPosition = step.Position ?? clientState.LocalPlayer?.Position ?? Vector3.Zero;
|
|
task = new WaitCondition.Task(delegate
|
|
{
|
|
Vector3? vector = clientState.LocalPlayer?.Position;
|
|
return vector.HasValue && (lastPosition - vector.Value).Length() > 2f;
|
|
}, "Wait(tp away from " + lastPosition.ToString("G", CultureInfo.InvariantCulture) + ")");
|
|
}
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[3]
|
|
{
|
|
task,
|
|
new WaitDelay(),
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
return new global::_003C_003Ez__ReadOnlyArray<ITask>(new ITask[2]
|
|
{
|
|
new WaitDelay(),
|
|
Next(quest, sequence)
|
|
});
|
|
}
|
|
|
|
private static NextStep Next(Quest quest, QuestSequence sequence)
|
|
{
|
|
return new NextStep(quest.Id, sequence.Sequence);
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitDelay(TimeSpan Delay) : ITask
|
|
{
|
|
public WaitDelay()
|
|
: this(TimeSpan.FromSeconds(1L))
|
|
{
|
|
}
|
|
|
|
public bool ShouldRedoOnInterrupt()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"Wait(seconds: {Delay.TotalSeconds})";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitDelayExecutor : AbstractDelayedTaskExecutor<WaitDelay>
|
|
{
|
|
protected override bool StartInternal()
|
|
{
|
|
base.Delay = base.Task.Delay;
|
|
return true;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitNextStepOrSequence : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "Wait(next step or sequence)";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitNextStepOrSequenceExecutor : TaskExecutor<WaitNextStepOrSequence>
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitForCompletionFlags(QuestId Quest, QuestStep Step) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "Wait(QW: " + string.Join(", ", Step.CompletionQuestVariablesFlags.Select((QuestWorkValue x) => x?.ToString() ?? "-")) + ")";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitForCompletionFlagsExecutor(QuestFunctions questFunctions) : TaskExecutor<WaitForCompletionFlags>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(base.Task.Quest);
|
|
if (questProgressInfo == null || !QuestWorkUtils.MatchesQuestWork(base.Task.Step.CompletionQuestVariablesFlags, questProgressInfo))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitObjectAtPosition(uint DataId, Vector3 Destination, float Distance) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"WaitObj({DataId} at {Destination.ToString("G", CultureInfo.InvariantCulture)} < {Distance})";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitObjectAtPositionExecutor(GameFunctions gameFunctions) : TaskExecutor<WaitObjectAtPosition>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!gameFunctions.IsObjectAtPosition(base.Task.DataId, base.Task.Destination, base.Task.Distance))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitQuestAccepted(ElementId ElementId) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"WaitQuestAccepted({ElementId})";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitQuestAcceptedExecutor(QuestFunctions questFunctions) : TaskExecutor<WaitQuestAccepted>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!questFunctions.IsQuestAccepted(base.Task.ElementId))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record WaitQuestCompleted(ElementId ElementId) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"WaitQuestComplete({ElementId})";
|
|
}
|
|
}
|
|
|
|
internal sealed class WaitQuestCompletedExecutor(QuestFunctions questFunctions) : TaskExecutor<WaitQuestCompleted>()
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (!questFunctions.IsQuestComplete(base.Task.ElementId))
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed record NextStep(ElementId ElementId, int Sequence) : ILastTask, ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "NextStep";
|
|
}
|
|
}
|
|
|
|
internal sealed class NextStepExecutor : TaskExecutor<NextStep>
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
return ETaskResult.NextStep;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal sealed class EndAutomation : ILastTask, ITask
|
|
{
|
|
public ElementId ElementId
|
|
{
|
|
get
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
public int Sequence
|
|
{
|
|
get
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "EndAutomation";
|
|
}
|
|
}
|
|
|
|
internal sealed class EndAutomationExecutor : TaskExecutor<EndAutomation>
|
|
{
|
|
protected override bool Start()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
return ETaskResult.End;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|