236 lines
8.7 KiB
C#
236 lines
8.7 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.Data;
|
|
using Questionable.External;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Common;
|
|
using Questionable.Model.Common.Converter;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Controller.Steps.Shared;
|
|
|
|
internal static class AethernetShortcut
|
|
{
|
|
internal sealed class Factory(AetheryteData aetheryteData, TerritoryData territoryData, IClientState clientState) : ITaskFactory
|
|
{
|
|
public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
|
|
{
|
|
if (step.AethernetShortcut == null)
|
|
{
|
|
yield break;
|
|
}
|
|
yield return new WaitNavmesh.Task();
|
|
yield return new Task(step.AethernetShortcut.From, step.AethernetShortcut.To, step.SkipConditions?.AethernetShortcutIf ?? new SkipAetheryteCondition());
|
|
if (AetheryteShortcut.MoveAwayFromAetheryteExecutor.AppliesTo(step.AethernetShortcut.To))
|
|
{
|
|
yield return new WaitCondition.Task(() => clientState.TerritoryType == aetheryteData.TerritoryIds[step.AethernetShortcut.To], "Wait(territory: " + territoryData.GetNameAndId(aetheryteData.TerritoryIds[step.AethernetShortcut.To]) + ")");
|
|
yield return new AetheryteShortcut.MoveAwayFromAetheryte(step.AethernetShortcut.To);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed record Task(EAetheryteLocation From, EAetheryteLocation To, SkipAetheryteCondition SkipConditions) : ISkippableTask, ITask
|
|
{
|
|
public Task(EAetheryteLocation from, EAetheryteLocation to)
|
|
: this(from, to, new SkipAetheryteCondition())
|
|
{
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"UseAethernet({From} -> {To})";
|
|
}
|
|
}
|
|
|
|
internal sealed class UseAethernetShortcut(ILogger<UseAethernetShortcut> logger, AetheryteFunctions aetheryteFunctions, GameFunctions gameFunctions, QuestFunctions questFunctions, IClientState clientState, AetheryteData aetheryteData, TerritoryData territoryData, LifestreamIpc lifestreamIpc, MovementController movementController, ICondition condition) : TaskExecutor<Task>()
|
|
{
|
|
private bool _moving;
|
|
|
|
private bool _teleported;
|
|
|
|
private bool _triedMounting;
|
|
|
|
private DateTime _continueAt = DateTime.MinValue;
|
|
|
|
protected override bool Start()
|
|
{
|
|
if (!base.Task.SkipConditions.Never)
|
|
{
|
|
if (base.Task.SkipConditions.InSameTerritory && clientState.TerritoryType == aetheryteData.TerritoryIds[base.Task.To])
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut because the target is in the same territory");
|
|
return false;
|
|
}
|
|
if (base.Task.SkipConditions.InTerritory.Contains(clientState.TerritoryType))
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut because the target is in the specified territory");
|
|
return false;
|
|
}
|
|
if (base.Task.SkipConditions.QuestsCompleted.Count > 0 && base.Task.SkipConditions.QuestsCompleted.All(questFunctions.IsQuestComplete))
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut, all prequisite quests are complete");
|
|
return true;
|
|
}
|
|
if (base.Task.SkipConditions.QuestsAccepted.Count > 0 && base.Task.SkipConditions.QuestsAccepted.All(questFunctions.IsQuestAccepted))
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut, all prequisite quests are accepted");
|
|
return true;
|
|
}
|
|
if (base.Task.SkipConditions.AetheryteLocked.HasValue && !aetheryteFunctions.IsAetheryteUnlocked(base.Task.SkipConditions.AetheryteLocked.Value))
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is locked");
|
|
return false;
|
|
}
|
|
if (base.Task.SkipConditions.AetheryteUnlocked.HasValue && aetheryteFunctions.IsAetheryteUnlocked(base.Task.SkipConditions.AetheryteUnlocked.Value))
|
|
{
|
|
logger.LogInformation("Skipping aethernet shortcut because the target aetheryte is unlocked");
|
|
return false;
|
|
}
|
|
}
|
|
if (aetheryteFunctions.IsAetheryteUnlocked(base.Task.From) && aetheryteFunctions.IsAetheryteUnlocked(base.Task.To))
|
|
{
|
|
ushort territoryType = clientState.TerritoryType;
|
|
Vector3 playerPosition = clientState.LocalPlayer.Position;
|
|
if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.To))
|
|
{
|
|
if (aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) < (base.Task.From.IsFirmamentAetheryte() ? 11f : 4f))
|
|
{
|
|
DoTeleport();
|
|
return true;
|
|
}
|
|
if (base.Task.From == EAetheryteLocation.SolutionNine)
|
|
{
|
|
logger.LogInformation("Moving to S9 aetheryte");
|
|
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.442986f, 9f);
|
|
num2++;
|
|
span[num2] = new Vector3(9f, 8.442986f, 0f);
|
|
num2++;
|
|
span[num2] = new Vector3(-9f, 8.442986f, 0f);
|
|
num2++;
|
|
span[num2] = new Vector3(0f, 8.442986f, -9f);
|
|
Vector3 to = list.MinBy((Vector3 x) => Vector3.Distance(playerPosition, x));
|
|
_moving = true;
|
|
movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, to, fly: false, sprint: true, 0.25f);
|
|
return true;
|
|
}
|
|
if (territoryData.CanUseMount(territoryType) && aetheryteData.CalculateDistance(playerPosition, territoryType, base.Task.From) > 30f && !gameFunctions.HasStatusPreventingMount())
|
|
{
|
|
_triedMounting = gameFunctions.Mount();
|
|
if (_triedMounting)
|
|
{
|
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
|
return true;
|
|
}
|
|
}
|
|
MoveTo();
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (clientState.TerritoryType != aetheryteData.TerritoryIds[base.Task.To])
|
|
{
|
|
throw new TaskException($"Aethernet shortcut not unlocked (from: {base.Task.From}, to: {base.Task.To})");
|
|
}
|
|
logger.LogWarning("Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), skipping as we are already in the destination territory", base.Task.From, base.Task.To);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void MoveTo()
|
|
{
|
|
logger.LogInformation("Moving to aethernet shortcut");
|
|
_moving = true;
|
|
EAetheryteLocation eAetheryteLocation = base.Task.From;
|
|
float num = (base.Task.From.IsFirmamentAetheryte() ? 4.4f : ((eAetheryteLocation != EAetheryteLocation.UldahChamberOfRule) ? ((!AetheryteConverter.IsLargeAetheryte(base.Task.From)) ? 6.9f : 10.9f) : 5f));
|
|
float value = num;
|
|
bool flag = aetheryteData.IsGoldSaucerAetheryte(base.Task.From) && !AetheryteConverter.IsLargeAetheryte(base.Task.From);
|
|
movementController.NavigateTo(EMovementType.Quest, (uint)base.Task.From, aetheryteData.Locations[base.Task.From], fly: false, sprint: true, value, flag ? new float?(5f) : ((float?)null));
|
|
}
|
|
|
|
private void DoTeleport()
|
|
{
|
|
logger.LogInformation("Using lifestream to teleport to {Destination}", base.Task.To);
|
|
lifestreamIpc.Teleport(base.Task.To);
|
|
_teleported = true;
|
|
}
|
|
|
|
public override ETaskResult Update()
|
|
{
|
|
if (DateTime.Now < _continueAt)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (_triedMounting)
|
|
{
|
|
if (condition[ConditionFlag.Mounted])
|
|
{
|
|
_triedMounting = false;
|
|
MoveTo();
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (_moving)
|
|
{
|
|
DateTime movementStartedAt = movementController.MovementStartedAt;
|
|
if (movementStartedAt == DateTime.MaxValue || movementStartedAt.AddSeconds(2.0) >= DateTime.Now)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!movementController.IsPathfinding && !movementController.IsPathRunning)
|
|
{
|
|
_moving = false;
|
|
}
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (!_teleported)
|
|
{
|
|
DoTeleport();
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
Vector3? vector = clientState.LocalPlayer?.Position;
|
|
if (!vector.HasValue)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
if (aetheryteData.IsAirshipLanding(base.Task.To))
|
|
{
|
|
if (aetheryteData.CalculateAirshipLandingDistance(vector.Value, clientState.TerritoryType, base.Task.To) > 5f)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
}
|
|
else if (aetheryteData.IsCityAetheryte(base.Task.To) || aetheryteData.IsGoldSaucerAetheryte(base.Task.To))
|
|
{
|
|
if (aetheryteData.CalculateDistance(vector.Value, clientState.TerritoryType, base.Task.To) > 20f)
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
}
|
|
else if (clientState.TerritoryType != aetheryteData.TerritoryIds[base.Task.To])
|
|
{
|
|
return ETaskResult.StillRunning;
|
|
}
|
|
return ETaskResult.TaskComplete;
|
|
}
|
|
|
|
public override bool ShouldInterruptOnDamage()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|