diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4ac68e07 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "LLib"] + path = LLib + url = https://git.carvel.li/liza/LLib.git diff --git a/LLib b/LLib new file mode 160000 index 00000000..b5125d4b --- /dev/null +++ b/LLib @@ -0,0 +1 @@ +Subproject commit b5125d4b3f7cdc0c7514a01764e5b5d4d85f80a7 diff --git a/Questionable.sln b/Questionable.sln index a208fe5e..c14cc412 100644 --- a/Questionable.sln +++ b/Questionable.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable", "Questionable\Questionable.csproj", "{C91EEF13-A1AC-4A40-B695-DD4E378E5989}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.Build.0 = Debug|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.ActiveCfg = Release|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.Build.0 = Release|Any CPU + {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Questionable/Controller/MovementController.cs b/Questionable/Controller/MovementController.cs index 3d88a3f5..f4985002 100644 --- a/Questionable/Controller/MovementController.cs +++ b/Questionable/Controller/MovementController.cs @@ -66,7 +66,7 @@ internal sealed class MovementController : IDisposable } } else if (!Destination.IsFlying && !_condition[ConditionFlag.Mounted] && navPoints.Count > 0 && - !_gameFunctions.HasStatusPreventingSprintOrMount()) + !_gameFunctions.HasStatusPreventingSprintOrMount() && Destination.CanSprint) { float actualDistance = 0; foreach (Vector3 end in navPoints) @@ -118,10 +118,19 @@ internal sealed class MovementController : IDisposable { if (AetheryteConverter.IsLargeAetheryte((EAetheryteLocation)Destination.DataId)) { + /* + if ((EAetheryteLocation) Destination.DataId is EAetheryteLocation.OldSharlayan + or EAetheryteLocation.UltimaThuleAbodeOfTheEa) + Stop(); + // TODO verify the first part of this, is there any aetheryte like that? + // TODO Unsure if this is per-aetheryte or what; because e.g. old sharlayan is at -1.53; + // but Elpis aetherytes fail at around -0.95 if (localPlayerPosition.Y - gameObject.Position.Y < 2.95f && localPlayerPosition.Y - gameObject.Position.Y > -0.9f) Stop(); + */ + Stop(); } else { @@ -145,27 +154,27 @@ internal sealed class MovementController : IDisposable return pointOnFloor != null && Math.Abs(pointOnFloor.Value.Y - p.Y) > 0.5f; } - private void PrepareNavigation(EMovementType type, uint? dataId, Vector3 to, bool fly, float? stopDistance) + private void PrepareNavigation(EMovementType type, uint? dataId, Vector3 to, bool fly, bool sprint, float? stopDistance) { ResetPathfinding(); _gameFunctions.ExecuteCommand("/automove off"); - Destination = new DestinationData(dataId, to, stopDistance ?? (DefaultStopDistance - 0.2f), fly); + Destination = new DestinationData(dataId, to, stopDistance ?? (DefaultStopDistance - 0.2f), fly, sprint); } - public void NavigateTo(EMovementType type, uint? dataId, Vector3 to, bool fly, float? stopDistance = null) + public void NavigateTo(EMovementType type, uint? dataId, Vector3 to, bool fly, bool sprint, float? stopDistance = null) { - PrepareNavigation(type, dataId, to, fly, stopDistance); + PrepareNavigation(type, dataId, to, fly, sprint, stopDistance); _cancellationTokenSource = new(); _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10)); _pathfindTask = _navmeshIpc.Pathfind(_clientState.LocalPlayer!.Position, to, fly, _cancellationTokenSource.Token); } - public void NavigateTo(EMovementType type, uint? dataId, List to, bool fly, float? stopDistance) + public void NavigateTo(EMovementType type, uint? dataId, List to, bool fly, bool sprint, float? stopDistance) { - PrepareNavigation(type, dataId, to.Last(), fly, stopDistance); + PrepareNavigation(type, dataId, to.Last(), fly, sprint, stopDistance); _navmeshIpc.MoveTo(to); } @@ -198,5 +207,5 @@ internal sealed class MovementController : IDisposable Stop(); } - public sealed record DestinationData(uint? DataId, Vector3 Position, float StopDistance, bool IsFlying); + public sealed record DestinationData(uint? DataId, Vector3 Position, float StopDistance, bool IsFlying, bool CanSprint); } diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index ac87704d..a29c9180 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -3,17 +3,23 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; +using System.Linq; using System.Numerics; using System.Text.Json; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI; +using LLib.GameUI; +using Lumina.Excel.CustomSheets; using Questionable.Data; using Questionable.External; using Questionable.Model.V1; using Questionable.Model.V1.Converter; +using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; namespace Questionable.Controller; @@ -28,6 +34,7 @@ internal sealed class QuestController private readonly ICondition _condition; private readonly IChatGui _chatGui; private readonly IFramework _framework; + private readonly IGameGui _gameGui; private readonly AetheryteData _aetheryteData; private readonly LifestreamIpc _lifestreamIpc; private readonly TerritoryData _territoryData; @@ -35,7 +42,8 @@ internal sealed class QuestController public QuestController(DalamudPluginInterface pluginInterface, IDataManager dataManager, IClientState clientState, GameFunctions gameFunctions, MovementController movementController, IPluginLog pluginLog, ICondition condition, - IChatGui chatGui, IFramework framework, AetheryteData aetheryteData, LifestreamIpc lifestreamIpc) + IChatGui chatGui, IFramework framework, IGameGui gameGui, AetheryteData aetheryteData, + LifestreamIpc lifestreamIpc) { _pluginInterface = pluginInterface; _dataManager = dataManager; @@ -46,6 +54,7 @@ internal sealed class QuestController _condition = condition; _chatGui = chatGui; _framework = framework; + _gameGui = gameGui; _aetheryteData = aetheryteData; _lifestreamIpc = lifestreamIpc; _territoryData = new TerritoryData(dataManager); @@ -318,11 +327,45 @@ internal sealed class QuestController } } - if (step.SkipIf.Contains(ESkipCondition.FlyingUnlocked) && _gameFunctions.IsFlyingUnlocked(step.TerritoryId)) + if (!step.SkipIf.Contains(ESkipCondition.Never)) { - _pluginLog.Information("Skipping step, as flying is unlocked"); - IncreaseStepCount(); - return; + _pluginLog.Information("Checking skip conditions"); + + if (step.SkipIf.Contains(ESkipCondition.FlyingUnlocked) && + _gameFunctions.IsFlyingUnlocked(step.TerritoryId)) + { + _pluginLog.Information("Skipping step, as flying is unlocked"); + IncreaseStepCount(); + return; + } + + if (step is + { + DataId: not null, + InteractionType: EInteractionType.AttuneAetheryte or EInteractionType.AttuneAethernetShard + } && + _gameFunctions.IsAetheryteUnlocked((EAetheryteLocation)step.DataId.Value)) + { + _pluginLog.Information("Skipping step, as aetheryte/aethernet shard is unlocked"); + IncreaseStepCount(); + return; + } + + if (step is { DataId: not null, InteractionType: EInteractionType.AttuneAetherCurrent } && + _gameFunctions.IsAetherCurrentUnlocked(step.DataId.Value)) + { + _pluginLog.Information("Skipping step, as current is unlocked"); + IncreaseStepCount(); + return; + } + + QuestWork? questWork = _gameFunctions.GetQuestEx(CurrentQuest.Quest.QuestId); + if (questWork != null && step.MatchesQuestVariables(questWork.Value)) + { + _pluginLog.Information("Skipping step, as quest variables match"); + IncreaseStepCount(); + return; + } } if (!CurrentQuest.StepProgress.AethernetShortcutUsed) @@ -350,7 +393,7 @@ internal sealed class QuestController } else _movementController.NavigateTo(EMovementType.Quest, (uint)from, _aetheryteData.Locations[from], - false, + false, true, AetheryteConverter.IsLargeAetheryte(from) ? 10.9f : 6.9f); return; @@ -368,6 +411,11 @@ internal sealed class QuestController { _pluginLog.Information("We're at the jump destination, skipping movement"); } + else if (step.InteractionType == EInteractionType.CutsceneSelectString && + _condition[ConditionFlag.OccupiedInCutSceneEvent]) + { + _pluginLog.Information("In cutscene selection, skipping movement"); + } else if (step.Position != null) { float distance; @@ -413,7 +461,9 @@ internal sealed class QuestController if (actualDistance > distance) { _movementController.NavigateTo(EMovementType.Quest, step.DataId, step.Position.Value, - step.Fly && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); + fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), + sprint: step.Sprint != false, + stopDistance: distance); return; } } @@ -428,7 +478,9 @@ internal sealed class QuestController distance /= 2; _movementController.NavigateTo(EMovementType.Quest, step.DataId, [step.Position.Value], - step.Fly && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), distance); + fly: step.Fly == true && _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), + sprint: step.Sprint != false, + stopDistance: distance); return; } } @@ -609,8 +661,8 @@ internal sealed class QuestController else { _movementController.NavigateTo(EMovementType.Quest, step.DataId, - [step.JumpDestination.Position], - false, step.JumpDestination.StopDistance ?? stopDistance); + [step.JumpDestination.Position], false, false, + step.JumpDestination.StopDistance ?? stopDistance); _framework.RunOnTick(() => ActionManager.Instance()->UseAction(ActionType.GeneralAction, 2), TimeSpan.FromSeconds(step.JumpDestination.DelaySeconds ?? 0.5f)); } @@ -623,6 +675,54 @@ internal sealed class QuestController // Need to manually forward break; + case EInteractionType.CutsceneSelectString: + // to do this automatically, should likely be in Addon's post setup + if (_gameGui.TryGetAddonByName("CutSceneSelectString", out var addon) && + LAddon.IsAddonReady(&addon->AtkUnitBase)) + { + foreach (DialogueChoice dialogueChoice in step.DialogueChoices) + { + var excelSheet = _dataManager.Excel.GetSheet(dialogueChoice.ExcelSheet); + if (excelSheet == null) + { + _pluginLog.Error($"Unknown excel sheet '{dialogueChoice.ExcelSheet}'"); + continue; + } + + string? excelString = excelSheet + .FirstOrDefault(x => x.Key == dialogueChoice.Answer) + ?.Value + ?.ToString(); + if (excelString == null) + { + _pluginLog.Error( + $"Could not extract '{dialogueChoice.Answer}' from sheet '{dialogueChoice.ExcelSheet}'"); + return; + } + + _pluginLog.Verbose($"Looking for option '{excelString}'"); + for (int i = 5; i < addon->AtkUnitBase.AtkValuesCount; ++i) + { + var atkValue = addon->AtkUnitBase.AtkValues[i]; + if (atkValue.Type != ValueType.String) + continue; + + string? atkString = atkValue.ReadAtkString(); + _pluginLog.Verbose($"Option {i}: {atkString}"); + if (excelString == atkString) + { + _pluginLog.Information($"Selecting option {i - 5}: {atkString}"); + addon->AtkUnitBase.FireCallbackInt(i - 5); + return; + } + } + } + } + else if (step.DataId != null && !_condition[ConditionFlag.OccupiedInCutSceneEvent]) + _gameFunctions.InteractWith(step.DataId.Value); + + break; + default: _pluginLog.Warning($"Action '{step.InteractionType}' is not implemented"); break; diff --git a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs b/Questionable/Model/V1/Converter/InteractionTypeConverter.cs index fcd2228b..09a4dfaf 100644 --- a/Questionable/Model/V1/Converter/InteractionTypeConverter.cs +++ b/Questionable/Model/V1/Converter/InteractionTypeConverter.cs @@ -20,6 +20,7 @@ public sealed class InteractionTypeConverter() : EnumConverter { EInteractionType.Duty, "Duty" }, { EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" }, { EInteractionType.Jump, "Jump" }, + { EInteractionType.CutsceneSelectString, "CutsceneSelectString" }, { EInteractionType.ShouldBeAJump, "ShouldBeAJump" }, { EInteractionType.Instruction, "Instruction" }, }; diff --git a/Questionable/Model/V1/Converter/SkipConditionConverter.cs b/Questionable/Model/V1/Converter/SkipConditionConverter.cs index 0d98e515..644e0dea 100644 --- a/Questionable/Model/V1/Converter/SkipConditionConverter.cs +++ b/Questionable/Model/V1/Converter/SkipConditionConverter.cs @@ -6,6 +6,7 @@ public sealed class SkipConditionConverter() : EnumConverter(Val { private static readonly Dictionary Values = new() { + { ESkipCondition.Never, "Never" }, { ESkipCondition.FlyingUnlocked, "FlyingUnlocked" }, }; } diff --git a/Questionable/Model/V1/DialogueChoice.cs b/Questionable/Model/V1/DialogueChoice.cs new file mode 100644 index 00000000..1c32f218 --- /dev/null +++ b/Questionable/Model/V1/DialogueChoice.cs @@ -0,0 +1,7 @@ +namespace Questionable.Model.V1; + +public sealed class DialogueChoice +{ + public string ExcelSheet { get; set; } = null!; + public string Answer { get; set; } = null!; +} diff --git a/Questionable/Model/V1/EInteractionType.cs b/Questionable/Model/V1/EInteractionType.cs index f0ca8a32..ae0267f0 100644 --- a/Questionable/Model/V1/EInteractionType.cs +++ b/Questionable/Model/V1/EInteractionType.cs @@ -20,6 +20,7 @@ public enum EInteractionType Duty, SinglePlayerDuty, Jump, + CutsceneSelectString, /// /// Needs to be adjusted for coords etc. in the quest data. diff --git a/Questionable/Model/V1/ESkipCondition.cs b/Questionable/Model/V1/ESkipCondition.cs index 42b67ef5..bced035a 100644 --- a/Questionable/Model/V1/ESkipCondition.cs +++ b/Questionable/Model/V1/ESkipCondition.cs @@ -7,5 +7,6 @@ namespace Questionable.Model.V1; public enum ESkipCondition { None, + Never, FlyingUnlocked, } diff --git a/Questionable/Model/V1/QuestStep.cs b/Questionable/Model/V1/QuestStep.cs index 1ab58e52..6462f0ef 100644 --- a/Questionable/Model/V1/QuestStep.cs +++ b/Questionable/Model/V1/QuestStep.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Numerics; using System.Text.Json.Serialization; +using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions; using Questionable.Model.V1.Converter; namespace Questionable.Model.V1; @@ -21,7 +22,8 @@ public class QuestStep public bool Disabled { get; set; } public bool DisableNavmesh { get; set; } public bool? Mount { get; set; } - public bool Fly { get; set; } + public bool? Fly { get; set; } + public bool? Sprint { get; set; } public string? Comment { get; set; } public EAetheryteLocation? AetheryteShortcut { get; set; } @@ -42,4 +44,28 @@ public class QuestStep public uint? ContentFinderConditionId { get; set; } public IList SkipIf { get; set; } = new List(); + public IList CompletionQuestVariablesFlags { get; set; } = new List(); + public IList DialogueChoices { get; set; } = new List(); + + public unsafe bool MatchesQuestVariables(QuestWork questWork) + { + if (CompletionQuestVariablesFlags.Count != 6) + return false; + + for (int i = 0; i < 6; ++i) + { + short? check = CompletionQuestVariablesFlags[i]; + if (check == null) + continue; + + byte actualValue = questWork.Variables[i]; + byte expectedValue = check > 0 ? (byte)check : (byte)0; + byte checkByte = check > 0 ? (byte)check : (byte)-check; + + if ((actualValue & checkByte) != expectedValue) + return false; + } + + return true; + } } diff --git a/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4257_In Agamas Footsteps.json b/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4257_In Agamas Footsteps.json index e529fcc7..e6170225 100644 --- a/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4257_In Agamas Footsteps.json +++ b/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4257_In Agamas Footsteps.json @@ -44,7 +44,9 @@ "Z": -239.7956 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "Mount": false, + "Sprint": false } ] }, diff --git a/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4259_Radiant Patrol.json b/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4259_Radiant Patrol.json index fbe3e621..fc857101 100644 --- a/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4259_Radiant Patrol.json +++ b/Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4259_Radiant Patrol.json @@ -44,6 +44,14 @@ }, "TerritoryId": 957, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], "$": "QuestVariables after: 17 0 0 0 0 64" }, { @@ -60,6 +68,14 @@ "KillEnemyDataIds": [ 14111 ], + "CompletionQuestVariablesFlags": [ + null, + 1, + null, + null, + null, + null + ], "$": "QuestVariables after killing enemy: 17 1 0 0 0 64" }, { @@ -71,6 +87,14 @@ }, "TerritoryId": 957, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ], "$": "QuestVariables after: 33 2 0 0 0 96" }, { @@ -81,7 +105,16 @@ "Z": 372.54907 }, "TerritoryId": 957, - "InteractionType": "Interact" + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], + "$": "QuestVariables if done first: 16 16 16 0 0 128" } ] }, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json b/Questionable/QuestPaths/Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json index b3509be2..a306f320 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json @@ -43,8 +43,21 @@ "Z": 799.2217 }, "TerritoryId": 957, - "InteractionType": "WaitForManualProgress", - "Comment": "Talk (2, 2, 1)" + "InteractionType": "CutsceneSelectString", + "DialogueChoices": [ + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A2_000_088" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A3_000_098" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A5_000_107" + } + ] } ] }, @@ -59,8 +72,21 @@ "Z": 681.7273 }, "TerritoryId": 957, - "InteractionType": "WaitForManualProgress", - "Comment": "Talk (2, 1, 2)" + "InteractionType": "CutsceneSelectString", + "DialogueChoices": [ + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A6_000_149" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A7_000_158" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A8_000_164" + } + ] } ] }, @@ -75,8 +101,21 @@ "Z": 517.72327 }, "TerritoryId": 957, - "InteractionType": "WaitForManualProgress", - "Comment": "Talk (2, 2, 2)" + "InteractionType": "CutsceneSelectString", + "DialogueChoices": [ + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A9_000_200" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A10_000_209" + }, + { + "ExcelSheet": "quest/043/AktKma114_04370", + "Answer": "TEXT_AKTKMA114_04370_A11_000_218" + } + ] } ] }, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json b/Questionable/QuestPaths/Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json index 9598331d..f7b7ff8e 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json @@ -77,9 +77,9 @@ "Y": 4.357494, "Z": 0.7476196 }, + "StopDistance": 5, "TerritoryId": 962, "InteractionType": "Interact", - "StopDistance": 5, "AethernetShortcut": [ "[Old Sharlayan] The Rostra", "[Old Sharlayan] The Baldesion Annex" diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4451_Friends Gathered.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4451_Friends Gathered.json index ff79368e..2e4d6d93 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4451_Friends Gathered.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4451_Friends Gathered.json @@ -86,7 +86,8 @@ "AethernetShortcut": [ "[Old Sharlayan] Scholar's Harbor", "[Old Sharlayan] The Studium" - ] + ], + "$.1": "QuestVariables if done first: 16 16 128 0 0 128" }, { "DataId": 1039825, @@ -97,7 +98,6 @@ }, "TerritoryId": 962, "InteractionType": "Interact", - "Comment": "Unsure why this is on the same Sequence No", "AethernetShortcut": [ "[Old Sharlayan] The Studium", "[Old Sharlayan] The Leveilleur Estate" diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4453_A Strange New World.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4453_A Strange New World.json index cf022dd1..f6e509fd 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4453_A Strange New World.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4453_A Strange New World.json @@ -58,7 +58,7 @@ "TerritoryId": 960, "InteractionType": "Interact", "$.0": "[2]", - "$.2": "QuestVariables if done after [1]: 33 1 0 0 0 192" + "$.1": "QuestVariables if done after [1]: 33 1 0 0 0 192" }, { "DataId": 1040293, @@ -68,7 +68,9 @@ "Z": 665.5221 }, "TerritoryId": 960, - "InteractionType": "Interact" + "InteractionType": "Interact", + "$.0": "[3]", + "$.2": "QuestVariables if done first: 16 16 0 0 0 32" } ] }, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4454_On Burdened Wings.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4454_On Burdened Wings.json index a71e5b75..39fb5212 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4454_On Burdened Wings.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4454_On Burdened Wings.json @@ -31,6 +31,14 @@ "StopDistance": 8, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], "$.0": "[1]", "$.1": "QuestVariables if done first: 1 0 0 0 0 64" }, @@ -45,6 +53,14 @@ "TerritoryId": 960, "InteractionType": "Interact", "$.0": "[2]", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ], "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 96" }, { @@ -67,7 +83,16 @@ }, "StopDistance": 8, "TerritoryId": 960, - "InteractionType": "Interact" + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], + "$.1": "QuestVariables if done first: 1 0 0 0 0 128" } ] }, @@ -98,8 +123,16 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ], "$.0": "[1]", - "$.1": "QuestVariables if done first: 1 0 0 0 0 ??" + "$.1": "QuestVariables if done first: 1 0 0 0 0 32" }, { "DataId": 2012282, @@ -110,6 +143,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], "$.0": "[1]", "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 ??" }, @@ -121,7 +162,16 @@ "Z": -20.676025 }, "TerritoryId": 960, - "InteractionType": "Interact" + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], + "Comment": "TODO Verify quest variables flags" } ] }, @@ -170,6 +220,7 @@ "Y": 64.78333, "Z": -200.3357 }, + "StopDistance": 8, "TerritoryId": 960, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4456_Roads Paved of Sacrifice.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4456_Roads Paved of Sacrifice.json index 3e06deef..21ce4968 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4456_Roads Paved of Sacrifice.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4456_Roads Paved of Sacrifice.json @@ -82,6 +82,7 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "Mount": true, "DisableNavmesh": true } ] @@ -114,6 +115,14 @@ "StopDistance": 4, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], "$.0": "[1]", "$.1": "QuestVariables if done first: 1 0 0 0 0 128" }, @@ -127,6 +136,14 @@ "StopDistance": 4, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], "$.0": "[2]", "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 192" }, @@ -152,7 +169,15 @@ "TerritoryId": 960, "InteractionType": "Interact", "$.0": "[3]", - "$.1": "QuestVariables if done after [2]: 3 0 0 0 0 224" + "$.1": "QuestVariables if done after [2]: 3 0 0 0 0 224", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ] }, { "DataId": 2012357, @@ -164,8 +189,16 @@ "StopDistance": 4, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 16 + ], "$.0": "[4]", - "$.1": "QuestVariables if done first: TODO" + "$.1": "QuestVariables if done first: 1 0 0 0 0 16" } ] }, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4458_Where Knowledge Leads.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4458_Where Knowledge Leads.json index ede2f55d..281f8579 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4458_Where Knowledge Leads.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4458_Where Knowledge Leads.json @@ -101,8 +101,7 @@ "TerritoryId": 960, "InteractionType": "AttuneAetherCurrent", "AetheryteShortcut": "Ultima Thule - Abode of the Ea", - "AetherCurrentId": 2818390, - "Comment": "TODO Verify" + "AetherCurrentId": 2818390 }, { "DataId": 1039778, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4459_Victory x Lost.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4459_Victory x Lost.json index 74e149b1..cc69553c 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4459_Victory x Lost.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4459_Victory x Lost.json @@ -12,7 +12,7 @@ "Y": 269.0203, "Z": -633.5393 }, - "StopDistance": 5, + "StopDistance": 6, "TerritoryId": 960, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4460_x.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4460_x.json index 58508155..9313227f 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4460_x.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4460_x.json @@ -116,13 +116,24 @@ "AetherCurrentId": 2818396 }, { + "DataId": 2012038, "Position": { - "X": 639.9123, - "Y": 438.7276, - "Z": 293.33954 + "X": 645.6607, + "Y": 438.6276, + "Z": 291.0269 }, + "StopDistance": 1, "TerritoryId": 960, - "InteractionType": "WalkTo" + "InteractionType": "Jump", + "JumpDestination": { + "DataId": 2012038, + "Position": { + "X": 637.1709, + "Y": 439.23096, + "Z": 289.66187 + }, + "StopDistance": 3 + } }, { "DataId": 2012038, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4462_Forge Ahead.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4462_Forge Ahead.json index 8d3eda32..1588d7ad 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4462_Forge Ahead.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4462_Forge Ahead.json @@ -68,6 +68,7 @@ "Y": 417.0675, "Z": 414.66382 }, + "StopDistance": 5, "TerritoryId": 960, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4463_Youre Not Alone.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4463_Youre Not Alone.json index 84bf520b..8e6c1eb9 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4463_Youre Not Alone.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4463_Youre Not Alone.json @@ -29,6 +29,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], "$.0": "[1]", "$.1": "QuestVariables if done first: 16 0 0 16 0 128" }, @@ -41,6 +49,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 16 + ], "$.0": "[2]", "$.1": "QuestVariables if done after [1]: 32 16 0 16 0 144" }, @@ -53,6 +69,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 8 + ], "$.0": "[3]", "$.1": "QuestVariables if done after [1, 2]: 48 17 0 16 0 152" }, @@ -65,6 +89,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ], "$.0": "[4]", "$.1": "QuestVariables if done after [1, 2, 3]: 65 17 0 16 0 184" }, @@ -77,6 +109,14 @@ }, "TerritoryId": 960, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 4 + ], "$.0": "[5]", "$.1": "QuestVariables if done after [1, 2, 3, 4]: 81 17 16 16 0 188" }, @@ -88,7 +128,17 @@ "Z": 241.9928 }, "TerritoryId": 960, - "InteractionType": "Interact" + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], + "$.0": "[6]", + "$.1": "QuestVariables if done first: 16 0 1 0 0 64" } ] }, @@ -146,6 +196,7 @@ "Y": 637.10297, "Z": 5.2338257 }, + "StopDistance": 5, "TerritoryId": 960, "InteractionType": "Interact" } diff --git a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4464_Endwalker.json b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4464_Endwalker.json index 02a9dae4..f4a8bdcb 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4464_Endwalker.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4464_Endwalker.json @@ -69,6 +69,14 @@ "StopDistance": 5, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 1 + ], "$.0": "[1]", "$.1": "QuestVariables if done first: 1 0 0 0 0 1" }, @@ -82,6 +90,14 @@ "StopDistance": 5, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 128 + ], "$.0": "[2]", "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 129" }, @@ -94,6 +110,14 @@ }, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 64 + ], "$.0": "[3]", "$.1": "QuestVariables if done after [1, 2]: 3 0 0 0 0 193" }, @@ -107,6 +131,14 @@ "StopDistance": 5, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 16 + ], "$.0": "[4]", "$.1": "QuestVariables if done after [1, 2, 3]: 4 0 0 0 0 209" }, @@ -119,6 +151,14 @@ }, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 32 + ], "$.0": "[5]", "$.1": "QuestVariables if done after [1, 2, 3, 4]: 5 0 0 0 0 241" }, @@ -131,6 +171,14 @@ }, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 2 + ], "$.0": "[6]", "$.1": "QuestVariables if done after [1, 2, 3, 4, 5]: 6 0 0 0 0 243" }, @@ -143,6 +191,14 @@ }, "TerritoryId": 351, "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 8 + ], "$.0": "[7]", "$.1": "QuestVariables if done after [1, 2, 3, 4, 5, 6]: 7 0 0 0 0 251" }, @@ -154,7 +210,16 @@ "Z": 0.1373291 }, "TerritoryId": 351, - "InteractionType": "Interact" + "InteractionType": "Interact", + "CompletionQuestVariablesFlags": [ + null, + null, + null, + null, + null, + 4 + ], + "$.2": "QuestVariables if done first: 1 0 0 0 0 0 4" } ] }, diff --git a/Questionable/QuestPaths/Endwalker/MSQ/H-6.1/4526_Newfound Adventure.json b/Questionable/QuestPaths/Endwalker/MSQ/H-6.1/4526_Newfound Adventure.json index 1104fe72..d482a328 100644 --- a/Questionable/QuestPaths/Endwalker/MSQ/H-6.1/4526_Newfound Adventure.json +++ b/Questionable/QuestPaths/Endwalker/MSQ/H-6.1/4526_Newfound Adventure.json @@ -13,7 +13,8 @@ "Z": -631.281 }, "TerritoryId": 156, - "InteractionType": "Interact" + "InteractionType": "Interact", + "DisableNavmesh": true }, { "DataId": 1041232, diff --git a/Questionable/QuestSchema/schema_v1.json b/Questionable/QuestSchema/schema_v1.json index c63791af..a75af5c6 100644 --- a/Questionable/QuestSchema/schema_v1.json +++ b/Questionable/QuestSchema/schema_v1.json @@ -106,6 +106,7 @@ "Duty", "SinglePlayerDuty", "Jump", + "CutsceneSelectString", "ShouldBeAJump", "Instruction" ] @@ -129,6 +130,12 @@ "type": "boolean", "description": "If true and flying is unlocked in a zone, will use a flight path" }, + "Sprint": { + "type": [ + "boolean", + "null" + ] + }, "AetheryteShortcut": { "type": "string", "description": "The Aetheryte to teleport to (before moving)", @@ -419,7 +426,7 @@ ] }, "ContentFinderConditionId": { - "type": "number", + "type": "integer", "exclusiveMinimum": 0 }, "SkipIf": { @@ -428,10 +435,60 @@ "items": { "type": "string", "enum": [ + "Never", "FlyingUnlocked" ] } }, + "CompletionQuestVariablesFlags": { + "type": "array", + "description": "Quest Variables that dictate whether or not this step is skipped: null is don't check, positive values need to be set, negative values need to be unset", + "items": { + "type": [ + "integer", + "null" + ], + "enum": [ + null, + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + -1, + -2, + -4, + -8, + -16, + -32, + -64, + -128 + ] + }, + "minItems": 6, + "maxItems": 6 + }, + "DialogueChoices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ExcelSheet": { + "type": "string" + }, + "Answer": { + "type": "string" + } + }, + "required": [ + "ExcelSheet", + "Answer" + ] + } + }, "Comment": { "type": "string" } diff --git a/Questionable/Questionable.csproj b/Questionable/Questionable.csproj index 4c374490..177892f4 100644 --- a/Questionable/Questionable.csproj +++ b/Questionable/Questionable.csproj @@ -54,6 +54,10 @@ + + + +