Add cutscene talk, update EW parts
authorLiza Carvelli <liza@carvel.li>
Sat, 1 Jun 2024 16:46:57 +0000 (18:46 +0200)
committerLiza Carvelli <liza@carvel.li>
Sat, 1 Jun 2024 16:46:57 +0000 (18:46 +0200)
31 files changed:
.gitmodules [new file with mode: 0644]
LLib [new submodule]
Questionable.sln
Questionable/Controller/MovementController.cs
Questionable/Controller/QuestController.cs
Questionable/Model/V1/Converter/InteractionTypeConverter.cs
Questionable/Model/V1/Converter/SkipConditionConverter.cs
Questionable/Model/V1/DialogueChoice.cs [new file with mode: 0644]
Questionable/Model/V1/EInteractionType.cs
Questionable/Model/V1/ESkipCondition.cs
Questionable/Model/V1/QuestStep.cs
Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4257_In Agamas Footsteps.json
Questionable/QuestPaths/Endwalker/AetherCurrents/Thavnair/4259_Radiant Patrol.json
Questionable/QuestPaths/Endwalker/MSQ/A-Thavnair1-Labyrinthos1/4370_A Fishermans Friend.json
Questionable/QuestPaths/Endwalker/MSQ/F-Labyrinthos2/4449_Her Children One and All.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4451_Friends Gathered.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4453_A Strange New World.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4454_On Burdened Wings.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4456_Roads Paved of Sacrifice.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4458_Where Knowledge Leads.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4459_Victory x Lost.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4460_x.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4462_Forge Ahead.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4463_Youre Not Alone.json
Questionable/QuestPaths/Endwalker/MSQ/G-UltimaThule/4464_Endwalker.json
Questionable/QuestPaths/Endwalker/MSQ/H-6.1/4526_Newfound Adventure.json
Questionable/QuestSchema/schema_v1.json
Questionable/Questionable.csproj
Questionable/QuestionablePlugin.cs
Questionable/Windows/DebugWindow.cs
Questionable/packages.lock.json

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..4ac68e0
--- /dev/null
@@ -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 (submodule)
index 0000000..b5125d4
--- /dev/null
+++ b/LLib
@@ -0,0 +1 @@
+Subproject commit b5125d4b3f7cdc0c7514a01764e5b5d4d85f80a7
index a208fe5e0d742109a550b9f6095c17018e13de23..c14cc412efc1611ef25c51ed7f56bffbf1670764 100644 (file)
@@ -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
index 3d88a3f548fd39e2e14cdfc7f8766c786fb31f24..f49850022157fbb20c6fbd88ff1a36179b7681f8 100644 (file)
@@ -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<Vector3> to, bool fly, float? stopDistance)
+    public void NavigateTo(EMovementType type, uint? dataId, List<Vector3> 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);
 }
index ac87704d670cb56367feaa41a96909e775339aca..a29c918075a13b2136307a41d652d4b3ff4ad21c 100644 (file)
@@ -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<AddonCutSceneSelectString>("CutSceneSelectString", out var addon) &&
+                    LAddon.IsAddonReady(&addon->AtkUnitBase))
+                {
+                    foreach (DialogueChoice dialogueChoice in step.DialogueChoices)
+                    {
+                        var excelSheet = _dataManager.Excel.GetSheet<QuestDialogueText>(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;
index fcd2228b2b9298e3f7e48e233fcf583eab64adb0..09a4dfafe1000f6e43fbe8896582fce65b8280af 100644 (file)
@@ -20,6 +20,7 @@ public sealed class InteractionTypeConverter() : EnumConverter<EInteractionType>
         { EInteractionType.Duty, "Duty" },
         { EInteractionType.SinglePlayerDuty, "SinglePlayerDuty" },
         { EInteractionType.Jump, "Jump" },
+        { EInteractionType.CutsceneSelectString, "CutsceneSelectString" },
         { EInteractionType.ShouldBeAJump, "ShouldBeAJump" },
         { EInteractionType.Instruction, "Instruction" },
     };
index 0d98e515a923cbfae5275b062952beefa81a4e9b..644e0deaf611fd0ab25757fe8ad7c617777dd806 100644 (file)
@@ -6,6 +6,7 @@ public sealed class SkipConditionConverter() : EnumConverter<ESkipCondition>(Val
 {
     private static readonly Dictionary<ESkipCondition, string> 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 (file)
index 0000000..1c32f21
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Questionable.Model.V1;
+
+public sealed class DialogueChoice
+{
+    public string ExcelSheet { get; set; } = null!;
+    public string Answer { get; set; } = null!;
+}
index f0ca8a32584711474eb550f5631852ac6e92b8ef..ae0267f0659169e4ff558c279346d4676ca1523e 100644 (file)
@@ -20,6 +20,7 @@ public enum EInteractionType
     Duty,
     SinglePlayerDuty,
     Jump,
+    CutsceneSelectString,
 
     /// <summary>
     /// Needs to be adjusted for coords etc. in the quest data.
index 42b67ef5484df06f40ae0d51646041d8fa8175bc..bced035a53458b1f3605011436bb32a37424a050 100644 (file)
@@ -7,5 +7,6 @@ namespace Questionable.Model.V1;
 public enum ESkipCondition
 {
     None,
+    Never,
     FlyingUnlocked,
 }
index 1ab58e52faab68d056fd90d8d1f1350a4abca96f..6462f0ef39838efc70bb2b5da9b53622e02c3504 100644 (file)
@@ -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<ESkipCondition> SkipIf { get; set; } = new List<ESkipCondition>();
+    public IList<short?> CompletionQuestVariablesFlags { get; set; } = new List<short?>();
+    public IList<DialogueChoice> DialogueChoices { get; set; } = new List<DialogueChoice>();
+
+    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;
+    }
 }
index e529fcc772ff188bc1316fba07cb2c91fe15f925..e6170225d3402bbe8edec41dd605e4826a88fadb 100644 (file)
@@ -44,7 +44,9 @@
             "Z": -239.7956
           },
           "TerritoryId": 957,
-          "InteractionType": "Interact"
+          "InteractionType": "Interact",
+          "Mount": false,
+          "Sprint": false
         }
       ]
     },
