261 lines
10 KiB
C#
261 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Runtime.InteropServices;
|
|
using Dalamud.Game.ClientState.Conditions;
|
|
using Dalamud.Plugin.Services;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller.Steps.Common;
|
|
using Questionable.Controller.Steps.Movement;
|
|
using Questionable.Controller.Utils;
|
|
using Questionable.Data;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Common;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Shared;
|
|
|
|
internal static class AetheryteShortcut
|
|
{
|
|
internal sealed class Factory(AetheryteData aetheryteData, TerritoryData territoryData, IClientState clientState) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (!step.AetheryteShortcut.HasValue)
|
|
{
|
|
yield break;
|
|
}
|
|
yield return new Task(step, quest.Id, step.AetheryteShortcut.Value, aetheryteData.TerritoryIds[step.AetheryteShortcut.Value]);
|
|
yield return new WaitAtEnd.WaitDelay(TimeSpan.FromSeconds(0.5));
|
|
if (MoveAwayFromAetheryteExecutor.AppliesTo(step.AetheryteShortcut.Value) && step.AethernetShortcut?.From != step.AetheryteShortcut.Value)
|
|
{
|
|
yield return new WaitCondition.Task(() => clientState.TerritoryType == aetheryteData.TerritoryIds[step.AetheryteShortcut.Value], "Wait(territory: " + territoryData.GetNameAndId(aetheryteData.TerritoryIds[step.AetheryteShortcut.Value]) + ")");
|
|
yield return new MoveAwayFromAetheryte(step.AetheryteShortcut.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed record Task(QuestStep? Step, ElementId? ElementId, EAetheryteLocation TargetAetheryte, ushort ExpectedTerritoryId) : ISkippableTask, ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"UseAetheryte({TargetAetheryte})";
|
|
}
|
|
}
|
|
|
|
internal sealed class UseAetheryteShortcut(ILogger<UseAetheryteShortcut> logger, AetheryteFunctions aetheryteFunctions, QuestFunctions questFunctions, IClientState clientState, IChatGui chatGui, ICondition condition, AetheryteData aetheryteData, ExtraConditionUtils extraConditionUtils) : TaskExecutor<Task>()
|
|
{
|
|
private bool _teleported;
|
|
|
|
private DateTime _continueAt;
|
|
|
|
protected override bool Start()
|
|
{
|
|
return !ShouldSkipTeleport();
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (DateTime.Now < _continueAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!_teleported)
|
|
{
|
|
_teleported = DoTeleport();
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (clientState.TerritoryType == base.Task.ExpectedTerritoryId)
|
|
{
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
|
|
private bool ShouldSkipTeleport()
|
|
{
|
|
ushort territoryType = clientState.TerritoryType;
|
|
if (base.Task.Step != null)
|
|
{
|
|
SkipAetheryteCondition skipAetheryteCondition = base.Task.Step.SkipConditions?.AetheryteShortcutIf ?? new SkipAetheryteCondition();
|
|
if (skipAetheryteCondition != null && !skipAetheryteCondition.Never)
|
|
{
|
|
if (skipAetheryteCondition.InTerritory.Contains(territoryType))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (InTerritory)");
|
|
return true;
|
|
}
|
|
if (skipAetheryteCondition.QuestsCompleted.Count > 0 && skipAetheryteCondition.QuestsCompleted.All(questFunctions.IsQuestComplete))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte, all prequisite quests are complete");
|
|
return true;
|
|
}
|
|
if (skipAetheryteCondition.QuestsAccepted.Count > 0 && skipAetheryteCondition.QuestsAccepted.All(questFunctions.IsQuestAccepted))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte, all prequisite quests are accepted");
|
|
return true;
|
|
}
|
|
if (skipAetheryteCondition.AetheryteLocked.HasValue && !aetheryteFunctions.IsAetheryteUnlocked(skipAetheryteCondition.AetheryteLocked.Value))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (AetheryteLocked)");
|
|
return true;
|
|
}
|
|
if (skipAetheryteCondition.AetheryteUnlocked.HasValue && aetheryteFunctions.IsAetheryteUnlocked(skipAetheryteCondition.AetheryteUnlocked.Value))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (AetheryteUnlocked)");
|
|
return true;
|
|
}
|
|
if (base.Task.ElementId != null)
|
|
{
|
|
QuestProgressInfo questProgressInfo = questFunctions.GetQuestProgressInfo(base.Task.ElementId);
|
|
if (skipAetheryteCondition.RequiredQuestVariablesNotMet && questProgressInfo != null && !QuestWorkUtils.MatchesRequiredQuestWorkConfig(base.Task.Step.RequiredQuestVariables, questProgressInfo, logger))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport, as required variables do not match");
|
|
return true;
|
|
}
|
|
}
|
|
NearPositionCondition nearPosition = skipAetheryteCondition.NearPosition;
|
|
if (nearPosition != null && clientState.TerritoryType == nearPosition.TerritoryId && Vector3.Distance(nearPosition.Position, clientState.LocalPlayer.Position) <= nearPosition.MaximumDistance)
|
|
{
|
|
logger.LogInformation("Skipping aetheryte shortcut, as we're near the position");
|
|
return true;
|
|
}
|
|
NearPositionCondition notNearPosition = skipAetheryteCondition.NotNearPosition;
|
|
if (notNearPosition != null && clientState.TerritoryType == notNearPosition.TerritoryId && notNearPosition.MaximumDistance <= Vector3.Distance(notNearPosition.Position, clientState.LocalPlayer.Position))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte shortcut, as we're not near the position");
|
|
return true;
|
|
}
|
|
if (skipAetheryteCondition.ExtraCondition.HasValue && skipAetheryteCondition.ExtraCondition != EExtraSkipCondition.None && extraConditionUtils.MatchesExtraCondition(skipAetheryteCondition.ExtraCondition.Value))
|
|
{
|
|
logger.LogInformation("Skipping step, extra condition {} matches", skipAetheryteCondition.ExtraCondition);
|
|
return true;
|
|
}
|
|
}
|
|
if (base.Task.ExpectedTerritoryId == territoryType && !skipAetheryteCondition.Never)
|
|
{
|
|
if (skipAetheryteCondition != null && skipAetheryteCondition.InSameTerritory)
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport due to SkipCondition (InSameTerritory)");
|
|
return true;
|
|
}
|
|
Vector3 position = clientState.LocalPlayer.Position;
|
|
if (base.Task.Step.Position.HasValue && (position - base.Task.Step.Position.Value).Length() < base.Task.Step.CalculateActualStopDistance())
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport, we're near the target");
|
|
return true;
|
|
}
|
|
if (aetheryteData.CalculateDistance(position, territoryType, base.Task.TargetAetheryte) < 20f || (base.Task.Step.AethernetShortcut != null && (aetheryteData.CalculateDistance(position, territoryType, base.Task.Step.AethernetShortcut.From) < 20f || aetheryteData.CalculateDistance(position, territoryType, base.Task.Step.AethernetShortcut.To) < 20f)))
|
|
{
|
|
logger.LogInformation("Skipping aetheryte teleport");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool DoTeleport()
|
|
{
|
|
if (!aetheryteFunctions.CanTeleport(base.Task.TargetAetheryte))
|
|
{
|
|
if (!aetheryteFunctions.IsTeleportUnlocked())
|
|
{
|
|
throw new TaskException("Teleport is not unlocked, attune to any aetheryte first.");
|
|
}
|
|
_continueAt = DateTime.Now.AddSeconds(1.0);
|
|
logger.LogTrace("Waiting for teleport cooldown...");
|
|
return false;
|
|
}
|
|
_continueAt = DateTime.Now.AddSeconds(8.0);
|
|
if (!aetheryteFunctions.IsAetheryteUnlocked(base.Task.TargetAetheryte))
|
|
{
|
|
chatGui.PrintError($"Aetheryte {base.Task.TargetAetheryte} is not unlocked.", "Questionable", 576);
|
|
throw new TaskException("Aetheryte is not unlocked");
|
|
}
|
|
base.ProgressContext = InteractionProgressContext.FromActionUseOrDefault(() => aetheryteFunctions.TeleportAetheryte(base.Task.TargetAetheryte));
|
|
if (base.ProgressContext != null)
|
|
{
|
|
logger.LogInformation("Travelling via aetheryte...");
|
|
return true;
|
|
}
|
|
chatGui.Print("Unable to teleport to aetheryte.", "Questionable", 576);
|
|
throw new TaskException("Unable to teleport to aetheryte");
|
|
}
|
|
|
|
public override bool WasInterrupted()
|
|
{
|
|
if (!condition[ConditionFlag.InCombat])
|
|
{
|
|
return base.WasInterrupted();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal sealed record MoveAwayFromAetheryte(EAetheryteLocation TargetAetheryte) : ITask
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"MoveAway({TargetAetheryte})";
|
|
}
|
|
}
|
|
|
|
internal sealed class MoveAwayFromAetheryteExecutor(MoveExecutor moveExecutor, AetheryteData aetheryteData, IClientState clientState) : TaskExecutor<MoveAwayFromAetheryte>()
|
|
{
|
|
private static readonly Dictionary<EAetheryteLocation, List<Vector3>> AetherytesToMoveFrom;
|
|
|
|
public static bool AppliesTo(EAetheryteLocation location)
|
|
{
|
|
return AetherytesToMoveFrom.ContainsKey(location);
|
|
}
|
|
|
|
protected override bool Start()
|
|
{
|
|
Vector3 playerPosition = clientState.LocalPlayer.Position;
|
|
if (aetheryteData.CalculateDistance(playerPosition, clientState.TerritoryType, base.Task.TargetAetheryte) >= 20f)
|
|
{
|
|
return false;
|
|
}
|
|
Vector3 destination = AetherytesToMoveFrom[base.Task.TargetAetheryte].MinBy((Vector3 x) => Vector3.Distance(x, playerPosition));
|
|
MoveTask task = new MoveTask(aetheryteData.TerritoryIds[base.Task.TargetAetheryte], destination, false, 0.25f, null, DisableNavmesh: true, null, Fly: false, Land: false, IgnoreDistanceToObject: false, RestartNavigation: false);
|
|
return moveExecutor.Start(task);
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
return moveExecutor.Update();
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static MoveAwayFromAetheryteExecutor()
|
|
{
|
|
Dictionary<EAetheryteLocation, List<Vector3>> dictionary = new Dictionary<EAetheryteLocation, List<Vector3>>();
|
|
int num = 4;
|
|
List<Vector3> list = new List<Vector3>(num);
|
|
CollectionsMarshal.SetCount(list, num);
|
|
Span<Vector3> span = CollectionsMarshal.AsSpan(list);
|
|
int num2 = 0;
|
|
span[num2] = new Vector3(0f, 8.8f, 15.5f);
|
|
num2++;
|
|
span[num2] = new Vector3(0f, 8.8f, -15.5f);
|
|
num2++;
|
|
span[num2] = new Vector3(15.5f, 8.8f, 0f);
|
|
num2++;
|
|
span[num2] = new Vector3(-15.5f, 8.8f, 0f);
|
|
dictionary.Add(EAetheryteLocation.SolutionNine, list);
|
|
AetherytesToMoveFrom = dictionary;
|
|
}
|
|
}
|
|
}
|