diff --git a/Questionable/Controller/MovementController.cs b/Questionable/Controller/MovementController.cs index 334d6ace8..6192f4ea8 100644 --- a/Questionable/Controller/MovementController.cs +++ b/Questionable/Controller/MovementController.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Numerics; using System.Threading; using System.Threading.Tasks; +using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game; using Questionable.External; namespace Questionable.Controller; @@ -15,16 +18,18 @@ internal sealed class MovementController : IDisposable private readonly NavmeshIpc _navmeshIpc; private readonly IClientState _clientState; private readonly GameFunctions _gameFunctions; + private readonly ICondition _condition; private readonly IPluginLog _pluginLog; private CancellationTokenSource? _cancellationTokenSource; private Task>? _pathfindTask; public MovementController(NavmeshIpc navmeshIpc, IClientState clientState, GameFunctions gameFunctions, - IPluginLog pluginLog) + ICondition condition, IPluginLog pluginLog) { _navmeshIpc = navmeshIpc; _clientState = clientState; _gameFunctions = gameFunctions; + _condition = condition; _pluginLog = pluginLog; } @@ -33,6 +38,7 @@ internal sealed class MovementController : IDisposable public bool IsPathfinding => _pathfindTask is { IsCompleted: false }; public Vector3? Destination { get; private set; } public float StopDistance { get; private set; } + public bool IsFlying { get; private set; } public void Update() { @@ -41,8 +47,35 @@ internal sealed class MovementController : IDisposable if (_pathfindTask.IsCompletedSuccessfully) { _pluginLog.Information( - $"Pathfinding complete, route: [{string.Join(" → ", _pathfindTask.Result.Select(x => x.ToString()))}]"); - _navmeshIpc.MoveTo(_pathfindTask.Result.Skip(1).ToList()); + string.Create(CultureInfo.InvariantCulture, + $"Pathfinding complete, route: [{string.Join(" → ", _pathfindTask.Result.Select(x => x.ToString()))}]")); + + var navPoints = _pathfindTask.Result.Skip(1).ToList(); + if (!IsFlying && !_condition[ConditionFlag.Mounted] && navPoints.Count > 0 && + !_gameFunctions.HasStatusPreventingSprintOrMount()) + { + Vector3 start = _clientState.LocalPlayer?.Position ?? navPoints[0]; + float actualDistance = 0; + foreach (Vector3 end in navPoints) + { + actualDistance += (start - end).Length(); + start = end; + } + + _pluginLog.Information($"Distance: {actualDistance}"); + unsafe + { + // 70 is ~10 seconds of sprint + if (actualDistance > 100f && + ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 4) == 0) + { + _pluginLog.Information("Triggering Sprint"); + ActionManager.Instance()->UseAction(ActionType.GeneralAction, 4); + } + } + } + + _navmeshIpc.MoveTo(navPoints); ResetPathfinding(); } else if (_pathfindTask.IsCompleted) @@ -60,21 +93,32 @@ internal sealed class MovementController : IDisposable } } - public void NavigateTo(EMovementType type, Vector3 to, bool fly, float? stopDistance = null) + private void PrepareNavigation(EMovementType type, Vector3 to, bool fly, float? stopDistance) { ResetPathfinding(); - _gameFunctions.ExecuteCommand("/automove off"); Destination = to; StopDistance = stopDistance ?? (DefaultStopDistance - 0.2f); + IsFlying = fly; + } + + public void NavigateTo(EMovementType type, Vector3 to, bool fly, float? stopDistance = null) + { + PrepareNavigation(type, to, fly, stopDistance); _cancellationTokenSource = new(); _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10)); _pathfindTask = _navmeshIpc.Pathfind(_clientState.LocalPlayer!.Position, to, fly, _cancellationTokenSource.Token); } + public void NavigateTo(EMovementType type, List to, bool fly, float? stopDistance) + { + PrepareNavigation(type, to.Last(), fly, stopDistance); + _navmeshIpc.MoveTo(to); + } + public void ResetPathfinding() { if (_cancellationTokenSource != null) diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index d3836af09..58730e4d6 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -10,47 +10,69 @@ using Dalamud.Plugin; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Object; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Questionable.Data; +using Questionable.External; using Questionable.Model.V1; namespace Questionable.Controller; internal sealed class QuestController { + private readonly DalamudPluginInterface _pluginInterface; + private readonly IDataManager _dataManager; private readonly IClientState _clientState; private readonly GameFunctions _gameFunctions; private readonly MovementController _movementController; private readonly IPluginLog _pluginLog; private readonly ICondition _condition; private readonly IChatGui _chatGui; - private readonly ICommandManager _commandManager; private readonly AetheryteData _aetheryteData; + private readonly LifestreamIpc _lifestreamIpc; private readonly TerritoryData _territoryData; private readonly Dictionary _quests = new(); public QuestController(DalamudPluginInterface pluginInterface, IDataManager dataManager, IClientState clientState, GameFunctions gameFunctions, MovementController movementController, IPluginLog pluginLog, ICondition condition, - IChatGui chatGui, ICommandManager commandManager) + IChatGui chatGui, AetheryteData aetheryteData, LifestreamIpc lifestreamIpc) { + _pluginInterface = pluginInterface; + _dataManager = dataManager; _clientState = clientState; _gameFunctions = gameFunctions; _movementController = movementController; _pluginLog = pluginLog; _condition = condition; _chatGui = chatGui; - _commandManager = commandManager; - _aetheryteData = new AetheryteData(dataManager); + _aetheryteData = aetheryteData; + _lifestreamIpc = lifestreamIpc; _territoryData = new TerritoryData(dataManager); + + Reload(); + } + + + public QuestProgress? CurrentQuest { get; set; } + public string? DebugState { get; private set; } + public string? Comment { get; private set; } + + public void Reload() + { + _quests.Clear(); + + CurrentQuest = null; + DebugState = null; + #if false LoadFromEmbeddedResources(); #endif LoadFromDirectory(new DirectoryInfo(@"E:\ffxiv\Questionable\Questionable\QuestPaths")); - LoadFromDirectory(pluginInterface.ConfigDirectory); + LoadFromDirectory(_pluginInterface.ConfigDirectory); foreach (var (questId, quest) in _quests) { var questData = - dataManager.GetExcelSheet()!.GetRow((uint)questId + 0x10000); + _dataManager.GetExcelSheet()!.GetRow((uint)questId + 0x10000); if (questData == null) continue; @@ -79,9 +101,6 @@ internal sealed class QuestController } #endif - public QuestProgress? CurrentQuest { get; set; } - public string? DebugState { get; set; } - private void LoadFromDirectory(DirectoryInfo configDirectory) { foreach (FileInfo fileInfo in configDirectory.GetFiles("*.json")) @@ -120,6 +139,9 @@ internal sealed class QuestController public void Update() { + Comment = null; + DebugState = null; + (ushort currentQuestId, byte currentSequence) = _gameFunctions.GetCurrentQuest(); if (currentQuestId == 0) { @@ -185,7 +207,8 @@ internal sealed class QuestController } var step = sequence.Steps[CurrentQuest.Step]; - DebugState = step.Comment ?? sequence.Comment ?? q.Data.Comment; + DebugState = null; + Comment = step.Comment ?? sequence.Comment ?? q.Data.Comment; } public (QuestSequence? Sequence, QuestStep? Step) GetNextStep() @@ -216,8 +239,7 @@ internal sealed class QuestController CurrentQuest = CurrentQuest with { Step = CurrentQuest.Step + 1, - AetheryteShortcutUsed = false, - AethernetShortcutUsed = false + StepProgress = new() }; } else @@ -225,30 +247,29 @@ internal sealed class QuestController CurrentQuest = CurrentQuest with { Step = 255, - AetheryteShortcutUsed = false, - AethernetShortcutUsed = false + StepProgress = new() }; } } - public void ExecuteNextStep() + public unsafe void ExecuteNextStep() { (QuestSequence? seq, QuestStep? step) = GetNextStep(); if (seq == null || step == null) return; Debug.Assert(CurrentQuest != null, nameof(CurrentQuest) + " != null"); - if (!CurrentQuest.AetheryteShortcutUsed && step.AetheryteShortcut != null) + if (!CurrentQuest.StepProgress.AetheryteShortcutUsed && step.AetheryteShortcut != null) { bool skipTeleport = false; ushort territoryType = _clientState.TerritoryType; if (step.TerritoryId == territoryType) { - Vector3 playerPosition = _clientState.LocalPlayer!.Position; - if (_aetheryteData.CalculateDistance(playerPosition, territoryType, step.AetheryteShortcut.Value) < 11 || + Vector3 pos = _clientState.LocalPlayer!.Position; + if (_aetheryteData.CalculateDistance(pos, territoryType, step.AetheryteShortcut.Value) < 11 || (step.AethernetShortcut != null && - (_aetheryteData.CalculateDistance(playerPosition, territoryType, step.AethernetShortcut.From) < 11 || - _aetheryteData.CalculateDistance(playerPosition, territoryType, step.AethernetShortcut.To) < 11))) + (_aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.From) < 20 || + _aetheryteData.CalculateDistance(pos, territoryType, step.AethernetShortcut.To) < 20))) { skipTeleport = true; } @@ -256,7 +277,10 @@ internal sealed class QuestController if (skipTeleport) { - CurrentQuest = CurrentQuest with { AetheryteShortcutUsed = true }; + CurrentQuest = CurrentQuest with + { + StepProgress = CurrentQuest.StepProgress with { AetheryteShortcutUsed = true } + }; } else { @@ -265,7 +289,10 @@ internal sealed class QuestController if (!_gameFunctions.IsAetheryteUnlocked(step.AetheryteShortcut.Value)) _chatGui.Print($"[Questionable] Aetheryte {step.AetheryteShortcut.Value} is not unlocked."); else if (_gameFunctions.TeleportAetheryte(step.AetheryteShortcut.Value)) - CurrentQuest = CurrentQuest with { AetheryteShortcutUsed = true }; + CurrentQuest = CurrentQuest with + { + StepProgress = CurrentQuest.StepProgress with { AetheryteShortcutUsed = true } + }; else _chatGui.Print("[Questionable] Unable to teleport to aetheryte."); } @@ -276,7 +303,7 @@ internal sealed class QuestController } } - if (!CurrentQuest.AethernetShortcutUsed) + if (!CurrentQuest.StepProgress.AethernetShortcutUsed) { if (step.AethernetShortcut != null) { @@ -291,10 +318,11 @@ internal sealed class QuestController { if (_aetheryteData.CalculateDistance(playerPosition, territoryType, from) < 11) { - // unsure if this works across languages - _commandManager.ProcessCommand( - $"/li {_aetheryteData.AethernetNames[step.AethernetShortcut.To].Replace("The ", "", StringComparison.Ordinal)}"); - CurrentQuest = CurrentQuest with { AethernetShortcutUsed = true }; + _lifestreamIpc.Teleport(to); + CurrentQuest = CurrentQuest with + { + StepProgress = CurrentQuest.StepProgress with { AethernetShortcutUsed = true } + }; } else _movementController.NavigateTo(EMovementType.Quest, _aetheryteData.Locations[from], false, @@ -315,30 +343,57 @@ internal sealed class QuestController var position = _clientState.LocalPlayer?.Position ?? new Vector3(); float actualDistance = (position - step.Position.Value).Length(); - if (actualDistance > 30f && !_condition[ConditionFlag.Mounted] && - _territoryData.CanUseMount(_clientState.TerritoryType)) + + if (step.Mount == true && !_gameFunctions.HasStatusPreventingSprintOrMount()) { - unsafe + if (!_condition[ConditionFlag.Mounted] && _territoryData.CanUseMount(_clientState.TerritoryType)) { - ActionManager.Instance()->UseAction(ActionType.Mount, 71); + if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, 71) == 0) + ActionManager.Instance()->UseAction(ActionType.Mount, 71); + return; + } + } + else if (step.Mount == false) + { + if (_condition[ConditionFlag.Mounted]) + { + _gameFunctions.Unmount(); + return; + } + } + + if (!step.DisableNavmesh) + { + if (step.Mount != false && actualDistance > 30f && !_condition[ConditionFlag.Mounted] && + _territoryData.CanUseMount(_clientState.TerritoryType)) + { + if (ActionManager.Instance()->GetActionStatus(ActionType.Mount, 71) == 0) + ActionManager.Instance()->UseAction(ActionType.Mount, 71); + return; } - return; + if (actualDistance > distance) + { + _movementController.NavigateTo(EMovementType.Quest, step.Position.Value, + _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); + return; + } } - else if (actualDistance > distance) + else { - _movementController.NavigateTo(EMovementType.Quest, step.Position.Value, - _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); - return; + if (actualDistance > distance) + { + _movementController.NavigateTo(EMovementType.Quest, [step.Position.Value], + _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); + return; + } } } switch (step.InteractionType) { case EInteractionType.Interact: - case EInteractionType.AttuneAetheryte: case EInteractionType.AttuneAethernetShard: - case EInteractionType.AttuneAetherCurrent: if (step.DataId != null) { _gameFunctions.InteractWith(step.DataId.Value); @@ -347,10 +402,81 @@ internal sealed class QuestController break; + case EInteractionType.AttuneAetheryte: + if (step.DataId != null) + { + if (!_gameFunctions.IsAetheryteUnlocked((EAetheryteLocation)step.DataId.Value)) + _gameFunctions.InteractWith(step.DataId.Value); + + IncreaseStepCount(); + } + + break; + + case EInteractionType.AttuneAetherCurrent: + if (step.DataId != null) + { + _pluginLog.Information( + $"{step.AetherCurrentId} → {_gameFunctions.IsAetherCurrentUnlocked(step.AetherCurrentId.GetValueOrDefault())}"); + if (step.AetherCurrentId == null || + !_gameFunctions.IsAetherCurrentUnlocked(step.AetherCurrentId.Value)) + _gameFunctions.InteractWith(step.DataId.Value); + + IncreaseStepCount(); + } + + break; + case EInteractionType.WalkTo: IncreaseStepCount(); break; + case EInteractionType.UseItem: + if (step is { DataId: not null, ItemId: not null }) + { + if (_gameFunctions.Unmount()) + return; + + _gameFunctions.UseItem(step.DataId.Value, step.ItemId.Value); + IncreaseStepCount(); + } + + break; + + case EInteractionType.Combat: + if (step.EnemySpawnType != null) + { + if (_gameFunctions.Unmount()) + return; + + if (step.DataId != null && step.EnemySpawnType == EEnemySpawnType.AfterInteraction) + _gameFunctions.InteractWith(step.DataId.Value); + + // next sequence should trigger automatically + IncreaseStepCount(); + } + + break; + + case EInteractionType.Emote: + if (step is { DataId: not null, Emote: not null }) + { + _gameFunctions.UseEmote(step.DataId.Value, step.Emote.Value); + IncreaseStepCount(); + } + + break; + + case EInteractionType.WaitForObjectAtPosition: + if (step is { DataId: not null, Position: not null } && + !_gameFunctions.IsObbjectAtPosition(step.DataId.Value, step.Position.Value)) + { + return; + } + + IncreaseStepCount(); + break; + default: _pluginLog.Warning($"Action '{step.InteractionType}' is not implemented"); break; @@ -361,6 +487,15 @@ internal sealed class QuestController Quest Quest, byte Sequence, int Step, + StepProgress StepProgress) + { + public QuestProgress(Quest quest, byte sequence, int step) + : this(quest, sequence, step, new StepProgress()) + { + } + } + + public sealed record StepProgress( bool AetheryteShortcutUsed = false, bool AethernetShortcutUsed = false); } diff --git a/Questionable/Data/AetheryteData.cs b/Questionable/Data/AetheryteData.cs index 8bc261e5c..7f0fb6659 100644 --- a/Questionable/Data/AetheryteData.cs +++ b/Questionable/Data/AetheryteData.cs @@ -40,7 +40,51 @@ internal sealed class AetheryteData { EAetheryteLocation.LimsaMarauder, new(-5.1728516f, 44.63257f, -218.06671f) }, { EAetheryteLocation.LimsaHawkersAlley, new(-213.61108f, 16.739136f, 51.80432f) }, - // ... missing a few + { EAetheryteLocation.Ishgard, new(-63.98114f, 11.154297f, 43.9917f) }, + { EAetheryteLocation.IshgardForgottenKnight, new(45.792236f, 24.551636f, 0.99176025f) }, + { EAetheryteLocation.IshgardSkysteelManufactory, new(-111.436646f, 16.128723f, -27.054321f) }, + { EAetheryteLocation.IshgardBrume, new(49.42395f, -11.154419f, 66.69714f) }, + { EAetheryteLocation.IshgardAthenaeumAstrologicum, new(133.37903f, -8.86554f, -64.77466f) }, + { EAetheryteLocation.IshgardJeweledCrozier, new(-134.6914f, -11.795227f, -15.396423f) }, + { EAetheryteLocation.IshgardSaintReymanaudsCathedral, new(-77.958374f, 10.60498f, -126.54315f) }, + { EAetheryteLocation.IshgardTribunal, new(78.01941f, 11.001709f, -126.51257f) }, + { EAetheryteLocation.IshgardLastVigil, new(0.015197754f, 16.525452f, -32.51703f) }, + + { EAetheryteLocation.Idyllshire, new(71.94617f, 211.26111f, -18.905945f) }, + { EAetheryteLocation.IdyllshireWest, new(-75.48645f, 210.22351f, -21.347473f) }, + + { EAetheryteLocation.RhalgrsReach, new(78.23291f, 1.9683228f, 97.45935f) }, + { EAetheryteLocation.RhalgrsReachWest, new(-84.275635f, 0.503479f, 9.323181f) }, + { EAetheryteLocation.RhalgrsReachNorthEast, new(101.24353f, 3.463745f, -115.46509f) }, + + { EAetheryteLocation.Kugane, new(47.501343f, 8.438171f, -37.30841f) }, + { EAetheryteLocation.KuganeShiokazeHostelry, new(-73.16705f, -6.088379f, -77.77527f) }, + { EAetheryteLocation.KuganePier1, new(-113.57294f, -3.8911133f, 155.41309f) }, + { EAetheryteLocation.KuganeThavnairianConsulate, new(27.17627f, 9.048584f, 141.58838f) }, + { EAetheryteLocation.KuganeMarkets, new(26.687988f, 4.92865f, 73.3501f) }, + { EAetheryteLocation.KuganeBokairoInn, new(-76.00525f, 19.058472f, -161.18109f) }, + { EAetheryteLocation.KuganeRubyBazaar, new(132.40247f, 12.954895f, 83.02429f) }, + { EAetheryteLocation.KuganeSekiseigumiBarracks, new(119.09656f, 13.01593f, -92.881714f) }, + { EAetheryteLocation.KuganeRakuzaDistrict, new(24.64331f, 7.003784f, -152.97174f) }, + + { EAetheryteLocation.FringesCastrumOriens, new(-629.11426f, 132.89075f, -509.14783f) }, + { EAetheryteLocation.FringesPeeringStones, new(415.3047f, 117.357056f, 246.75354f) }, + { EAetheryteLocation.PeaksAlaGannha, new(114.579956f, 120.10376f, -747.06647f) }, + { EAetheryteLocation.PeaksAlaGhiri, new(-271.3817f, 259.87634f, 748.86694f) }, + { EAetheryteLocation.LochsPortaPraetoria, new(-652.0333f, 53.391357f, -16.006714f) }, + { EAetheryteLocation.LochsAlaMhiganQuarter, new(612.4512f, 84.45862f, 656.82446f) }, + { EAetheryteLocation.RubySeaTamamizu, new(358.72437f, -118.05908f, -263.4165f) }, + { EAetheryteLocation.RubySeaOnokoro, new(88.181885f, 4.135132f, -583.3677f) }, + { EAetheryteLocation.YanxiaNamai, new(432.66956f, 73.07532f, -90.74542f) }, + { EAetheryteLocation.YanxiaHouseOfTheFierce, new(246.02112f, 9.079041f, -401.3581f) }, + { EAetheryteLocation.AzimSteppeReunion, new(556.1454f, -16.800232f, 340.10828f) }, + { EAetheryteLocation.AzimSteppeDawnThrone, new(78.26355f, 119.37134f, 36.301147f) }, + { EAetheryteLocation.AzimSteppeDhoroIloh, new(-754.63495f, 131.2428f, 116.5636f) }, + + { EAetheryteLocation.DomanEnclave, new(42.648926f, 1.4190674f, -14.8776245f) }, + { EAetheryteLocation.DomanEnclaveNorthern, new(8.987488f, 0.8086548f, -105.85187f) }, + { EAetheryteLocation.DomanEnclaveSouthern, new(-61.57019f, 0.77819824f, 90.684326f) }, + { EAetheryteLocation.DomanEnclaveDocks, new(96.269165f, -3.4332886f, 81.01013f) }, { EAetheryteLocation.Crystarium, new(-65.0188f, 4.5318604f, 0.015197754f) }, { EAetheryteLocation.CrystariumMarkets, new(-6.149414f, -7.736328f, 148.72961f) }, @@ -57,7 +101,21 @@ internal sealed class AetheryteData { EAetheryteLocation.EulmoreGloryGate, new(6.9122925f, 6.240906f, -56.351562f) }, { EAetheryteLocation.EulmoreSoutheastDerelict, new(71.82422f, -10.391418f, 65.32385f) }, - // ... missing a few + { EAetheryteLocation.LakelandFortJobb, new(753.7803f, 24.338135f, -28.82434f) }, + { EAetheryteLocation.LakelandOstallImperative, new(-735.01184f, 53.391357f, -230.02979f) }, + { EAetheryteLocation.KholusiaStilltide, new(668.32983f, 29.465088f, 289.17358f) }, + { EAetheryteLocation.KholusiaWright, new(-244.00702f, 20.736938f, 385.45813f) }, + { EAetheryteLocation.KholusiaTomra, new(-426.38287f, 419.27222f, -623.5294f) }, + { EAetheryteLocation.AmhAraengMordSouq, new(246.38745f, 12.985352f, -220.29456f) }, + { EAetheryteLocation.AmhAraengInnAtJourneysHead, new(399.0996f, -24.521301f, 307.97278f) }, + { EAetheryteLocation.AmhAraengTwine, new(-511.3451f, 47.989624f, -212.604f) }, + { EAetheryteLocation.RaktikaSlitherbough, new(-103.4104f, -19.333252f, 297.23047f) }, + { EAetheryteLocation.RaktikaFanow, new(382.77246f, 21.042175f, -194.11005f) }, + { EAetheryteLocation.IlMhegLydhaLran, new(-344.71655f, 48.722046f, 512.2606f) }, + { EAetheryteLocation.IlMhegPiaEnni, new(-72.55664f, 103.95972f, -857.35864f) }, + { EAetheryteLocation.IlMhegWolekdorf, new(380.51416f, 87.20532f, -687.2511f) }, + { EAetheryteLocation.TempestOndoCups, new(561.76074f, 352.62073f, -199.17603f) }, + { EAetheryteLocation.TempestMacarensesAngle, new(-141.74109f, -280.5371f, 218.00562f) }, { EAetheryteLocation.OldSharlayan, new(0.07623291f, 4.8065186f, -0.10687256f) }, { EAetheryteLocation.OldSharlayanStudium, new(-291.1574f, 20.004517f, -74.143616f) }, diff --git a/Questionable/External/LifestreamIpc.cs b/Questionable/External/LifestreamIpc.cs new file mode 100644 index 000000000..2ba2a35b9 --- /dev/null +++ b/Questionable/External/LifestreamIpc.cs @@ -0,0 +1,26 @@ +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; +using Questionable.Data; +using Questionable.Model.V1; + +namespace Questionable.External; + +internal sealed class LifestreamIpc +{ + private readonly AetheryteData _aetheryteData; + private readonly ICallGateSubscriber _aethernetTeleport; + + public LifestreamIpc(DalamudPluginInterface pluginInterface, AetheryteData aetheryteData) + { + _aetheryteData = aetheryteData; + _aethernetTeleport = pluginInterface.GetIpcSubscriber("Lifestream.AethernetTeleport"); + } + + public bool Teleport(EAetheryteLocation aetheryteLocation) + { + if (!_aetheryteData.AethernetNames.TryGetValue(aetheryteLocation, out string? name)) + return false; + + return _aethernetTeleport.InvokeFunc(name); + } +} diff --git a/Questionable/GameFunctions.cs b/Questionable/GameFunctions.cs index 652ff60ac..180aa66d1 100644 --- a/Questionable/GameFunctions.cs +++ b/Questionable/GameFunctions.cs @@ -7,10 +7,13 @@ using System.Numerics; using System.Runtime.InteropServices; using System.Text; using Dalamud.Game; +using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Control; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.System.Memory; @@ -18,6 +21,8 @@ using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using Lumina.Excel.GeneratedSheets; using Questionable.Model.V1; +using BattleChara = FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara; +using GameObject = Dalamud.Game.ClientState.Objects.Types.GameObject; namespace Questionable; @@ -34,15 +39,19 @@ internal sealed unsafe class GameFunctions private readonly ProcessChatBoxDelegate _processChatBox; private readonly delegate* unmanaged _sanitiseString; private readonly ReadOnlyDictionary _territoryToAetherCurrentCompFlgSet; + private readonly ReadOnlyDictionary _emoteCommands; private readonly IObjectTable _objectTable; private readonly ITargetManager _targetManager; + private readonly ICondition _condition; private readonly IPluginLog _pluginLog; - public GameFunctions(IDataManager dataManager, IObjectTable objectTable, ISigScanner sigScanner, ITargetManager targetManager, IPluginLog pluginLog) + public GameFunctions(IDataManager dataManager, IObjectTable objectTable, ISigScanner sigScanner, + ITargetManager targetManager, ICondition condition, IPluginLog pluginLog) { _objectTable = objectTable; _targetManager = targetManager; + _condition = condition; _pluginLog = pluginLog; _processChatBox = Marshal.GetDelegateForFunctionPointer(sigScanner.ScanText(Signatures.SendChat)); @@ -54,6 +63,13 @@ internal sealed unsafe class GameFunctions .Where(x => x.Unknown32 > 0) .ToDictionary(x => (ushort)x.RowId, x => x.Unknown32) .AsReadOnly(); + _emoteCommands = dataManager.GetExcelSheet()! + .Where(x => x.RowId > 0) + .Where(x => x.TextCommand != null && x.TextCommand.Value != null) + .Select(x => (x.RowId, Command: x.TextCommand.Value!.Command?.ToString())) + .Where(x => x.Command != null && x.Command.StartsWith('/')) + .ToDictionary(x => (EEmote)x.RowId, x => x.Command!) + .AsReadOnly(); } public (ushort CurrentQuest, byte Sequence) GetCurrentQuest() @@ -92,7 +108,7 @@ internal sealed unsafe class GameFunctions return false; } - for (ulong i = 0; i < telepo->TeleportList.Size(); ++ i) + for (ulong i = 0; i < telepo->TeleportList.Size(); ++i) { var data = telepo->TeleportList.Get(i); if (data.AetheryteId == aetheryteId) @@ -134,6 +150,13 @@ internal sealed unsafe class GameFunctions playerState->IsAetherCurrentZoneComplete(aetherCurrentCompFlgSet); } + public bool IsAetherCurrentUnlocked(uint aetherCurrentId) + { + var playerState = PlayerState.Instance(); + return playerState != null && + playerState->IsAetherCurrentUnlocked(aetherCurrentId); + } + public void ExecuteCommand(string command) { if (!command.StartsWith('/')) @@ -262,19 +285,81 @@ internal sealed unsafe class GameFunctions #endregion - public void InteractWith(uint dataId) + private GameObject? FindObjectByDataId(uint dataId) { foreach (var gameObject in _objectTable) { if (gameObject.DataId == dataId) { - _targetManager.Target = null; - _targetManager.Target = gameObject; - - TargetSystem.Instance()->InteractWithObject( - (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address, false); - return; + return gameObject; } } + + return null; + } + + public void InteractWith(uint dataId) + { + GameObject? gameObject = FindObjectByDataId(dataId); + if (gameObject != null) + { + _targetManager.Target = null; + _targetManager.Target = gameObject; + + TargetSystem.Instance()->InteractWithObject( + (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)gameObject.Address, false); + } + } + + public void UseItem(uint dataId, uint itemId) + { + GameObject? gameObject = FindObjectByDataId(dataId); + if (gameObject != null) + { + _targetManager.Target = gameObject; + AgentInventoryContext.Instance()->UseItem(itemId); + } + } + + public void UseEmote(uint dataId, EEmote emote) + { + GameObject? gameObject = FindObjectByDataId(dataId); + if (gameObject != null) + { + _targetManager.Target = gameObject; + ExecuteCommand($"{_emoteCommands[emote]} motion"); + } + } + + public bool IsObbjectAtPosition(uint dataId, Vector3 position) + { + GameObject? gameObject = FindObjectByDataId(dataId); + return gameObject != null && (gameObject.Position - position).Length() < 0.05f; + } + + public bool HasStatusPreventingSprintOrMount() + { + var gameObject = GameObjectManager.GetGameObjectByIndex(0); + if (gameObject != null && gameObject->ObjectKind == 1) + { + var battleChara = (BattleChara*)gameObject; + StatusManager* statusManager = battleChara->GetStatusManager; + return statusManager->HasStatus(565); + } + + return false; + } + + public bool Unmount() + { + if (_condition[ConditionFlag.Mounted]) + { + if (ActionManager.Instance()->GetActionStatus(ActionType.GeneralAction, 23) == 0) + ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23); + + return true; + } + + return false; } } diff --git a/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs b/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs index 0fb5affeb..48892f003 100644 --- a/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs +++ b/Questionable/Model/V1/Converter/AethernetShortcutConverter.cs @@ -20,7 +20,7 @@ public sealed class AethernetShortcutConverter : JsonConverter +public sealed class AetheryteConverter : JsonConverter { private static readonly Dictionary EnumToString = new() { @@ -14,6 +14,7 @@ public class AetheryteConverter : JsonConverter { EAetheryteLocation.Gridania, "Gridania" }, { EAetheryteLocation.Uldah, "Ul'dah" }, { EAetheryteLocation.Ishgard, "Ishgard" }, + { EAetheryteLocation.Idyllshire, "Idyllshire" }, { EAetheryteLocation.RhalgrsReach, "Rhalgr's Reach" }, { EAetheryteLocation.FringesCastrumOriens, "Fringes - Castrum Oriens" }, @@ -31,8 +32,6 @@ public class AetheryteConverter : JsonConverter { EAetheryteLocation.AzimSteppeDawnThrone, "Azim Steppe - Dawn Throne" }, { EAetheryteLocation.AzimSteppeDhoroIloh, "Azim Steppe - Dhoro Iloh" }, { EAetheryteLocation.DomanEnclave, "Doman Enclave" }, - { EAetheryteLocation.DomamEnclaveNorthern, "Doman Enclave - Northern Enclave" }, - { EAetheryteLocation.DomamEnclaveSouthern, "Doman Enclave - Southern Enclave" }, { EAetheryteLocation.Crystarium, "Crystarium" }, { EAetheryteLocation.Eulmore, "Eulmore" }, diff --git a/Questionable/Model/V1/Converter/EmoteConverter.cs b/Questionable/Model/V1/Converter/EmoteConverter.cs new file mode 100644 index 000000000..ce3010493 --- /dev/null +++ b/Questionable/Model/V1/Converter/EmoteConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Questionable.Model.V1.Converter; + +public class EmoteConverter : JsonConverter +{ + private static readonly Dictionary EnumToString = new() + { + { EEmote.Stretch, "stretch" }, + { EEmote.Wave, "wave" }, + { EEmote.Rally, "rally" }, + { EEmote.Deny, "deny" }, + }; + + private static readonly Dictionary StringToEnum = + EnumToString.ToDictionary(x => x.Value, x => x.Key); + + public override EEmote Read(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + throw new JsonException(); + + string? str = reader.GetString(); + if (str == null) + throw new JsonException(); + + return StringToEnum.TryGetValue(str, out EEmote value) ? value : throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, EEmote value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + writer.WriteStringValue(EnumToString[value]); + } +} diff --git a/Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs b/Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs new file mode 100644 index 000000000..29578b685 --- /dev/null +++ b/Questionable/Model/V1/Converter/EnemySpawnTypeConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Questionable.Model.V1.Converter; + +public class EnemySpawnTypeConverter : JsonConverter +{ + private static readonly Dictionary EnumToString = new() + { + { EEnemySpawnType.AfterInteraction, "AfterInteraction" }, + { EEnemySpawnType.AutoOnEnterArea, "AutoOnEnterArea" }, + }; + + private static readonly Dictionary StringToEnum = + EnumToString.ToDictionary(x => x.Value, x => x.Key); + + public override EEnemySpawnType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + throw new JsonException(); + + string? str = reader.GetString(); + if (str == null) + throw new JsonException(); + + return StringToEnum.TryGetValue(str, out EEnemySpawnType value) ? value : throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, EEnemySpawnType value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + writer.WriteStringValue(EnumToString[value]); + } + +} diff --git a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs b/Questionable/Model/V1/Converter/InteractionTypeConverter.cs index bf4db1216..1bab6dc7a 100644 --- a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs +++ b/Questionable/Model/V1/Converter/InteractionTypeConverter.cs @@ -12,12 +12,13 @@ public sealed class InteractionTypeConverter : JsonConverter { { EInteractionType.Interact, "Interact" }, { EInteractionType.WalkTo, "WalkTo" }, - { EInteractionType.AttuneAethernetShard, "AttuneAethenetShard" }, + { EInteractionType.AttuneAethernetShard, "AttuneAethernetShard" }, { EInteractionType.AttuneAetheryte, "AttuneAetheryte" }, { EInteractionType.AttuneAetherCurrent, "AttuneAetherCurrent" }, { EInteractionType.Combat, "Combat" }, { EInteractionType.UseItem, "UseItem" }, { EInteractionType.Emote, "Emote" }, + { EInteractionType.WaitForObjectAtPosition, "WaitForNpcAtPosition" }, { EInteractionType.ManualAction, "ManualAction" } }; diff --git a/Questionable/Model/V1/EAetheryteLocation.cs b/Questionable/Model/V1/EAetheryteLocation.cs index bb62eb1ab..d7e2c6b4e 100644 --- a/Questionable/Model/V1/EAetheryteLocation.cs +++ b/Questionable/Model/V1/EAetheryteLocation.cs @@ -45,7 +45,7 @@ public enum EAetheryteLocation IshgardLastVigil = 87, Idyllshire = 75, - IdyllshireWest = 76, + IdyllshireWest = 90, RhalgrsReach = 104, RhalgrsReachWest = 121, @@ -77,8 +77,9 @@ public enum EAetheryteLocation AzimSteppeDhoroIloh = 128, DomanEnclave = 127, - DomamEnclaveNorthern = 129, - DomamEnclaveSouthern = 130, + DomanEnclaveNorthern = 129, + DomanEnclaveSouthern = 130, + DomanEnclaveDocks = 162, Crystarium = 133, CrystariumMarkets = 149, @@ -101,15 +102,15 @@ public enum EAetheryteLocation KholusiaWright = 138, KholusiaTomra = 139, AmhAraengMordSouq = 140, - AmhAraengInnAtJourneysHead = 141, - AmhAraengTwine = 142, - RaktikaSlitherbough = 143, - RaktikaFanow = 144, - IlMhegLydhaLran = 145, - IlMhegPiaEnni = 146, - IlMhegWolekdorf = 147, - TempestOndoCups = 148, - TempestMacarensesAngle = 156, + AmhAraengInnAtJourneysHead = 161, + AmhAraengTwine = 141, + RaktikaSlitherbough = 142, + RaktikaFanow = 143, + IlMhegLydhaLran = 144, + IlMhegPiaEnni = 145, + IlMhegWolekdorf = 146, + TempestOndoCups = 147, + TempestMacarensesAngle = 148, OldSharlayan = 182, OldSharlayanStudium = 184, diff --git a/Questionable/Model/V1/EEmote.cs b/Questionable/Model/V1/EEmote.cs new file mode 100644 index 000000000..0d8f4a002 --- /dev/null +++ b/Questionable/Model/V1/EEmote.cs @@ -0,0 +1,11 @@ +namespace Questionable.Model.V1; + +public enum EEmote +{ + None = 0, + + Stretch = 37, + Wave = 16, + Rally = 34, + Deny = 25, +} diff --git a/Questionable/Model/V1/EEnemySpawnType.cs b/Questionable/Model/V1/EEnemySpawnType.cs new file mode 100644 index 000000000..c2c968d5d --- /dev/null +++ b/Questionable/Model/V1/EEnemySpawnType.cs @@ -0,0 +1,8 @@ +namespace Questionable.Model.V1; + +public enum EEnemySpawnType +{ + None = 0, + AfterInteraction, + AutoOnEnterArea, +} diff --git a/Questionable/Model/V1/EInteractionType.cs b/Questionable/Model/V1/EInteractionType.cs index 2684f6d5a..9e9311b11 100644 --- a/Questionable/Model/V1/EInteractionType.cs +++ b/Questionable/Model/V1/EInteractionType.cs @@ -10,5 +10,6 @@ public enum EInteractionType Combat, UseItem, Emote, + WaitForObjectAtPosition, ManualAction } diff --git a/Questionable/Model/V1/QuestStep.cs b/Questionable/Model/V1/QuestStep.cs index e573c6a33..4b8f6a70d 100644 --- a/Questionable/Model/V1/QuestStep.cs +++ b/Questionable/Model/V1/QuestStep.cs @@ -18,6 +18,8 @@ public class QuestStep public float? StopDistance { get; set; } public ushort TerritoryId { get; set; } public bool Disabled { get; set; } + public bool DisableNavmesh { get; set; } + public bool? Mount { get; set; } public string? Comment { get; set; } [JsonConverter(typeof(AetheryteConverter))] @@ -25,4 +27,15 @@ public class QuestStep [JsonConverter(typeof(AethernetShortcutConverter))] public AethernetShortcut? AethernetShortcut { get; set; } + public uint? AetherCurrentId { get; set; } + + public uint? ItemId { get; set; } + + [JsonConverter(typeof(EmoteConverter))] + public EEmote? Emote { get; set; } + + [JsonConverter(typeof(EnemySpawnTypeConverter))] + public EEnemySpawnType? EnemySpawnType { get; set; } + + public IList? KillEnemyDataIds { get; set; } } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4358_Old Sharlayan New to You.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4358_Old Sharlayan New to You.json index 118e6d8e7..81b52c87c 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4358_Old Sharlayan New to You.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4358_Old Sharlayan New to You.json @@ -58,7 +58,7 @@ "Z": 127.753 }, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1038578, @@ -76,6 +76,15 @@ { "Sequence": 4, "Steps": [ + { + "Position": { + "X": -8.38828, + "Y": 3.2249968, + "Z": 9.224017 + }, + "TerritoryId": 962, + "InteractionType": "WalkTo" + }, { "DataId": 1038578, "Position": { @@ -127,6 +136,15 @@ "TerritoryId": 962, "InteractionType": "WalkTo" }, + { + "Position": { + "X": 96.67595, + "Y": 15.025446, + "Z": -134.08261 + }, + "TerritoryId": 962, + "InteractionType": "WalkTo" + }, { "DataId": 1038578, "Position": { @@ -150,9 +168,8 @@ "Y": 41.367188, "Z": -156.6034 }, - "StopDistance": 0.25, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1038578, @@ -161,6 +178,7 @@ "Y": 18.800978, "Z": -142.65858 }, + "StopDistance": 0.25, "TerritoryId": 962, "InteractionType": "Interact" } @@ -177,7 +195,7 @@ "Z": -118.73047 }, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 188, @@ -187,7 +205,7 @@ "Z": 13.77887 }, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1038578, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4359_Hitting the Books.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4359_Hitting the Books.json index 94715073d..355fc511c 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4359_Hitting the Books.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4359_Hitting the Books.json @@ -38,7 +38,7 @@ "Z": 29.709229 }, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 184, @@ -48,7 +48,7 @@ "Z": -74.143616 }, "TerritoryId": 962, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1038675, @@ -167,6 +167,7 @@ "Y": 19.003881, "Z": 13.321045 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4360_A Seat at the Last Stand.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4360_A Seat at the Last Stand.json index b26408cfd..00251ec3d 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4360_A Seat at the Last Stand.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4360_A Seat at the Last Stand.json @@ -12,6 +12,7 @@ "Y": 19.003874, "Z": 18.966919 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4362_Glorified Ratcatcher.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4362_Glorified Ratcatcher.json index ed2fc020e..6c73776c3 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4362_Glorified Ratcatcher.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4362_Glorified Ratcatcher.json @@ -42,6 +42,7 @@ "Y": 186.03699, "Z": -740.9324 }, + "StopDistance": 1, "TerritoryId": 956, "InteractionType": "Combat", "EnemySpawnType": "AutoOnEnterArea", @@ -77,6 +78,7 @@ "Z": -595.69696 }, "TerritoryId": 956, + "StopDistance": 1, "InteractionType": "Combat", "EnemySpawnType": "AutoOnEnterArea", "KillEnemyDataIds": [ @@ -97,13 +99,23 @@ "Z": -595.69696 }, "TerritoryId": 956, - "InteractionType": "UseItem" + "InteractionType": "UseItem", + "ItemId": 2003129 } ] }, { "Sequence": 6, "Steps": [ + { + "Position": { + "X": 222.61905, + "Y": 182.78828, + "Z": -704.0299 + }, + "TerritoryId": 956, + "InteractionType": "WalkTo" + }, { "DataId": 2011980, "Position": { @@ -112,7 +124,8 @@ "Z": -767.7577 }, "TerritoryId": 956, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818314 }, { "DataId": 1038701, @@ -122,7 +135,8 @@ "Z": -823.3921 }, "TerritoryId": 956, - "InteractionType": "UseItem" + "InteractionType": "UseItem", + "ItemId": 2003129 } ] }, @@ -144,6 +158,15 @@ { "Sequence": 255, "Steps": [ + { + "Position": { + "X": 254.80028, + "Y": 163.44171, + "Z": -626.4951 + }, + "TerritoryId": 956, + "InteractionType": "WalkTo" + }, { "DataId": 1038702, "Position": { diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4364_The Medial Circuit.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4364_The Medial Circuit.json index 50932ec44..c372782fe 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4364_The Medial Circuit.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4364_The Medial Circuit.json @@ -28,7 +28,17 @@ "Z": 66.75818 }, "TerritoryId": 956, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818315 + }, + { + "Position": { + "X": 760.1999, + "Y": 145.74788, + "Z": -52.025288 + }, + "TerritoryId": 956, + "InteractionType": "WalkTo" }, { "DataId": 2011840, @@ -83,6 +93,16 @@ { "Sequence": 4, "Steps": [ + { + "Position": { + "X": 483.16574, + "Y": 83.132675, + "Z": 74.693695 + }, + "TerritoryId": 956, + "InteractionType": "WalkTo", + "Comment": "Avoids aggroing some enemies on the hill" + }, { "DataId": 2011842, "Position": { @@ -141,7 +161,8 @@ "Z": -267.23126 }, "TerritoryId": 956, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818320 }, { "DataId": 2011843, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4365_The Full Reports Warts and All.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4365_The Full Reports Warts and All.json index e525de45f..b08a2e313 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4365_The Full Reports Warts and All.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4365_The Full Reports Warts and All.json @@ -20,6 +20,17 @@ { "Sequence": 1, "Steps": [ + { + "Position": { + "X": 304.306, + "Y": 84.01365, + "Z": -292.01114 + }, + "TerritoryId": 956, + "InteractionType": "WalkTo", + "DisableNavmesh": true, + "Mount": true + }, { "DataId": 1038736, "Position": { diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4366_A Guide of Sorts.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4366_A Guide of Sorts.json index 350b87f8c..d15b6634d 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4366_A Guide of Sorts.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4366_A Guide of Sorts.json @@ -46,16 +46,19 @@ "Z": -395.31555 }, "TerritoryId": 956, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818318 }, { "Position": { - "X": -327.6718, - "Y": 79.535736, - "Z": -400.00397 + "X": -300.80545, + "Y": 59.384476, + "Z": -409.0928 }, "TerritoryId": 956, - "InteractionType": "WalkTo" + "InteractionType": "WalkTo", + "DisableNavmesh": true, + "Mount": true }, { "DataId": 2011983, @@ -65,7 +68,8 @@ "Z": -286.27454 }, "TerritoryId": 956, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818319 }, { "DataId": 1038736, @@ -114,6 +118,7 @@ "Y": 41.37599, "Z": -141.16125 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4367_Estate Visitor.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4367_Estate Visitor.json index fa9a8b2ff..2884f8a8a 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4367_Estate Visitor.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4367_Estate Visitor.json @@ -12,6 +12,7 @@ "Y": 41.37599, "Z": -142.1684 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } @@ -27,6 +28,7 @@ "Y": 41.37599, "Z": -141.00867 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } @@ -42,6 +44,7 @@ "Y": 41.37599, "Z": -142.1684 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact" } @@ -92,7 +95,11 @@ "Z": 4.5318604 }, "TerritoryId": 962, - "InteractionType": "Interact" + "InteractionType": "Interact", + "AethernetShortcut": [ + "[Old Sharlayan] The Leveilleur Estate", + "[Old Sharlayan] The Baldesion Annex" + ] }, { "DataId": 1040291, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4368_For Thavnair Bound.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4368_For Thavnair Bound.json index 8c0164402..eaaa25c96 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4368_For Thavnair Bound.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4368_For Thavnair Bound.json @@ -83,7 +83,8 @@ "Z": 633.93604 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "DisableNavmesh": true } ] }, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json index 851409398..9301bbcf4 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json @@ -83,6 +83,16 @@ { "Sequence": 5, "Steps": [ + { + "Position": { + "X": 232.93636, + "Y": 15.136732, + "Z": 526.6279 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "Mount": true + }, { "DataId": 2011992, "Position": { @@ -91,7 +101,9 @@ "Z": 473.65527 }, "TerritoryId": 957, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818333, + "DisableNavmesh": true }, { "Position": { @@ -102,6 +114,16 @@ "TerritoryId": 957, "InteractionType": "WalkTo" }, + { + "Position": { + "X": 199.50157, + "Y": 1.769943, + "Z": 738.831 + }, + "StopDistance": 0.25, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, { "DataId": 1038608, "Position": { @@ -109,9 +131,9 @@ "Y": 1.769943, "Z": 738.9843 }, - "StopDistance": 0.25, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "Mount": false } ] }, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4371_House of Divinities.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4371_House of Divinities.json index f0ec8bbd0..8ef23c2e8 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4371_House of Divinities.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4371_House of Divinities.json @@ -12,6 +12,7 @@ "Y": 5.880045, "Z": 612.02405 }, + "StopDistance": 5, "TerritoryId": 957, "InteractionType": "Interact" } @@ -92,7 +93,8 @@ "Z": 537.8346 }, "TerritoryId": 957, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818329 }, { "DataId": 1038616, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4372_The Great Work.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4372_The Great Work.json index cf5170b38..ad1ec68aa 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4372_The Great Work.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4372_The Great Work.json @@ -1,6 +1,7 @@ { "Version": 1, "Author": "liza", + "Comment": "This is possibly the least polished quest so far, as the follow steps are awkward (need to move >10 units away to trigger the follow-up)", "QuestSequence": [ { "Sequence": 0, @@ -83,23 +84,184 @@ "Y": 49.103825, "Z": 152.91064 }, + "StopDistance": 5, "TerritoryId": 957, - "InteractionType": "ManualAction", - "Comment": "Follow the Lantern" + "InteractionType": "Interact" + }, + { + "DataId": 1038636, + "Position": { + "X": -425.414, + "Y": 38.415024, + "Z": 160.1206 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WaitForNpcAtPosition" + }, + { + "Position": { + "X": -425.43683, + "Y": 38.413155, + "Z": 160.11292 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, + { + "DataId": 1038636, + "Position": { + "X": -425.414, + "Y": 38.415024, + "Z": 160.1206 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WalkTo" } ] }, { "Sequence": 5, - "Comment": "Follow the Lantern" + "Steps": [ + { + "DataId": 1041219, + "Position": { + "X": -425.43683, + "Y": 38.413155, + "Z": 160.11292 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Interact" + }, + { + "DataId": 1041219, + "Position": { + "X": -419.6078, + "Y": 43.950115, + "Z": 87.3025 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WaitForNpcAtPosition" + }, + { + "Position": { + "X": -430.35034, + "Y": 46.160213, + "Z": 93.2392 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, + { + "DataId": 1041220, + "Position": { + "X": -419.60785, + "Y": 43.9499, + "Z": 87.296875 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WalkTo" + } + ] }, { "Sequence": 6, - "Comment": "Follow the Lantern" + "Steps": [ + { + "DataId": 1041220, + "Position": { + "X": -419.60785, + "Y": 43.9499, + "Z": 87.296875 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Interact" + }, + { + "DataId": 1041220, + "Position": { + "X": -444.7032, + "Y": 49.551, + "Z": 97.1969 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WaitForNpcAtPosition" + }, + { + "Position": { + "X": -433.19608, + "Y": 46.94587, + "Z": 93.295135 + }, + "StopDistance": 0.35, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, + { + "DataId": 1041221, + "Position": { + "X": -444.72424, + "Y": 49.55681, + "Z": 97.18469 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WalkTo" + } + ] }, { "Sequence": 7, - "Comment": "Follow the Lantern" + "Steps": [ + { + "DataId": 1041221, + "Position": { + "X": -444.72424, + "Y": 49.55681, + "Z": 97.18469 + }, + "StopDistance": 5, + "TerritoryId": 957, + "InteractionType": "Interact" + }, + { + "DataId": 1041221, + "Position": { + "X": -484.5909, + "Y": 54.18516, + "Z": 128.3601 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WaitForNpcAtPosition" + }, + { + "Position": { + "X": -472.65085, + "Y": 53.779083, + "Z": 124.34451 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, + { + "DataId": 1038637, + "Position": { + "X": -484.61133, + "Y": 54.187614, + "Z": 128.34363 + }, + "StopDistance": 4, + "TerritoryId": 957, + "InteractionType": "WalkTo" + } + ] }, { "Sequence": 8, @@ -111,6 +273,7 @@ "Y": 54.187614, "Z": 128.34363 }, + "StopDistance": 5, "TerritoryId": 957, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4373_Shadowed Footsteps.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4373_Shadowed Footsteps.json index f5af5151f..135528e62 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4373_Shadowed Footsteps.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4373_Shadowed Footsteps.json @@ -107,7 +107,8 @@ "Z": 117.69275 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Great Work" } ] } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4375_Tipping the Scale.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4375_Tipping the Scale.json index aa5f5bb5f..8d3327d79 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4375_Tipping the Scale.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4375_Tipping the Scale.json @@ -22,18 +22,18 @@ "Steps": [ { "Position": { - "X": -80.636894, - "Y": 99.974266, - "Z": -708.7214 + "X": -82.02301, + "Y": 95.24942, + "Z": -697.3925 }, "TerritoryId": 957, "InteractionType": "WalkTo" }, { "Position": { - "X": -67.775665, - "Y": 97.140656, - "Z": -710.1025 + "X": -78.47051, + "Y": 99.96379, + "Z": -711.17303 }, "TerritoryId": 957, "InteractionType": "WalkTo" @@ -46,7 +46,9 @@ "Z": -710.7805 }, "TerritoryId": 957, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818330, + "DisableNavmesh": true }, { "DataId": 1038631, @@ -56,7 +58,8 @@ "Z": 117.69275 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Great Work" } ] }, @@ -71,7 +74,28 @@ "Z": -561.82196 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818334 + }, + { + "Position": { + "X": -489.27457, + "Y": 72.74904, + "Z": -546.8438 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "Mount": true + }, + { + "Position": { + "X": -523.7225, + "Y": 9.401685, + "Z": -554.4276 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "DisableNavmesh": true }, { "DataId": 1038651, @@ -95,6 +119,7 @@ "Y": -0.090369135, "Z": -562.4323 }, + "StopDistance": 6, "TerritoryId": 957, "InteractionType": "Interact" } @@ -111,7 +136,8 @@ "Z": 17.532532 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "AetheryteShortcut": "Thavnair - Great Work" } ] } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4376_The Satrap of Radz at Han.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4376_The Satrap of Radz at Han.json index eebb5a89c..7c362e72b 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4376_The Satrap of Radz at Han.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4376_The Satrap of Radz at Han.json @@ -20,6 +20,15 @@ { "Sequence": 1, "Steps": [ + { + "Position": { + "X": -130.78743, + "Y": 86.83725, + "Z": -252.08578 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, { "DataId": 2011994, "Position": { @@ -28,7 +37,28 @@ "Z": -288.3192 }, "TerritoryId": 957, - "InteractionType": "AttuneAetherCurrent" + "InteractionType": "AttuneAetherCurrent", + "AetherCurrentId": 2818335, + "DisableNavmesh": true + }, + { + "Position": { + "X": -156.25183, + "Y": 90.34184, + "Z": -399.8714 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo" + }, + { + "Position": { + "X": -126.149765, + "Y": 73.745605, + "Z": -427.64508 + }, + "TerritoryId": 957, + "InteractionType": "WalkTo", + "DisableNavmesh": true }, { "DataId": 1038656, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4377_In the Dark of the Tower.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4377_In the Dark of the Tower.json index 788e1b7f6..843ea9bec 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4377_In the Dark of the Tower.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4377_In the Dark of the Tower.json @@ -12,6 +12,7 @@ "Y": 1.9073486E-06, "Z": 1.5715942 }, + "StopDistance": 5, "TerritoryId": 987, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4378_The Jewel of Thavnair.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4378_The Jewel of Thavnair.json index 00ec3b59b..b7f92b58b 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4378_The Jewel of Thavnair.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4378_The Jewel of Thavnair.json @@ -42,6 +42,7 @@ "Y": -27.000013, "Z": 177.5387 }, + "StopDistance": 5, "TerritoryId": 963, "InteractionType": "Interact" } @@ -58,7 +59,7 @@ "Z": 110.55151 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1040256, @@ -113,7 +114,7 @@ "Z": 202.2583 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 191, @@ -123,7 +124,7 @@ "Z": -31.815125 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1040259, diff --git a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4379_The Color of Joy.json b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4379_The Color of Joy.json index d07aa57bf..d39b9f2aa 100644 --- a/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4379_The Color of Joy.json +++ b/Questionable/QuestPaths/Endwalker-A-Thavnair1-Labyrinthos1/4379_The Color of Joy.json @@ -43,7 +43,7 @@ "Z": 27.725586 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1040264, @@ -98,7 +98,12 @@ "Z": 1.3884888 }, "TerritoryId": 962, - "InteractionType": "Interact" + "InteractionType": "Interact", + "AetheryteShortcut": "Old Sharlayan", + "AethernetShortcut": [ + "[Old Sharlayan] Aetheryte Plaza", + "[Old Sharlayan] The Baldesion Annex" + ] } ] }, @@ -112,6 +117,7 @@ "Y": 4.357494, "Z": 0.7476196 }, + "StopDistance": 6, "TerritoryId": 962, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker-C-MareLamentorum/4402_A Taste of the Moon.json b/Questionable/QuestPaths/Endwalker-C-MareLamentorum/4402_A Taste of the Moon.json index 78b3ff93a..edb355106 100644 --- a/Questionable/QuestPaths/Endwalker-C-MareLamentorum/4402_A Taste of the Moon.json +++ b/Questionable/QuestPaths/Endwalker-C-MareLamentorum/4402_A Taste of the Moon.json @@ -72,7 +72,7 @@ "InteractionType": "Interact" }, { - "ItemId": -1, + "ItemId": null, "TerritoryId": 959, "InteractionType": "UseItem" } diff --git a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4410_The Blasphemy Unmasked.json b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4410_The Blasphemy Unmasked.json index 9955b19ca..87cc42bc8 100644 --- a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4410_The Blasphemy Unmasked.json +++ b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4410_The Blasphemy Unmasked.json @@ -111,7 +111,7 @@ "Z": -197.61963 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1039001, diff --git a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4411_Amidst the Apocalypse.json b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4411_Amidst the Apocalypse.json index 8d420d253..95816917e 100644 --- a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4411_Amidst the Apocalypse.json +++ b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4411_Amidst the Apocalypse.json @@ -57,7 +57,7 @@ "Z": 13.473633 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1039540, diff --git a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4418_At Worlds End.json b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4418_At Worlds End.json index 66ea9fcea..fc480a1e4 100644 --- a/Questionable/QuestPaths/Endwalker-D-Thavnair2/4418_At Worlds End.json +++ b/Questionable/QuestPaths/Endwalker-D-Thavnair2/4418_At Worlds End.json @@ -53,7 +53,7 @@ "Z": -98.43509 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard" + "InteractionType": "AttuneAethernetShard" }, { "DataId": 1040374, diff --git a/Questionable/QuestPaths/Endwalker-H-6.1/4531_Sharing the Wealth.json b/Questionable/QuestPaths/Endwalker-H-6.1/4531_Sharing the Wealth.json index 43f71acde..d965b66c7 100644 --- a/Questionable/QuestPaths/Endwalker-H-6.1/4531_Sharing the Wealth.json +++ b/Questionable/QuestPaths/Endwalker-H-6.1/4531_Sharing the Wealth.json @@ -28,7 +28,7 @@ "Z": -210.6151 }, "TerritoryId": 963, - "InteractionType": "AttuneAethenetShard", + "InteractionType": "AttuneAethernetShard", "Comment": "This is pretty late here, maybe move it to some other quest" }, { diff --git a/Questionable/QuestSchema/schema_v1.json b/Questionable/QuestSchema/schema_v1.json index d260a904b..99e736456 100644 --- a/Questionable/QuestSchema/schema_v1.json +++ b/Questionable/QuestSchema/schema_v1.json @@ -47,10 +47,12 @@ "properties": { "DataId": { "type": "integer", + "description": "The data id of the NPC/Object/Aetheryte/Aether Current", "exclusiveMinimum": 0 }, "Position": { "type": "object", + "description": "Position to (typically) walk to", "properties": { "X": { "type": "number" @@ -69,38 +71,53 @@ ] }, "StopDistance": { - "type": "number", + "type": ["number", "null"], + "description": "Set if pathfinding should stop closer or further away from the default stop distance", "exclusiveMinimum": 0 }, "TerritoryId": { "type": "integer", + "description": "The territory id associated with the location", "exclusiveMinimum": 0 }, "InteractionType": { "type": "string", + "description": "What to do at the position", "enum": [ "Interact", "WalkTo", - "AttuneAethenetShard", + "AttuneAethernetShard", "AttuneAetheryte", "AttuneAetherCurrent", "Combat", "UseItem", "Emote", + "WaitForNpcAtPosition", "ManualAction" ] }, "Disabled": { + "description": "Unused (TODO)", "type": "boolean" }, + "DisableNavmesh": { + "description": "If true, will go to the position in a straight line instead of using pathfinding", + "type": "boolean" + }, + "Mount": { + "type": ["boolean", "null"], + "description": "If true, will mount regardless of distance to position. If false, will unmount." + }, "AetheryteShortcut": { "type": "string", + "description": "The Aetheryte to teleport to (before moving)", "$comment": "TODO add remaining aetherytes for 2.x/3.x", "enum": [ "Limsa Lominsa", "Gridania", "Ul'dah", "Ishgard", + "Idyllshire", "Rhalgr's Reach", "Fringes - Castrum Oriens", @@ -118,8 +135,6 @@ "Azim Steppe - Dawn Throne", "Azim Steppe - Dhoro Iloh", "Doman Enclave", - "Doman Enclave - Northern Enclave", - "Doman Enclave - Southern Enclave", "Crystarium", "Eulmore", @@ -155,12 +170,13 @@ "Elpis - Twelve Wonders", "Elpis - Poieten Oikos", "Ultima Thule - Reah Tahra", - "Ultima Thula - Abode of the Ea", + "Ultima Thule - Abode of the Ea", "Ultima Thule - Base Omicron" ] }, "AethernetShortcut": { "type": "array", + "description": "A pair of aethernet locations (from + to) to use as a shortcut", "minItems": 2, "maxItems": 2, "items": { @@ -176,7 +192,7 @@ "[Limsa Lominsa] Airship Landing", "[Gridania] Aetheryte Plaza", "[Gridania] Archer's Guild", - "[Gridania] Leatherworker's Guld & Shaded Bower", + "[Gridania] Leatherworker's Guild & Shaded Bower", "[Gridania] Lancer's Guild", "[Gridania] Conjurer's Guild", "[Gridania] Botanist's Guild", @@ -249,14 +265,20 @@ ] } }, + "AetherCurrentId": { + "type": "number", + "description": "The aether current id, used to check if a given aetheryte is unlocked" + }, "EnemySpawnType": { "type": "string", + "description": "Determines how enemy spawning is handled in combat locations", "enum": [ "AutoOnEnterArea", "AfterInteraction" ] }, "KillEnemyDataIds": { + "description": "The enemy data ids which are supposed to be killed", "type": "array", "items": { "type": "integer" @@ -264,6 +286,7 @@ }, "Emote": { "type": "string", + "description": "The emote to use", "enum": [ "stretch", "wave", @@ -271,8 +294,14 @@ "deny" ] }, + "ItemId": { + "type": ["number", "null"], + "description": "The Item to use", + "exclusiveMinimum": 0 + }, "SkipIf": { "type": "array", + "description": "TODO Not implemented", "items": { "type": "string", "enum": [ diff --git a/Questionable/Questionable.cs b/Questionable/QuestionablePlugin.cs similarity index 80% rename from Questionable/Questionable.cs rename to Questionable/QuestionablePlugin.cs index 9a0531395..7f738f826 100644 --- a/Questionable/Questionable.cs +++ b/Questionable/QuestionablePlugin.cs @@ -7,12 +7,13 @@ using Dalamud.Plugin; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI; using Questionable.Controller; +using Questionable.Data; using Questionable.External; using Questionable.Windows; namespace Questionable; -public sealed class Questionable : IDalamudPlugin +public sealed class QuestionablePlugin : IDalamudPlugin { private readonly WindowSystem _windowSystem = new(nameof(Questionable)); @@ -25,9 +26,10 @@ public sealed class Questionable : IDalamudPlugin private readonly MovementController _movementController; - public Questionable(DalamudPluginInterface pluginInterface, IClientState clientState, ITargetManager targetManager, - IFramework framework, IGameGui gameGui, IDataManager dataManager, ISigScanner sigScanner, - IObjectTable objectTable, IPluginLog pluginLog, ICondition condition, IChatGui chatGui, ICommandManager commandManager) + public QuestionablePlugin(DalamudPluginInterface pluginInterface, IClientState clientState, + ITargetManager targetManager, IFramework framework, IGameGui gameGui, IDataManager dataManager, + ISigScanner sigScanner, IObjectTable objectTable, IPluginLog pluginLog, ICondition condition, IChatGui chatGui, + ICommandManager commandManager) { ArgumentNullException.ThrowIfNull(pluginInterface); ArgumentNullException.ThrowIfNull(sigScanner); @@ -38,13 +40,15 @@ public sealed class Questionable : IDalamudPlugin _clientState = clientState; _framework = framework; _gameGui = gameGui; - _gameFunctions = new GameFunctions(dataManager, objectTable, sigScanner, targetManager, pluginLog); + _gameFunctions = new GameFunctions(dataManager, objectTable, sigScanner, targetManager, condition, pluginLog); + AetheryteData aetheryteData = new AetheryteData(dataManager); NavmeshIpc navmeshIpc = new NavmeshIpc(pluginInterface); + LifestreamIpc lifestreamIpc = new LifestreamIpc(pluginInterface, aetheryteData); _movementController = - new MovementController(navmeshIpc, clientState, _gameFunctions, pluginLog); + new MovementController(navmeshIpc, clientState, _gameFunctions, condition, pluginLog); _questController = new QuestController(pluginInterface, dataManager, _clientState, _gameFunctions, - _movementController, pluginLog, condition, chatGui, commandManager); + _movementController, pluginLog, condition, chatGui, aetheryteData, lifestreamIpc); _windowSystem.AddWindow(new DebugWindow(_movementController, _questController, _gameFunctions, clientState, targetManager)); diff --git a/Questionable/Windows/DebugWindow.cs b/Questionable/Windows/DebugWindow.cs index 7e789f2a7..1f26255d0 100644 --- a/Questionable/Windows/DebugWindow.cs +++ b/Questionable/Windows/DebugWindow.cs @@ -35,7 +35,7 @@ internal sealed class DebugWindow : Window IsOpen = true; SizeConstraints = new WindowSizeConstraints { - MinimumSize = new Vector2(100, 0), + MinimumSize = new Vector2(200, 30), MaximumSize = default }; } @@ -43,16 +43,22 @@ internal sealed class DebugWindow : Window public override unsafe void Draw() { if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null) + { + ImGui.Text("Not logged in."); return; + } var currentQuest = _questController.CurrentQuest; if (currentQuest != null) { ImGui.TextUnformatted($"Quest: {currentQuest.Quest.Name} / {currentQuest.Sequence} / {currentQuest.Step}"); ImGui.TextUnformatted(_questController.DebugState ?? "--"); + ImGui.TextUnformatted(_questController.Comment ?? "--"); - ImGui.BeginDisabled(_questController.GetNextStep().Step == null); - ImGui.Text($"{_questController.GetNextStep().Step?.Position}"); + var nextStep = _questController.GetNextStep(); + ImGui.BeginDisabled(nextStep.Step == null); + ImGui.Text(string.Create(CultureInfo.InvariantCulture, + $"{nextStep.Step?.InteractionType} @ {nextStep.Step?.Position}")); if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { _questController.ExecuteNextStep(); @@ -163,5 +169,8 @@ internal sealed class DebugWindow : Window if (ImGui.Button("Stop Nav")) _movementController.Stop(); ImGui.EndDisabled(); + + if (ImGui.Button("Reload Data")) + _questController.Reload(); } }