qstbak/Questionable/Questionable.Controller.Steps.Shared/AetheryteTeleport.cs
2026-08-17 20:29:32 +10:00

184 lines
5.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.Game.ClientState.Objects.SubKinds;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Movement;
using Questionable.Functions;
using SmartNav.Data;
using SmartNav.Model;
namespace Questionable.Controller.Steps.Shared;
internal static class AetheryteTeleport
{
internal sealed record Task(EAetheryteLocation TargetAetheryte, ushort ExpectedTerritoryId) : ISkippableTask, ITask
{
public override string ToString()
{
return $"UseAetheryte({TargetAetheryte})";
}
}
internal sealed class AetheryteTeleportExecutor(ILogger<AetheryteTeleportExecutor> logger, AetheryteFunctions aetheryteFunctions, IClientState clientState, IObjectTable objectTable, IChatGui chatGui, ICondition condition, AetheryteData aetheryteData) : TaskExecutor<Task>()
{
private bool _teleported;
private long _retryTeleportAt;
private int _teleportAttempts;
private const int MaxTeleportAttempts = 3;
protected override bool Start()
{
return true;
}
public override ETaskResult Update()
{
if (!_teleported)
{
if (Environment.TickCount64 < _retryTeleportAt)
{
return ETaskResult.StillRunning;
}
_teleported = DoTeleport();
return ETaskResult.StillRunning;
}
if (clientState.TerritoryType == base.Task.ExpectedTerritoryId)
{
IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null || aetheryteData.CalculateDistance(localPlayer.Position, clientState.TerritoryType, base.Task.TargetAetheryte) <= 50f)
{
ResetProgressTimer();
return ETaskResult.TaskComplete;
}
}
if (base.ProgressContext != null)
{
if (base.ProgressContext.WasInterrupted())
{
logger.LogDebug("Teleport to {Aetheryte} was interrupted", base.Task.TargetAetheryte);
return ETaskResult.StillRunning;
}
if (base.ProgressContext.WasSuccessful())
{
ResetProgressTimer();
}
}
return ETaskResult.StillRunning;
}
private bool DoTeleport()
{
if (!aetheryteFunctions.CanTeleport(base.Task.TargetAetheryte))
{
if (!aetheryteFunctions.IsTeleportUnlocked())
{
throw new TaskException("Teleport is not unlocked, attune to any aetheryte first.");
}
_retryTeleportAt = Environment.TickCount64 + 1000;
logger.LogTrace("Waiting for teleport cooldown...");
return false;
}
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;
}
if (++_teleportAttempts < 3)
{
_retryTeleportAt = Environment.TickCount64 + 1000;
logger.LogWarning("Teleport did not start (attempt {Attempt}/{Max}), retrying...", _teleportAttempts, 3);
return false;
}
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, IObjectTable objectTable) : TaskExecutor<MoveAwayFromAetheryte>()
{
private static readonly Dictionary<EAetheryteLocation, List<Vector3>> AetherytesToMoveFrom;
public static bool AppliesTo(EAetheryteLocation location)
{
return AetherytesToMoveFrom.ContainsKey(location);
}
protected override bool Start()
{
IPlayerCharacter localPlayer = objectTable.LocalPlayer;
if (localPlayer == null)
{
return false;
}
Vector3 playerPosition = 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, MountRequired: false, DismountRequired: 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);
span[0] = new Vector3(0f, 8.8f, 15.5f);
span[1] = new Vector3(0f, 8.8f, -15.5f);
span[2] = new Vector3(15.5f, 8.8f, 0f);
span[3] = new Vector3(-15.5f, 8.8f, 0f);
dictionary.Add(EAetheryteLocation.SolutionNine, list);
AetherytesToMoveFrom = dictionary;
}
}
}