index fbe3e6219e205c6ba5fd6c47c49290f804721f95..fc857101126eea38b8fa31a16cddf0ca5fc71a1e 100644 (file)
           },
           "TerritoryId": 957,
           "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            64
+          ],
           "$": "QuestVariables after: 17 0 0 0 0 64"
         },
         {
           "KillEnemyDataIds": [
             14111
           ],
+          "CompletionQuestVariablesFlags": [
+            null,
+            1,
+            null,
+            null,
+            null,
+            null
+          ],
           "$": "QuestVariables after killing enemy: 17 1 0 0 0 64"
         },
         {
           },
           "TerritoryId": 957,
           "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            32
+          ],
           "$": "QuestVariables after: 33 2 0 0 0 96"
         },
         {
             "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"
         }
       ]
     },
index b3509be2e0a2bf06f7b46ed56b665a425b472c2f..a306f32064a0c6234e81f4d9f97c223be3957e9f 100644 (file)
             "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"
+            }
+          ]
         }
       ]
     },
             "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"
+            }
+          ]
         }
       ]
     },
             "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"
+            }
+          ]
         }
       ]
     },
index 9598331dc2c4b3b8004a27f8f7769bc20ea6138a..f7b7ff8ef2ad43b01722b5784577b3644b4fbd2d 100644 (file)
@@ -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"
index ff79368ec8701e144decfb2ba5be4fbe02a066fb..2e4d6d9379da1552784cc909df6b2e2799d104b9 100644 (file)
@@ -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"
index cf022dd13f917e44b628964635aefa326ec372c4..f6e509fd197af230a7989425d1c022948151228e 100644 (file)
@@ -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"
         }
       ]
     },
index a71e5b75abc3995d34b6953c680b7f8583c1cf05..39fb52120452643abf2af4f8b41db2f07199f66b 100644 (file)
           "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"
         },
           "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"
         },
         {
           },
           "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"
         }
       ]
     },
           },
           "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,
           },
           "TerritoryId": 960,
           "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            64
+          ],
           "$.0": "[1]",
           "$.1": "QuestVariables if done after [1]: 2 0 0 0 0 ??"
         },
             "Z": -20.676025
           },
           "TerritoryId": 960,
-          "InteractionType": "Interact"
+          "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            128
+          ],
+          "Comment": "TODO Verify quest variables flags"
         }
       ]
     },
             "Y": 64.78333,
             "Z": -200.3357
           },
+          "StopDistance": 8,
           "TerritoryId": 960,
           "InteractionType": "Interact"
         }
index 3e06deef50c65a1c22c7735c688397eac5222b19..21ce496850125b091d91a7d14a51f18c23e082c8 100644 (file)
@@ -82,6 +82,7 @@
           },
           "TerritoryId": 960,
           "InteractionType": "Interact",
+          "Mount": true,
           "DisableNavmesh": true
         }
       ]
           "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"
         },
           "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"
         },
           "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,
           "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"
         }
       ]
     },
index ede2f55d6ae3356b454b3294ee652fd01d53d37f..281f85790b1a885d0c9a2feabfff807940544438 100644 (file)
           "TerritoryId": 960,
           "InteractionType": "AttuneAetherCurrent",
           "AetheryteShortcut": "Ultima Thule - Abode of the Ea",
-          "AetherCurrentId": 2818390,
-          "Comment": "TODO Verify"
+          "AetherCurrentId": 2818390
         },
         {
           "DataId": 1039778,
index 74e149b1fee10858e76c1045301e8d468b9c64fd..cc69553cb785d71bfa155e929a809f35c3a73a4d 100644 (file)
@@ -12,7 +12,7 @@
             "Y": 269.0203,
             "Z": -633.5393
           },
-          "StopDistance": 5,
+          "StopDistance": 6,
           "TerritoryId": 960,
           "InteractionType": "Interact"
         }
index 5850815514fd29425fd35bd5a68283681bf06f1a..9313227fecfa373bbad179a9aa801a2a4a27528f 100644 (file)
           "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,
index 8d3eda32451bf64196f143dd9b8e413dce5a8df5..1588d7adeed60369bc5caf3d3c9fd365915e7178 100644 (file)
@@ -68,6 +68,7 @@
             "Y": 417.0675,
             "Z": 414.66382
           },
+          "StopDistance": 5,
           "TerritoryId": 960,
           "InteractionType": "Interact"
         }
index 84bf520b824302a449148bffba5a6d480d320552..8e6c1eb950899378897d26b143f9a4b4a2839ee8 100644 (file)
           },
           "TerritoryId": 960,
           "InteractionType": "Interact",
+          "CompletionQuestVariablesFlags": [
+            null,
+            null,
+            null,
+            null,
+            null,
+            128
+          ],
           "$.0": "[1]",
           "$.1": "QuestVariables if done first: 16 0 0 16 0 128"
         },
           },
           "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"
         },
           },
           "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"
         },
           },
           "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"
         },
           },
           "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"
         },
             "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"
         }
       ]
     },
             "Y": 637.10297,
             "Z": 5.2338257
           },
+          "StopDistance": 5,
           "TerritoryId": 960,
           "InteractionType": "Interact"
         }
index 02a9dae404c7f9b4b181d76218a3be50a9767ce1..f4a8bdcb13bdf4a02b715035e7febcd2c2c70e85 100644 (file)
           "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"
         },
           "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"
         },
           },
           "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"
         },
           "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"
         },
           },
           "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"
         },
           },
           "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"
         },
           },
           "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"
         },
             "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"
         }
       ]
     },
index 1104fe7262c7c9cc53284aae97eeb2f8cd5a58bc..d482a328f5b7bcb76f69f399517125153ce3647c 100644 (file)
@@ -13,7 +13,8 @@
             "Z": -631.281
           },
           "TerritoryId": 156,
-          "InteractionType": "Interact"
+          "InteractionType": "Interact",
+          "DisableNavmesh": true
         },
         {
           "DataId": 1041232,
index c63791afa0ed7fbc26e897239357ebf7dc8bb1a7..a75af5c6279d83a5c729f087cf75f9452b010d49 100644 (file)
                     "Duty",
                     "SinglePlayerDuty",
                     "Jump",
+                    "CutsceneSelectString",
                     "ShouldBeAJump",
                     "Instruction"
                   ]
                   "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)",
                   ]
                 },
                 "ContentFinderConditionId": {
-                  "type": "number",
+                  "type": "integer",
                   "exclusiveMinimum": 0
                 },
                 "SkipIf": {
                   "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"
                 }
index 4c3744900d9bcb13e8a44161dd0d1bf6abb38841..177892f40f99f8779725e11b9ad23c598196c768 100644 (file)
         </Reference>
     </ItemGroup>
 
+    <ItemGroup>
+      <ProjectReference Include="..\LLib\LLib.csproj" />
+    </ItemGroup>
+
     <!--
     <ItemGroup>
         <EmbeddedResource Include="QuestPaths/**/*.json"/>
index ccb22e365332e71b28d4f59463fb552cff2c8f47..6142d47fe83399a49cb72dc887d4b5b6457df633 100644 (file)
@@ -53,7 +53,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         _movementController =
             new MovementController(navmeshIpc, clientState, _gameFunctions, condition, pluginLog);
         _questController = new QuestController(pluginInterface, dataManager, _clientState, _gameFunctions,
-            _movementController, pluginLog, condition, chatGui, framework, aetheryteData, lifestreamIpc);
+            _movementController, pluginLog, condition, chatGui, framework, gameGui, aetheryteData, lifestreamIpc);
         _windowSystem.AddWindow(new DebugWindow(_movementController, _questController, _gameFunctions, clientState,
             targetManager));
 
@@ -88,7 +88,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
                 out Vector3 worldPos))
         {
             _movementController.NavigateTo(EMovementType.Shortcut, null, worldPos,
-                _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType));
+                _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType), true);
         }
     }
 
index 43ffd947986db34ce94d8210b2819ef70cdaf09b..32a073bd4da22b8bf70a047888ed82584d63a198 100644 (file)
@@ -147,7 +147,8 @@ internal sealed class DebugWindow : Window
                 if (ImGui.Button("Move to Target"))
                 {
                     _movementController.NavigateTo(EMovementType.DebugWindow, _targetManager.Target.DataId,
-                        _targetManager.Target.Position, _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType));
+                        _targetManager.Target.Position, _gameFunctions.IsFlyingUnlocked(_clientState.TerritoryType),
+                        true);
                 }
             }
             else
index 3fe96a8457cde4ccecf65da9c3eb37c45b37fc69..e5fcfce94540fb6bad199bb6f59ec1cd0e3e3ffa 100644 (file)
@@ -21,6 +21,9 @@
         "type": "Transitive",
         "resolved": "8.0.0",
         "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
+      },
+      "llib": {
+        "type": "Project"
       }
     }
   }