diff --git a/Directory.Build.targets b/Directory.Build.targets
index 9c7a8c24..cfa9441e 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -1,5 +1,5 @@
-
- 4.13
+
+ 4.15
diff --git a/GatheringPathRenderer/.gitignore b/GatheringPathRenderer/.gitignore
new file mode 100644
index 00000000..a60a458a
--- /dev/null
+++ b/GatheringPathRenderer/.gitignore
@@ -0,0 +1 @@
+/dist
diff --git a/GatheringPathRenderer/DalamudPackager.targets b/GatheringPathRenderer/DalamudPackager.targets
new file mode 100644
index 00000000..7f129a87
--- /dev/null
+++ b/GatheringPathRenderer/DalamudPackager.targets
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/GatheringPathRenderer/GatheringPathRenderer.csproj b/GatheringPathRenderer/GatheringPathRenderer.csproj
index 6009a518..73d05cbe 100644
--- a/GatheringPathRenderer/GatheringPathRenderer.csproj
+++ b/GatheringPathRenderer/GatheringPathRenderer.csproj
@@ -1,4 +1,11 @@
+
+ 0.1
+ dist
+ $(SolutionDir)=X:\
+ x64
+
+
@@ -6,4 +13,5 @@
+
diff --git a/GatheringPathRenderer/GatheringPathRenderer.json b/GatheringPathRenderer/GatheringPathRenderer.json
index 8d68d1d8..56f91709 100644
--- a/GatheringPathRenderer/GatheringPathRenderer.json
+++ b/GatheringPathRenderer/GatheringPathRenderer.json
@@ -1,6 +1,7 @@
{
"Name": "GatheringPathRenderer",
"Author": "Liza Carvelli",
- "Punchline": "dev only plugin: Renders gathering location.",
- "Description": "dev only plugin: Renders gathering location (without ECommons polluting the entire normal project)."
+ "Punchline": "[Questionable dev plugin]: Renders gathering location.",
+ "Description": "[Questionable dev plugin]: Renders gathering location using Splatoon.",
+ "RepoUrl": "https://git.carvel.li/liza/Questionable/src/branch/master/GatheringPathRenderer"
}
diff --git a/GatheringPathRenderer/RendererPlugin.cs b/GatheringPathRenderer/RendererPlugin.cs
index 5ef430ea..93c666a8 100644
--- a/GatheringPathRenderer/RendererPlugin.cs
+++ b/GatheringPathRenderer/RendererPlugin.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.Encodings.Web;
@@ -17,11 +18,11 @@ using ECommons.Schedulers;
using ECommons.SplatoonAPI;
using GatheringPathRenderer.Windows;
using LLib.GameData;
-using Questionable.Model;
using Questionable.Model.Gathering;
namespace GatheringPathRenderer;
+[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public sealed class RendererPlugin : IDalamudPlugin
{
private const long OnTerritoryChange = -2;
@@ -56,8 +57,10 @@ public sealed class RendererPlugin : IDalamudPlugin
_editorCommands = new EditorCommands(this, dataManager, commandManager, targetManager, clientState, chatGui,
configuration);
- _editorWindow = new EditorWindow(this, _editorCommands, dataManager, targetManager, clientState, objectTable)
+ var configWindow = new ConfigWindow(pluginInterface, configuration);
+ _editorWindow = new EditorWindow(this, _editorCommands, dataManager, targetManager, clientState, objectTable, configWindow)
{ IsOpen = true };
+ _windowSystem.AddWindow(configWindow);
_windowSystem.AddWindow(_editorWindow);
_currentClassJob = (EClassJob?)_clientState.LocalPlayer?.ClassJob.RowId ?? EClassJob.Adventurer;
@@ -78,6 +81,7 @@ public sealed class RendererPlugin : IDalamudPlugin
{
get
{
+#if DEBUG
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Parent;
if (solutionDirectory != null)
{
@@ -88,6 +92,12 @@ public sealed class RendererPlugin : IDalamudPlugin
}
throw new Exception("Unable to resolve project path");
+#else
+ var allPluginsDirectory = _pluginInterface.ConfigFile.Directory ?? throw new Exception("Unknown directory for plugin configs");
+ return allPluginsDirectory
+ .CreateSubdirectory("Questionable")
+ .CreateSubdirectory("GatheringPaths");
+#endif
}
}
@@ -103,12 +113,18 @@ public sealed class RendererPlugin : IDalamudPlugin
try
{
- foreach (var expansionFolder in ExpansionData.ExpansionFolders.Values)
+#if DEBUG
+ foreach (var expansionFolder in Questionable.Model.ExpansionData.ExpansionFolders.Values)
LoadFromDirectory(
new DirectoryInfo(Path.Combine(PathsDirectory.FullName, expansionFolder)));
-
_pluginLog.Information(
$"Loaded {_gatheringLocations.Count} gathering root locations from project directory");
+#else
+ LoadFromDirectory(PathsDirectory);
+ _pluginLog.Information(
+ $"Loaded {_gatheringLocations.Count} gathering root locations from {PathsDirectory.FullName} directory");
+#endif
+
}
catch (Exception e)
{
diff --git a/GatheringPathRenderer/Windows/ConfigWindow.cs b/GatheringPathRenderer/Windows/ConfigWindow.cs
new file mode 100644
index 00000000..0e9ba04c
--- /dev/null
+++ b/GatheringPathRenderer/Windows/ConfigWindow.cs
@@ -0,0 +1,33 @@
+using Dalamud.Interface.Windowing;
+using Dalamud.Plugin;
+using ImGuiNET;
+
+namespace GatheringPathRenderer.Windows;
+
+internal sealed class ConfigWindow : Window
+{
+ private readonly IDalamudPluginInterface _pluginInterface;
+ private readonly Configuration _configuration;
+
+ public ConfigWindow(IDalamudPluginInterface pluginInterface, Configuration configuration)
+ : base("Gathering Path Config", ImGuiWindowFlags.AlwaysAutoResize)
+ {
+ _pluginInterface = pluginInterface;
+ _configuration = configuration;
+
+ AllowPinning = false;
+ AllowClickthrough = false;
+ }
+
+ public override void Draw()
+ {
+ string authorName = _configuration.AuthorName;
+ if (ImGui.InputText("Author name for new files", ref authorName, 256))
+ {
+ _configuration.AuthorName = authorName;
+ Save();
+ }
+ }
+
+ private void Save() => _pluginInterface.SavePluginConfig(_configuration);
+}
diff --git a/GatheringPathRenderer/Windows/EditorWindow.cs b/GatheringPathRenderer/Windows/EditorWindow.cs
index 1150558f..c117f4a9 100644
--- a/GatheringPathRenderer/Windows/EditorWindow.cs
+++ b/GatheringPathRenderer/Windows/EditorWindow.cs
@@ -6,6 +6,7 @@ using System.Numerics;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
+using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
@@ -32,8 +33,8 @@ internal sealed class EditorWindow : Window
_targetLocation;
public EditorWindow(RendererPlugin plugin, EditorCommands editorCommands, IDataManager dataManager,
- ITargetManager targetManager, IClientState clientState, IObjectTable objectTable)
- : base("Gathering Path Editor###QuestionableGatheringPathEditor",
+ ITargetManager targetManager, IClientState clientState, IObjectTable objectTable, ConfigWindow configWindow)
+ : base($"Gathering Path Editor {typeof(EditorWindow).Assembly.GetName().Version!.ToString(2)}###QuestionableGatheringPathEditor",
ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNavFocus | ImGuiWindowFlags.AlwaysAutoResize)
{
_plugin = plugin;
@@ -48,6 +49,20 @@ internal sealed class EditorWindow : Window
MinimumSize = new Vector2(300, 100),
};
+ TitleBarButtons.Add(new TitleBarButton
+ {
+ Icon = FontAwesomeIcon.Cog,
+ IconOffset = new Vector2(1.5f, 1),
+ Click = _ => configWindow.IsOpen = true,
+ Priority = int.MinValue,
+ ShowTooltip = () =>
+ {
+ ImGui.BeginTooltip();
+ ImGui.Text("Open Configuration");
+ ImGui.EndTooltip();
+ }
+ });
+
RespectCloseHotkey = false;
ShowCloseButton = false;
AllowPinning = false;
diff --git a/GatheringPaths/7.x - Dawntrail/Shaaloani/1002_Yawtanane Grasslands_BTN.json b/GatheringPaths/7.x - Dawntrail/Shaaloani/1002_Yawtanane Grasslands_BTN.json
new file mode 100644
index 00000000..44d84892
--- /dev/null
+++ b/GatheringPaths/7.x - Dawntrail/Shaaloani/1002_Yawtanane Grasslands_BTN.json
@@ -0,0 +1,138 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "Steps": [
+ {
+ "TerritoryId": 1190,
+ "InteractionType": "None"
+ }
+ ],
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34920,
+ "Locations": [
+ {
+ "Position": {
+ "X": 192.6021,
+ "Y": 12.31054,
+ "Z": 631.2545
+ }
+ },
+ {
+ "Position": {
+ "X": 194.8373,
+ "Y": 12.50387,
+ "Z": 646.5401
+ }
+ },
+ {
+ "Position": {
+ "X": 180.8447,
+ "Y": 12.43262,
+ "Z": 610.7131
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34919,
+ "Locations": [
+ {
+ "Position": {
+ "X": 186.171,
+ "Y": 12.54104,
+ "Z": 634.9042
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34917,
+ "Locations": [
+ {
+ "Position": {
+ "X": 39.45634,
+ "Y": -0.06042051,
+ "Z": 502.3853
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34918,
+ "Locations": [
+ {
+ "Position": {
+ "X": 46.03248,
+ "Y": -0.7049216,
+ "Z": 491.6059
+ }
+ },
+ {
+ "Position": {
+ "X": 36.15481,
+ "Y": -0.0501074,
+ "Z": 505.9388
+ }
+ },
+ {
+ "Position": {
+ "X": 24.72226,
+ "Y": 0.5922582,
+ "Z": 528.0809
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34922,
+ "Locations": [
+ {
+ "Position": {
+ "X": 2.302937,
+ "Y": -4.586716,
+ "Z": 687.4797
+ }
+ },
+ {
+ "Position": {
+ "X": 30.02284,
+ "Y": -2.447479,
+ "Z": 704.4326
+ }
+ },
+ {
+ "Position": {
+ "X": 41.59287,
+ "Y": -0.8454803,
+ "Z": 692.0099
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34921,
+ "Locations": [
+ {
+ "Position": {
+ "X": 18.47237,
+ "Y": -2.987581,
+ "Z": 690.8011
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPathGenerator/RoslynElements/CombatItemUseExtensions.cs b/QuestPathGenerator/RoslynElements/CombatItemUseExtensions.cs
new file mode 100644
index 00000000..93e5d696
--- /dev/null
+++ b/QuestPathGenerator/RoslynElements/CombatItemUseExtensions.cs
@@ -0,0 +1,29 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Questionable.Model.Questing;
+using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
+using static Questionable.QuestPathGenerator.RoslynShortcuts;
+
+namespace Questionable.QuestPathGenerator.RoslynElements;
+
+internal static class CombatItemUseExtensions
+{
+ public static ExpressionSyntax ToExpressionSyntax(this CombatItemUse combatItemuse)
+ {
+ var emptyItemuse = new CombatItemUse();
+ return ObjectCreationExpression(
+ IdentifierName(nameof(CombatItemUse)))
+ .WithInitializer(
+ InitializerExpression(
+ SyntaxKind.ObjectInitializerExpression,
+ SeparatedList(
+ SyntaxNodeList(
+ Assignment(nameof(CombatItemUse.ItemId), combatItemuse.ItemId,
+ emptyItemuse.ItemId)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(CombatItemUse.Condition), combatItemuse.Condition, emptyItemuse.Condition)
+ .AsSyntaxNodeOrToken(),
+ Assignment(nameof(combatItemuse.Value), combatItemuse.Value, emptyItemuse.Value)
+ .AsSyntaxNodeOrToken()))));
+ }
+}
diff --git a/QuestPathGenerator/RoslynElements/QuestStepExtensions.cs b/QuestPathGenerator/RoslynElements/QuestStepExtensions.cs
index 6b76bb95..2d3ec803 100644
--- a/QuestPathGenerator/RoslynElements/QuestStepExtensions.cs
+++ b/QuestPathGenerator/RoslynElements/QuestStepExtensions.cs
@@ -107,6 +107,9 @@ internal static class QuestStepExtensions
.AsSyntaxNodeOrToken(),
AssignmentList(nameof(QuestStep.ComplexCombatData), step.ComplexCombatData)
.AsSyntaxNodeOrToken(),
+ Assignment(nameof(QuestStep.CombatItemUse), step.CombatItemUse,
+ emptyStep.CombatItemUse)
+ .AsSyntaxNodeOrToken(),
Assignment(nameof(QuestStep.CombatDelaySecondsAtStart),
step.CombatDelaySecondsAtStart,
emptyStep.CombatDelaySecondsAtStart)
diff --git a/QuestPathGenerator/RoslynShortcuts.cs b/QuestPathGenerator/RoslynShortcuts.cs
index c5fb47f5..2c1df09f 100644
--- a/QuestPathGenerator/RoslynShortcuts.cs
+++ b/QuestPathGenerator/RoslynShortcuts.cs
@@ -71,6 +71,7 @@ public static class RoslynShortcuts
GatheringNodeGroup nodeGroup => nodeGroup.ToExpressionSyntax(),
GatheringNode nodeLocation => nodeLocation.ToExpressionSyntax(),
GatheringLocation location => location.ToExpressionSyntax(),
+ CombatItemUse combatItemUse => combatItemUse.ToExpressionSyntax(),
not null when value.GetType().IsEnum => MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(value.GetType().Name), IdentifierName(value.GetType().GetEnumName(value)!)),
_ => throw new Exception($"Unsupported data type {value.GetType()} = {value}")
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/A5-Southern Thanalan, Little Ala Mhigo/757_On to Little Ala Mhigo.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/A5-Southern Thanalan, Little Ala Mhigo/757_On to Little Ala Mhigo.json
index 27a25019..19fac78d 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/A5-Southern Thanalan, Little Ala Mhigo/757_On to Little Ala Mhigo.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/A5-Southern Thanalan, Little Ala Mhigo/757_On to Little Ala Mhigo.json
@@ -36,6 +36,16 @@
"InteractionType": "WalkTo",
"TargetTerritoryId": 146
},
+ {
+ "Position": {
+ "X": -47.50145,
+ "Y": 16.362688,
+ "Z": -439.62503
+ },
+ "TerritoryId": 146,
+ "InteractionType": "WalkTo",
+ "$": "vnav sometimes attempts to walk to the aetheryte using exactly one waypoint between zone boundary and aetheryte, which means walking into walls"
+ },
{
"TerritoryId": 146,
"InteractionType": "AttuneAetheryte",
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B7-Coerthas Central Highlands, Camp Dragonhead/897_The Talk of Coerthas.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B7-Coerthas Central Highlands, Camp Dragonhead/897_The Talk of Coerthas.json
index fc0042f5..b5543417 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B7-Coerthas Central Highlands, Camp Dragonhead/897_The Talk of Coerthas.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B7-Coerthas Central Highlands, Camp Dragonhead/897_The Talk of Coerthas.json
@@ -103,7 +103,8 @@
},
"TerritoryId": 155,
"InteractionType": "WalkTo",
- "$": "NW Skyfire Locks door (inside)"
+ "$": "NW Skyfire Locks door (inside)",
+ "Mount": true
},
{
"Position": {
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/924_Ye of Little Faith.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/924_Ye of Little Faith.json
index f3401f9b..66891113 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/924_Ye of Little Faith.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/924_Ye of Little Faith.json
@@ -49,7 +49,8 @@
},
"TerritoryId": 155,
"InteractionType": "WalkTo",
- "Comment": "North Whitebrim, Stairs (bottom)"
+ "Comment": "North Whitebrim, Stairs (bottom)",
+ "Mount": true
},
{
"Position": {
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/927_Factual Folklore.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/927_Factual Folklore.json
index b9550d58..924c6a9d 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/927_Factual Folklore.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/B9-Coerthas Central Highlands, Whitebrim Front/927_Factual Folklore.json
@@ -35,10 +35,13 @@
"Z": 89.58569
},
"TerritoryId": 155,
- "InteractionType": "Instruction",
- "Comment": "Use Quest item on enemy to weaken it first",
- "$": "Status Effects: 22 (HP Penalty) + 62 (Damage Down)",
+ "InteractionType": "Combat",
"EnemySpawnType": "AfterInteraction",
+ "CombatItemUse": {
+ "ItemId": 2000961,
+ "Condition": "MissingStatus",
+ "Value": 22
+ },
"ComplexCombatData": [
{
"DataId": 2196
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/961_Representing the Representative.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/961_Representing the Representative.json
index 6d2279b9..f4d1379f 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/961_Representing the Representative.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/961_Representing the Representative.json
@@ -76,6 +76,15 @@
{
"Sequence": 3,
"Steps": [
+ {
+ "Position": {
+ "X": 303.96317,
+ "Y": -36.40591,
+ "Z": 316.74185
+ },
+ "TerritoryId": 138,
+ "InteractionType": "WalkTo"
+ },
{
"DataId": 1006501,
"Position": {
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/962_The Reluctant Researcher.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/962_The Reluctant Researcher.json
index 8266ce99..23eb5a02 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/962_The Reluctant Researcher.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C2-Western La Noscea, Isles of Umbra/962_The Reluctant Researcher.json
@@ -26,6 +26,15 @@
{
"Sequence": 1,
"Steps": [
+ {
+ "Position": {
+ "X": 303.96317,
+ "Y": -36.40591,
+ "Z": 316.74185
+ },
+ "TerritoryId": 138,
+ "InteractionType": "WalkTo"
+ },
{
"DataId": 1007640,
"Position": {
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1001_Drowning Out the Voices.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1001_Drowning Out the Voices.json
index d1a4fdbf..1fcf9d81 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1001_Drowning Out the Voices.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1001_Drowning Out the Voices.json
@@ -19,6 +19,7 @@
},
{
"Sequence": 1,
+ "Comment": "'Invalid target' messages can just be bad positioning?",
"Steps": [
{
"Position": {
@@ -52,7 +53,6 @@
"InteractionType": "Combat",
"EnemySpawnType": "AfterItemUse",
"ItemId": 2000766,
- "GroundTarget": true,
"KillEnemyDataIds": [
46
],
@@ -86,6 +86,27 @@
64
]
},
+ {
+ "Position": {
+ "X": -219.34567,
+ "Y": 4.551038,
+ "Z": -637.8296
+ },
+ "TerritoryId": 156,
+ "InteractionType": "WalkTo",
+ "SkipConditions": {
+ "StepIf": {
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 8
+ ]
+ }
+ }
+ },
{
"DataId": 2002234,
"Position": {
@@ -161,7 +182,6 @@
"InteractionType": "Combat",
"EnemySpawnType": "AfterItemUse",
"ItemId": 2000766,
- "GroundTarget": true,
"KillEnemyDataIds": [
46
],
diff --git a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1004_Fool Me Twice.json b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1004_Fool Me Twice.json
index daaad896..0629a682 100644
--- a/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1004_Fool Me Twice.json
+++ b/QuestPaths/2.x - A Realm Reborn/MSQ-2/C6-Mor Dhona/1004_Fool Me Twice.json
@@ -72,7 +72,7 @@
"Z": -609.4606
},
"TerritoryId": 156,
- "InteractionType": "UseItem",
+ "InteractionType": "SinglePlayerDuty",
"ItemId": 2000771
}
]
diff --git a/QuestPaths/2.x - A Realm Reborn/Side Quests/Black Shroud/Gridania/970_Some Seedy Business.json b/QuestPaths/2.x - A Realm Reborn/Side Quests/Black Shroud/Gridania/970_Some Seedy Business.json
index 84536780..03953144 100644
--- a/QuestPaths/2.x - A Realm Reborn/Side Quests/Black Shroud/Gridania/970_Some Seedy Business.json
+++ b/QuestPaths/2.x - A Realm Reborn/Side Quests/Black Shroud/Gridania/970_Some Seedy Business.json
@@ -57,7 +57,7 @@
},
"TerritoryId": 152,
"InteractionType": "Interact",
- "StopDistance": 0.1,
+ "StopDistance": 0.25,
"AetheryteShortcut": "East Shroud - Hawthorne Hut",
"Fly": true,
"SkipConditions": {
@@ -83,7 +83,7 @@
},
"TerritoryId": 152,
"InteractionType": "Interact",
- "StopDistance": 0.1,
+ "StopDistance": 0.25,
"AetheryteShortcut": "East Shroud - Hawthorne Hut",
"Fly": true,
"SkipConditions": {
@@ -109,7 +109,7 @@
},
"TerritoryId": 152,
"InteractionType": "Interact",
- "StopDistance": 0.1,
+ "StopDistance": 0.25,
"AetheryteShortcut": "East Shroud - Hawthorne Hut",
"Fly": true,
"SkipConditions": {
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1979_Basic Training.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1979_Basic Training.json
new file mode 100644
index 00000000..2955b31f
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1979_Basic Training.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012145,
+ "Position": {
+ "X": -607.7516,
+ "Y": -176.4502,
+ "Z": -527.5502
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -441.42657,
+ "Y": -167.25401,
+ "Z": -432.4462
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4093,
+ "MinimumKillCount": 5
+ }
+ ],
+ "Fly": true,
+ "$": "5 in close range here, so if any dead it might be a little slower.",
+ "$.1": "this is the area the quest suggests; there /are/ spinner-rooks sooner, and walking there at level w/o flying might cause interruption, but they're more spread out everywhere else - YMMV."
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012145,
+ "Position": {
+ "X": -607.7516,
+ "Y": -176.4502,
+ "Z": -527.5502
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1980_Good Clean Fun.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1980_Good Clean Fun.json
new file mode 100644
index 00000000..be941a7e
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1980_Good Clean Fun.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012146,
+ "Position": {
+ "X": -643.76294,
+ "Y": -176.4502,
+ "Z": -527.3976
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -439.6144,
+ "Y": -186.3405,
+ "Z": -617.2911
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4092,
+ "MinimumKillCount": 5
+ }
+ ],
+ "Fly": true,
+ "$": "by starting here we can guarantee a more sane straightline progression instead of starting in the middle, going all the way to one side, then running all the way to the other side."
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012146,
+ "Position": {
+ "X": -643.76294,
+ "Y": -176.4502,
+ "Z": -527.3976
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Azys Lla - Helix"
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1981_General Protection Fault.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1981_General Protection Fault.json
new file mode 100644
index 00000000..8d59d544
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1981_General Protection Fault.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012147,
+ "Position": {
+ "X": -426.627,
+ "Y": -162.1281,
+ "Z": -328.6031
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -528.74396,
+ "Y": -143.65883,
+ "Z": -580.686
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AutoOnEnterArea",
+ "KillEnemyDataIds": [4503],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012147,
+ "Position": {
+ "X": -426.627,
+ "Y": -162.1281,
+ "Z": -328.6031
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1982_Excessive Force.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1982_Excessive Force.json
new file mode 100644
index 00000000..c3906c89
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1982_Excessive Force.json
@@ -0,0 +1,65 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012148,
+ "Position": {
+ "X": -173.2663,
+ "Y": -162.03395,
+ "Z": -510.39905
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -189.43037,
+ "Y": -160.1837,
+ "Z": -499.3027
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4091,
+ "MinimumKillCount": 3,
+ "$": "Slay clockwork paladins."
+ },
+ {
+ "DataId": 4090,
+ "MinimumKillCount": 3,
+ "$": "Slay clockwork engineers."
+ }
+ ],
+ "$": "they're all around so we can just start here. possible issue with a FATE spawning here, YMMV."
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012148,
+ "Position": {
+ "X": -173.2663,
+ "Y": -162.03395,
+ "Z": -510.39905
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1983_Chimerical Abominations.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1983_Chimerical Abominations.json
new file mode 100644
index 00000000..e84881f9
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1983_Chimerical Abominations.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012289,
+ "Position": {
+ "X": 231.18933,
+ "Y": -72.92926,
+ "Z": -603.1434
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 517.51276,
+ "Y": -40.378292,
+ "Z": -785.99677
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4141,
+ "MinimumKillCount": 2
+ }
+ ],
+ "Fly": true,
+ "$": "FATE spawns on top of the suggested area, so we move a bit east."
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012289,
+ "Position": {
+ "X": 231.18933,
+ "Y": -72.92926,
+ "Z": -603.1434
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1984_Pollution Solution.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1984_Pollution Solution.json
new file mode 100644
index 00000000..ef384a78
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1984_Pollution Solution.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012290,
+ "Position": {
+ "X": 760.5248,
+ "Y": -30.307041,
+ "Z": -579.67505
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 668.9029,
+ "Y": -86.006,
+ "Z": -775.5664
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4046,
+ "MinimumKillCount": 4
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012290,
+ "Position": {
+ "X": 760.5248,
+ "Y": -30.307041,
+ "Z": -579.67505
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1985_Good Hunting.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1985_Good Hunting.json
new file mode 100644
index 00000000..689b47bb
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1985_Good Hunting.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012291,
+ "Position": {
+ "X": 804.074,
+ "Y": -26.326342,
+ "Z": -527.48914
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 355.31863,
+ "Y": -46.22946,
+ "Z": -507.70428
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4072,
+ "MinimumKillCount": 4
+ }
+ ],
+ "Fly": true,
+ "$": "we go to the west of the marked area to avoid idling in the FATE that spawns on the east of it; we'll still path through it when we expend all the westward mobs, but that's just how they spawn, shrugging emoji."
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012291,
+ "Position": {
+ "X": 804.074,
+ "Y": -26.326342,
+ "Z": -527.48914
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1986_Defense Protocols.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1986_Defense Protocols.json
new file mode 100644
index 00000000..ce12a094
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1986_Defense Protocols.json
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012292,
+ "Position": {
+ "X": 583.9779,
+ "Y": 10.93506,
+ "Z": 100.02283
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 773.4017,
+ "Y": -0.06258035,
+ "Z": 147.04689
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "KillEnemyDataIds": [4669, 4970, 4671, 4672, 4673],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012292,
+ "Position": {
+ "X": 583.9779,
+ "Y": 10.93506,
+ "Z": 100.02283
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1987_Snikt.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1987_Snikt.json
new file mode 100644
index 00000000..42a0325a
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1987_Snikt.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012293,
+ "Position": {
+ "X": 573.4187,
+ "Y": 13.072888,
+ "Z": 329.2439
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 495.16974,
+ "Y": 31.67624,
+ "Z": 517.37463
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4083,
+ "MinimumKillCount": 4
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012293,
+ "Position": {
+ "X": 573.4187,
+ "Y": 13.072888,
+ "Z": 329.2439
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1988_A Crude Facsimile.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1988_A Crude Facsimile.json
new file mode 100644
index 00000000..18eef643
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1988_A Crude Facsimile.json
@@ -0,0 +1,156 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012294,
+ "Position": {
+ "X": 366.4148,
+ "Y": 20.214104,
+ "Z": 756.7101
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 505.4667,
+ "Y": 16.87959,
+ "Z": 751.14856
+ },
+ "TerritoryId": 402,
+ "InteractionType": "WalkTo",
+ "Fly": true,
+ "$": "can get stuck on the spire structure when trying to land. could land elsewhere but we want to start with this one because it's the first on foot, so if done before flying unlocked that would necessitate walking directly past it.",
+ "SkipConditions": {
+ "StepIf": {
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ }
+ }
+ },
+ {
+ "DataId": 2005900,
+ "Position": {
+ "X": 506.76733,
+ "Y": 16.861145,
+ "Z": 750.24023
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 2005903,
+ "Position": {
+ "X": 544.64014,
+ "Y": 15.945618,
+ "Z": 760.61633
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 8
+ ]
+ },
+ {
+ "DataId": 2005899,
+ "Position": {
+ "X": 545.4031,
+ "Y": 20.2182,
+ "Z": 802.8534
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 2005902,
+ "Position": {
+ "X": 614.6791,
+ "Y": 20.2182,
+ "Z": 758.938
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 16
+ ]
+ },
+ {
+ "DataId": 2005901,
+ "Position": {
+ "X": 605.3406,
+ "Y": 15.945618,
+ "Z": 708.06433
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012294,
+ "Position": {
+ "X": 366.4148,
+ "Y": 20.214104,
+ "Z": 756.7101
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1989_Recycling.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1989_Recycling.json
new file mode 100644
index 00000000..1541b26d
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1989_Recycling.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012295,
+ "Position": {
+ "X": 214.64856,
+ "Y": 13.75853,
+ "Z": 536.9801
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": 144.31177,
+ "Y": 5.9740877,
+ "Z": 387.8086
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4630,
+ "MinimumKillCount": 3
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012295,
+ "Position": {
+ "X": 214.64856,
+ "Y": 13.75853,
+ "Z": 536.9801
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1990_Inadequate Safety Measures.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1990_Inadequate Safety Measures.json
new file mode 100644
index 00000000..e9a8aff8
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1990_Inadequate Safety Measures.json
@@ -0,0 +1,85 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012296,
+ "Position": {
+ "X": -658.32,
+ "Y": -75.48534,
+ "Z": 699.1531
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -345.30673,
+ "Y": -89.61499,
+ "Z": 353.53
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4075,
+ "MinimumKillCount": 3
+ }
+ ],
+ "CompletionQuestVariablesFlags": [
+ { "Low": 3 },
+ null,
+ null,
+ null,
+ null,
+ null
+ ],
+ "Fly": true
+ },
+ {
+ "Position": {
+ "X": -553.4664,
+ "Y": -104.895905,
+ "Z": 175.68343
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4668,
+ "MinimumKillCount": 3
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012296,
+ "Position": {
+ "X": -658.32,
+ "Y": -75.48534,
+ "Z": 699.1531
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1991_Environmental Unbalance.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1991_Environmental Unbalance.json
new file mode 100644
index 00000000..701a787c
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1991_Environmental Unbalance.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012297,
+ "Position": {
+ "X": -554.95544,
+ "Y": -89.69182,
+ "Z": 771.87756
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -679.16223,
+ "Y": -48.151093,
+ "Z": 542.7097
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 4095,
+ "MinimumKillCount": 4
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012297,
+ "Position": {
+ "X": -554.95544,
+ "Y": -89.69182,
+ "Z": 771.87756
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1992_Elevated Aggression Levels.json b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1992_Elevated Aggression Levels.json
new file mode 100644
index 00000000..b41f31b3
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Azys Lla/1992_Elevated Aggression Levels.json
@@ -0,0 +1,116 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "goatzone",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012298,
+ "Position": {
+ "X": -192.67572,
+ "Y": -102.749916,
+ "Z": 476.9817
+ },
+ "TerritoryId": 402,
+ "InteractionType": "AcceptQuest",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -375.98807,
+ "Y": -106.10026,
+ "Z": 710.48956
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AutoOnEnterArea",
+ "ComplexCombatData": [
+ {
+ "DataId": 4493,
+ "MinimumKillCount": 1
+ }
+ ],
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ],
+ "Fly": true
+ },
+ {
+ "Position": {
+ "X": -372.88766,
+ "Y": -105.78837,
+ "Z": 746.95245
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AutoOnEnterArea",
+ "ComplexCombatData": [
+ {
+ "DataId": 4493,
+ "MinimumKillCount": 1
+ }
+ ],
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "Position": {
+ "X": -321.94852,
+ "Y": -106.57244,
+ "Z": 716.5662
+ },
+ "TerritoryId": 402,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AutoOnEnterArea",
+ "ComplexCombatData": [
+ {
+ "DataId": 4493,
+ "MinimumKillCount": 1
+ }
+ ],
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012298,
+ "Position": {
+ "X": -192.67572,
+ "Y": -102.749916,
+ "Z": 476.9817
+ },
+ "TerritoryId": 402,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1762_The Secret to Success.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1762_The Secret to Success.json
new file mode 100644
index 00000000..449db054
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1762_The Secret to Success.json
@@ -0,0 +1,126 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014133,
+ "Position": {
+ "X": -259.84595,
+ "Y": 126.44779,
+ "Z": 1.9073486
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 2006005,
+ "Position": {
+ "X": -157.70203,
+ "Y": 110.73462,
+ "Z": -52.414795
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 2006006,
+ "Position": {
+ "X": -228.25977,
+ "Y": 112.47424,
+ "Z": -127.55017
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 2006007,
+ "Position": {
+ "X": -348.10413,
+ "Y": 116.50256,
+ "Z": -91.93567
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014133,
+ "Position": {
+ "X": -259.84595,
+ "Y": 126.44779,
+ "Z": 1.9073486
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1764
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1764_Introductory Dragonslaying.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1764_Introductory Dragonslaying.json
new file mode 100644
index 00000000..c526f673
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1764_Introductory Dragonslaying.json
@@ -0,0 +1,98 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014134,
+ "Position": {
+ "X": -293.23267,
+ "Y": 126.85495,
+ "Z": 5.2643433
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014136,
+ "Position": {
+ "X": 292.74426,
+ "Y": 132.44626,
+ "Z": -244.73944
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "Position": {
+ "X": 289.0053,
+ "Y": 132.30807,
+ "Z": -233.57523
+ },
+ "DataId": 4041,
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AfterInteraction",
+ "KillEnemyDataIds": [
+ 4041
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014136,
+ "Position": {
+ "X": 292.74426,
+ "Y": 132.44626,
+ "Z": -244.73944
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1765
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1765_Intermediate Dragonslaying.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1765_Intermediate Dragonslaying.json
new file mode 100644
index 00000000..4bea2439
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1765_Intermediate Dragonslaying.json
@@ -0,0 +1,99 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014138,
+ "Position": {
+ "X": -219.19586,
+ "Y": 112.21238,
+ "Z": -244.1596
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 4470,
+ "Position": {
+ "X": -468.44992,
+ "Y": 93.85853,
+ "Z": -506.40417
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AutoOnEnterArea",
+ "KillEnemyDataIds": [
+ 4470
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "Position": {
+ "X": -219.1624,
+ "Y": 112.29031,
+ "Z": -239.88861
+ },
+ "TerritoryId": 397,
+ "InteractionType": "WalkTo",
+ "Fly": true
+ },
+ {
+ "DataId": 1014138,
+ "Position": {
+ "X": -219.19586,
+ "Y": 112.21238,
+ "Z": -244.1596
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1766,
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1766_Advanced Dragonslaying.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1766_Advanced Dragonslaying.json
new file mode 100644
index 00000000..aadaebf0
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1766_Advanced Dragonslaying.json
@@ -0,0 +1,107 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014138,
+ "Position": {
+ "X": -219.19586,
+ "Y": 112.21238,
+ "Z": -244.1596
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014140,
+ "Position": {
+ "X": -472.19043,
+ "Y": 93.87282,
+ "Z": -503.7156
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 4468,
+ "Position": {
+ "X": -696.8278,
+ "Y": 100.08534,
+ "Z": -603.926
+ },
+ "StopDistance": 0.5,
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "KillEnemyDataIds": [
+ 4468
+ ],
+ "EnemySpawnType": "AutoOnEnterArea",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 1014140,
+ "Position": {
+ "X": -472.19043,
+ "Y": 93.87282,
+ "Z": -503.7156
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1769
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1769_Expert Dragonslaying.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1769_Expert Dragonslaying.json
new file mode 100644
index 00000000..41a0e1d5
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1769_Expert Dragonslaying.json
@@ -0,0 +1,185 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014142,
+ "Position": {
+ "X": -290.33344,
+ "Y": 76.98337,
+ "Z": -259.93744
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 2006013,
+ "Position": {
+ "X": -287.76996,
+ "Y": 77.74463,
+ "Z": -278.00415
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014142,
+ "Position": {
+ "X": -290.33344,
+ "Y": 76.98337,
+ "Z": -259.93744
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 2006014,
+ "Position": {
+ "X": -291.8593,
+ "Y": 76.98169,
+ "Z": -261.0056
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 4037,
+ "Position": {
+ "X": -285.1214,
+ "Y": 76.98337,
+ "Z": -275.5287
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "KillEnemyDataIds": [4037],
+ "EnemySpawnType": "AfterInteraction"
+ }
+ ]
+ },
+ {
+ "Sequence": 5,
+ "Steps": [
+ {
+ "DataId": 1014142,
+ "Position": {
+ "X": -290.33344,
+ "Y": 76.98337,
+ "Z": -259.93744
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 6,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 7,
+ "Steps": [
+ {
+ "DataId": 1014144,
+ "Position": {
+ "X": -258.16742,
+ "Y": 126.98671,
+ "Z": 12.77179
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 8,
+ "Steps": [
+ {
+ "DataId": 2006015,
+ "Position": {
+ "X": -252.33844,
+ "Y": 127.00073,
+ "Z": 11.093262
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 9,
+ "Steps": [
+ {
+ "DataId": 1014144,
+ "Position": {
+ "X": -258.16742,
+ "Y": 126.98671,
+ "Z": 12.77179
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1889_Unknown Ultimatum.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1889_Unknown Ultimatum.json
new file mode 100644
index 00000000..2072032e
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1889_Unknown Ultimatum.json
@@ -0,0 +1,121 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014147,
+ "Position": {
+ "X": -256.97723,
+ "Y": 126.99508,
+ "Z": 13.168518
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "Position": {
+ "X": -298.8484,
+ "Y": 221.68138,
+ "Z": 548.0529
+ },
+ "TerritoryId": 397,
+ "InteractionType": "WalkTo"
+ },
+ {
+ "Land": true,
+ "DataId": 1014149,
+ "Position": {
+ "X": -294.5144,
+ "Y": 221.58685,
+ "Z": 545.922
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 2006017,
+ "Position": {
+ "X": -253.34558,
+ "Y": 221.36255,
+ "Z": 528.92346
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 4076,
+ "Position": {
+ "X": -257.5558,
+ "Y": 221.35532,
+ "Z": 520.9053
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "KillEnemyDataIds": [4076],
+ "EnemySpawnType": "AfterInteraction"
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 1014149,
+ "Position": {
+ "X": -294.5144,
+ "Y": 221.58685,
+ "Z": 545.922
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1890
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1890_Personal Effects.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1890_Personal Effects.json
new file mode 100644
index 00000000..68341985
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1890_Personal Effects.json
@@ -0,0 +1,92 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Position": {
+ "X": -297.49585,
+ "Y": 219.87524,
+ "Z": 281.65045
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "OverworldEnemies",
+ "ComplexCombatData": [
+ {
+ "DataId": 3992,
+ "MinimumKillCount": 5
+ }
+ ],
+ "Fly": true
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014150,
+ "Position": {
+ "X": 507.13354,
+ "Y": 217.95148,
+ "Z": 791.37854
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "AetheryteShortcut": "Coerthas Western Highlands - Falcon's Nest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Position": {
+ "X": -110.26132,
+ "Y": 153.61101,
+ "Z": 10.394781
+ },
+ "TerritoryId": 397,
+ "InteractionType": "WalkTo",
+ "Fly": true,
+ "SkipConditions": {
+ "StepIf": {
+ "Flying": "Locked"
+ }
+ }
+ },
+ {
+ "Fly": true,
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1891
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1891_Ayleth Absconds.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1891_Ayleth Absconds.json
new file mode 100644
index 00000000..03490c14
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1891_Ayleth Absconds.json
@@ -0,0 +1,98 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014151,
+ "Position": {
+ "X": -156.08453,
+ "Y": 219.14235,
+ "Z": 669.7031
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 732,
+ "Position": {
+ "X": -151.29321,
+ "Y": 218.95082,
+ "Z": 669.4895
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AfterInteraction",
+ "KillEnemyDataIds": [
+ 732
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 1014151,
+ "Position": {
+ "X": -156.08453,
+ "Y": 219.14235,
+ "Z": 669.7031
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014151,
+ "Position": {
+ "X": -156.08453,
+ "Y": 219.14235,
+ "Z": 669.7031
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1892
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1892_A Missing Tooth.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1892_A Missing Tooth.json
new file mode 100644
index 00000000..d0ee8c1c
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1892_A Missing Tooth.json
@@ -0,0 +1,83 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 2006019,
+ "Position": {
+ "X": -152.42242,
+ "Y": 219.62305,
+ "Z": 669.94727
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 4782,
+ "Position": {
+ "X": -158.97404,
+ "Y": 219.45131,
+ "Z": 672.79553
+ },
+ "TerritoryId": 397,
+ "KillEnemyDataIds": [
+ 4782
+ ],
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AfterInteraction"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 2006181,
+ "Position": {
+ "X": -152.42242,
+ "Y": 219.62305,
+ "Z": 669.94727
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1893
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1893_A Noble Purpose.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1893_A Noble Purpose.json
new file mode 100644
index 00000000..c5a003fc
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1893_A Noble Purpose.json
@@ -0,0 +1,113 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014146,
+ "Position": {
+ "X": -258.74725,
+ "Y": 126.98546,
+ "Z": 12.649658
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1011907,
+ "Position": {
+ "X": -288.8686,
+ "Y": 127.06639,
+ "Z": 13.199036
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 1011910,
+ "Position": {
+ "X": -298.26813,
+ "Y": 126.67049,
+ "Z": -1.4191895
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 1014133,
+ "Position": {
+ "X": -259.84595,
+ "Y": 126.44779,
+ "Z": 1.9073486
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014152,
+ "Position": {
+ "X": 496.1776,
+ "Y": 133.93082,
+ "Z": -862.2416
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014153,
+ "Position": {
+ "X": -295.82666,
+ "Y": 126.83744,
+ "Z": 3.829956
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 1898
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1898_A War without End.json b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1898_A War without End.json
new file mode 100644
index 00000000..fb968727
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Coerthas Western Highlands/1898_A War without End.json
@@ -0,0 +1,229 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1014153,
+ "Position": {
+ "X": -295.82666,
+ "Y": 126.83744,
+ "Z": 3.829956
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1014154,
+ "Position": {
+ "X": -519.1577,
+ "Y": 119.39417,
+ "Z": -161.24213
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 1014155,
+ "Position": {
+ "X": -527.245,
+ "Y": 118.94699,
+ "Z": -163.13422
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 5,
+ "Steps": [
+ {
+ "DataId": 2006026,
+ "Position": {
+ "X": -525.7191,
+ "Y": 119.06604,
+ "Z": -165.72827
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 6,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 7,
+ "Steps": [
+ {
+ "Fly": true,
+ "Position": {
+ "X": -92.57648,
+ "Y": 96.33008,
+ "Z": -645.1057
+ },
+ "TerritoryId": 397,
+ "InteractionType": "WalkTo"
+ },
+ {
+ "DataId": 2006027,
+ "Position": {
+ "X": -92.57648,
+ "Y": 96.33008,
+ "Z": -645.1057
+ },
+ "TerritoryId": 397,
+ "InteractionType": "UseItem",
+ "ItemId": 2001751
+ }
+ ]
+ },
+ {
+ "Sequence": 8,
+ "Steps": [
+ {
+ "DataId": 2006028,
+ "Position": {
+ "X": -92.5155,
+ "Y": 93.52246,
+ "Z": -623.621
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 4621,
+ "Position": {
+ "X": -92.37683,
+ "Y": 95.76039,
+ "Z": -640.7043
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Combat",
+ "EnemySpawnType": "AfterInteraction",
+ "KillEnemyDataIds": [
+ 4621
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 9,
+ "Steps": [
+ {
+ "DataId": 2006460,
+ "Position": {
+ "X": -92.57648,
+ "Y": 96.33008,
+ "Z": -645.1057
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 10,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 11,
+ "Steps": [
+ {
+ "DataId": 1014147,
+ "Position": {
+ "X": -256.97723,
+ "Y": 126.99508,
+ "Z": 13.168518
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1013710,
+ "Position": {
+ "X": -294.6975,
+ "Y": 126.84874,
+ "Z": 4.5318604
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1730_Cold Days, Colder Nights.json b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1730_Cold Days, Colder Nights.json
new file mode 100644
index 00000000..0be23e3f
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1730_Cold Days, Colder Nights.json
@@ -0,0 +1,140 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012170,
+ "Position": {
+ "X": 102.92212,
+ "Y": 3.6299734,
+ "Z": 65.56799
+ },
+ "TerritoryId": 418,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1012162,
+ "Position": {
+ "X": 135.97314,
+ "Y": 24.376427,
+ "Z": 12.619202
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 1012170,
+ "Position": {
+ "X": 102.92212,
+ "Y": 3.6299734,
+ "Z": 65.56799
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014711,
+ "Position": {
+ "X": 58.03003,
+ "Y": -7.146736,
+ "Z": 82.41394
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 1014712,
+ "Position": {
+ "X": 93.91919,
+ "Y": -19.941168,
+ "Z": 78.20239
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 1014713,
+ "Position": {
+ "X": 131.9447,
+ "Y": -20.000105,
+ "Z": 62.027832
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 1014714,
+ "Position": {
+ "X": 23.178406,
+ "Y": -12.020877,
+ "Z": 35.294067
+ },
+ "TerritoryId": 418,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012170,
+ "Position": {
+ "X": 102.92212,
+ "Y": 3.6299734,
+ "Z": 65.56799
+ },
+ "TerritoryId": 418,
+ "InteractionType": "CompleteQuest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1870_Caught in the Act.json b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1870_Caught in the Act.json
new file mode 100644
index 00000000..2016dfb6
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1870_Caught in the Act.json
@@ -0,0 +1,149 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012180,
+ "Position": {
+ "X": -174.18176,
+ "Y": -12.555469,
+ "Z": -21.561035
+ },
+ "TerritoryId": 419,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 2006246,
+ "Position": {
+ "X": -218.40247,
+ "Y": -16.037292,
+ "Z": -34.683777
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 2006247,
+ "Position": {
+ "X": -229.23633,
+ "Y": -20.035156,
+ "Z": -83.05487
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 2006248,
+ "Position": {
+ "X": -252.1554,
+ "Y": -20.035156,
+ "Z": -57.66388
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 2,
+ "Steps": [
+ {
+ "DataId": 1012180,
+ "Position": {
+ "X": -174.18176,
+ "Y": -12.555469,
+ "Z": -21.561035
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact"
+ }
+ ]
+
+ },
+ {
+ "Sequence": 3,
+ "Steps": [
+ {
+ "DataId": 1014721,
+ "Position": {
+ "X": 119.31018,
+ "Y": -12.634913,
+ "Z": -13.626343
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact",
+ "AethernetShortcut": [
+ "[Ishgard] The Jeweled Crozier",
+ "[Ishgard] Athenaeum Astrologicum"
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 4,
+ "Steps": [
+ {
+ "DataId": 2006330,
+ "Position": {
+ "X": 118.791504,
+ "Y": -11.6427,
+ "Z": -13.351685
+ },
+ "TerritoryId": 419,
+ "InteractionType": "Interact"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1012180,
+ "Position": {
+ "X": -174.18176,
+ "Y": -12.555469,
+ "Z": -21.561035
+ },
+ "TerritoryId": 419,
+ "InteractionType": "CompleteQuest",
+ "AethernetShortcut": [
+ "[Ishgard] Athenaeum Astrologicum",
+ "[Ishgard] The Jeweled Crozier"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1873_Enlisted.json b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1873_Enlisted.json
new file mode 100644
index 00000000..c603466d
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/Ishgard/1873_Enlisted.json
@@ -0,0 +1,68 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1014718,
+ "Position": {
+ "X": -29.526245,
+ "Y": 11.965078,
+ "Z": 48.355713
+ },
+ "TerritoryId": 419,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "DataId": 1011231,
+ "Position": {
+ "X": 503.1051,
+ "Y": 217.95148,
+ "Z": 790.2189
+ },
+ "TerritoryId": 397,
+ "InteractionType": "Interact",
+ "AetheryteShortcut": "Coerthas Western Highlands - Falcon's Nest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Position": {
+ "X": -110.26132,
+ "Y": 153.61101,
+ "Z": 10.394781
+ },
+ "TerritoryId": 397,
+ "InteractionType": "WalkTo",
+ "Fly": true,
+ "SkipConditions": {
+ "StepIf": {
+ "Flying": "Locked"
+ }
+ }
+ },
+ {
+ "DataId": 1014719,
+ "Position": {
+ "X": -293.6294,
+ "Y": 125.4389,
+ "Z": -19.058533
+ },
+ "TerritoryId": 397,
+ "InteractionType": "CompleteQuest",
+ "Fly": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/QuestPaths/3.x - Heavensward/Side Quests/The Churning Mists/1840_A Secret from Everyone.json b/QuestPaths/3.x - Heavensward/Side Quests/The Churning Mists/1840_A Secret from Everyone.json
new file mode 100644
index 00000000..e5351603
--- /dev/null
+++ b/QuestPaths/3.x - Heavensward/Side Quests/The Churning Mists/1840_A Secret from Everyone.json
@@ -0,0 +1,134 @@
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "Thaksin",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1012085,
+ "Position": {
+ "X": 286.88477,
+ "Y": 14.36517,
+ "Z": 645.1666
+ },
+ "TerritoryId": 400,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+
+ {
+ "Sequence": 1,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 2005720,
+ "Position": {
+ "X": 565.9419,
+ "Y": -9.445435,
+ "Z": -14.328308
+ },
+ "TerritoryId": 400,
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 16
+ ],
+ "InteractionType": "Interact"
+ },
+ {
+ "DataId": 2005718,
+ "Position": {
+ "X": 664.39294,
+ "Y": -0.4730835,
+ "Z": -3.2807007
+ },
+ "Fly": true,
+ "TerritoryId": 400,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 64
+ ]
+ },
+ {
+ "DataId": 2005721,
+ "Position": {
+ "X": 653.83374,
+ "Y": -0.7172241,
+ "Z": -70.02368
+ },
+ "TerritoryId": 400,
+ "Fly": true,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null, null,
+ null, null,
+ null, 8
+ ]
+ },
+ {
+ "DataId": 2005717,
+ "Position": {
+ "X": 639.00195,
+ "Y": 46.463623,
+ "Z": -113.05414
+ },
+ "TerritoryId": 400,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 128
+ ]
+ },
+ {
+ "DataId": 2005719,
+ "Position": {
+ "X": 527.0923,
+ "Y": -4.287842,
+ "Z": -84.916504
+ },
+ "Fly": true,
+ "TerritoryId": 400,
+ "InteractionType": "Interact",
+ "CompletionQuestVariablesFlags": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 32
+ ]
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Fly": true,
+ "DataId": 1012085,
+ "Position": {
+ "X": 286.88477,
+ "Y": 14.36517,
+ "Z": 645.1666
+ },
+ "TerritoryId": 400,
+ "InteractionType": "CompleteQuest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/QuestPaths/5.x - Shadowbringers/Allied Societies/Qitari/Dailies/3833_Qhoterl's Chronicles.json b/QuestPaths/5.x - Shadowbringers/Allied Societies/Qitari/Dailies/3833_Qhoterl's Chronicles.json
index bdbb1696..864bef9b 100644
--- a/QuestPaths/5.x - Shadowbringers/Allied Societies/Qitari/Dailies/3833_Qhoterl's Chronicles.json
+++ b/QuestPaths/5.x - Shadowbringers/Allied Societies/Qitari/Dailies/3833_Qhoterl's Chronicles.json
@@ -1,7 +1,6 @@
-{
+{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
- "Author": "liza",
- "Disabled": true,
+ "Author": "pot0to",
"QuestSequence": [
{
"Sequence": 0,
@@ -9,9 +8,9 @@
{
"DataId": 1032643,
"Position": {
- "X": 787.0145,
- "Y": -45.82774,
- "Z": -218.555
+ "X": 787.0145,
+ "Y": -45.82774,
+ "Z": -218.555
},
"TerritoryId": 817,
"InteractionType": "AcceptQuest"
@@ -22,26 +21,56 @@
"Sequence": 255,
"Steps": [
{
+ "DataId": 33005,
"Position": {
- "X": 788.1569,
- "Y": -45.82557,
- "Z": -212.9306
+ "X": 214.7744,
+ "Y": -19.57758,
+ "Z": 623.6767
},
"TerritoryId": 817,
- "InteractionType": "WalkTo",
- "AetheryteShortcut": "Rak'tika - Fanow",
+ "InteractionType": "Gather",
+ "RequiredQuestAcceptedJob": [
+ "Miner"
+ ],
+ "ItemsToGather": [
+ {
+ "ItemId": 29537,
+ "ItemCount": 3
+ }
+ ],
+ "Fly": true
+ },
+ {
+ "DataId": 33020,
+ "Position": {
+ "X": 226.1767,
+ "Y": -20.10281,
+ "Z": 643.5543
+ },
+ "TerritoryId": 817,
+ "InteractionType": "Gather",
+ "RequiredQuestAcceptedJob": [
+ "Botanist"
+ ],
+ "ItemsToGather": [
+ {
+ "ItemId": 29563,
+ "ItemCount": 3
+ }
+ ],
"Fly": true
},
{
"DataId": 1032643,
"Position": {
- "X": 787.0145,
- "Y": -45.82774,
- "Z": -218.555
+ "X": 787.0145,
+ "Y": -45.82774,
+ "Z": -218.555
},
- "StopDistance": 7,
"TerritoryId": 817,
- "InteractionType": "CompleteQuest"
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Rak'tika - Fanow",
+ "Fly": true
}
]
}
diff --git a/QuestPaths/7.x - Dawntrail/Side Quests/Kozama'uka/5083_The Wind's Blessing.json b/QuestPaths/7.x - Dawntrail/Side Quests/Kozama'uka/5083_The Wind's Blessing.json
index 903ee73e..c02008d7 100644
--- a/QuestPaths/7.x - Dawntrail/Side Quests/Kozama'uka/5083_The Wind's Blessing.json
+++ b/QuestPaths/7.x - Dawntrail/Side Quests/Kozama'uka/5083_The Wind's Blessing.json
@@ -96,6 +96,7 @@
},
"TerritoryId": 1188,
"InteractionType": "CompleteQuest",
+ "Fly": true,
"NextQuestId": 5084
}
]
diff --git a/QuestPaths/quest-v1.json b/QuestPaths/quest-v1.json
index d04fd82c..f0f82637 100644
--- a/QuestPaths/quest-v1.json
+++ b/QuestPaths/quest-v1.json
@@ -80,7 +80,7 @@
"null"
],
"description": "Set if pathfinding should stop closer or further away from the default stop distance",
- "exclusiveMinimum": 0
+ "minimum": 0.25
},
"IgnoreDistanceToObject": {
"type": "boolean",
@@ -183,7 +183,8 @@
"null"
],
"description": "The Item to use",
- "exclusiveMinimum": 0
+ "exclusiveMinimum": 0,
+ "maximum": 2010000
},
"SkipConditions": {
"type": "object",
@@ -648,13 +649,15 @@
"type": "object",
"properties": {
"ItemId": {
- "type": "integer"
+ "type": "integer",
+ "maximum": 2010000
},
"Condition": {
"type": "string",
"enum": [
"Incapacitated",
- "Health%"
+ "Health%",
+ "MissingStatus"
]
},
"Value": {
diff --git a/Questionable.Model/Questing/Converter/CombatItemUseConditionConverter.cs b/Questionable.Model/Questing/Converter/CombatItemUseConditionConverter.cs
index 1b4086ff..deadc4f6 100644
--- a/Questionable.Model/Questing/Converter/CombatItemUseConditionConverter.cs
+++ b/Questionable.Model/Questing/Converter/CombatItemUseConditionConverter.cs
@@ -9,5 +9,6 @@ public sealed class CombatItemUseConditionConverter() : EnumConverter? _previousQuestVariables;
public CombatController(
IEnumerable combatModules,
@@ -79,7 +82,9 @@ internal sealed class CombatController : IDisposable
Data = combatData,
LastDistanceCheck = DateTime.Now,
};
- _wasInCombat = combatData.SpawnType is EEnemySpawnType.QuestInterruption or EEnemySpawnType.FinishCombatIfAny;
+ _wasInCombat =
+ combatData.SpawnType is EEnemySpawnType.QuestInterruption or EEnemySpawnType.FinishCombatIfAny;
+ UpdateLastTargetAndQuestVariables(null);
return true;
}
else
@@ -115,7 +120,31 @@ internal sealed class CombatController : IDisposable
{
// wait until the game cleans up the target
if (lastTarget.IsDead)
- return EStatus.InCombat;
+ {
+ ElementId? elementId = _currentFight.Data.ElementId;
+ QuestProgressInfo? questProgressInfo = elementId != null
+ ? _questFunctions.GetQuestProgressInfo(elementId)
+ : null;
+
+ if (questProgressInfo != null &&
+ questProgressInfo.Sequence == _currentFight.Data.Sequence &&
+ QuestWorkUtils.HasCompletionFlags(_currentFight.Data.CompletionQuestVariablesFlags) &&
+ QuestWorkUtils.MatchesQuestWork(_currentFight.Data.CompletionQuestVariablesFlags,
+ questProgressInfo))
+ {
+ // would be the final enemy of the bunch
+ return EStatus.InCombat;
+ }
+ else if (questProgressInfo != null &&
+ questProgressInfo.Sequence == _currentFight.Data.Sequence &&
+ _previousQuestVariables != null &&
+ !questProgressInfo.Variables.SequenceEqual(_previousQuestVariables))
+ {
+ UpdateLastTargetAndQuestVariables(null);
+ }
+ else
+ return EStatus.InCombat;
+ }
}
else
_lastTargetId = null;
@@ -128,7 +157,7 @@ internal sealed class CombatController : IDisposable
{
int currentTargetPriority = GetKillPriority(target);
var nextTarget = FindNextTarget();
- int nextTargetPriority = GetKillPriority(target);
+ int nextTargetPriority = nextTarget != null ? GetKillPriority(nextTarget) : 0;
if (nextTarget != null && nextTarget.Equals(target))
{
@@ -147,7 +176,7 @@ internal sealed class CombatController : IDisposable
}
else if (nextTarget != null)
{
- if (nextTargetPriority > currentTargetPriority)
+ if (nextTargetPriority > currentTargetPriority || currentTargetPriority == 0)
SetTarget(nextTarget);
}
else
@@ -372,9 +401,18 @@ internal sealed class CombatController : IDisposable
float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius;
float actualDistance = Vector3.Distance(player.Position, gameObject.Position);
float maxDistance = player.ClassJob.ValueNullable?.Role is 3 or 4 ? 20f : 2.9f;
- if (actualDistance - hitboxOffset >= maxDistance)
+ bool outOfRange = actualDistance - hitboxOffset >= maxDistance;
+ bool isInLineOfSight = IsInLineOfSight(gameObject);
+ if (outOfRange || !isInLineOfSight)
{
- if (actualDistance - hitboxOffset <= 5)
+ bool useNavmesh = actualDistance - hitboxOffset > 5f;
+ if (!outOfRange && !isInLineOfSight)
+ {
+ maxDistance = Math.Min(maxDistance, actualDistance) / 2;
+ useNavmesh = true;
+ }
+
+ if (!useNavmesh)
{
_logger.LogInformation("Moving to {TargetName} ({DataId}) to attack", gameObject.Name,
gameObject.DataId);
@@ -391,6 +429,44 @@ internal sealed class CombatController : IDisposable
}
}
+ internal unsafe bool IsInLineOfSight(IGameObject target)
+ {
+ Vector3 sourcePos = _clientState.LocalPlayer!.Position;
+ sourcePos.Y += 2;
+
+ Vector3 targetPos = target.Position;
+ targetPos.Y += 2;
+
+ Vector3 direction = targetPos - sourcePos;
+ float distance = direction.Length();
+
+ direction = Vector3.Normalize(direction);
+
+ Vector3 originVect = new Vector3(sourcePos.X, sourcePos.Y, sourcePos.Z);
+ Vector3 directionVect = new Vector3(direction.X, direction.Y, direction.Z);
+
+ RaycastHit hit;
+ var flags = stackalloc int[] { 0x4000, 0, 0x4000, 0 };
+ var isLoSBlocked =
+ Framework.Instance()->BGCollisionModule->RaycastMaterialFilter(&hit, &originVect, &directionVect, distance,
+ 1, flags);
+
+ return isLoSBlocked == false;
+ }
+
+ private void UpdateLastTargetAndQuestVariables(IGameObject? target)
+ {
+ _lastTargetId = target?.GameObjectId;
+ _previousQuestVariables = _currentFight!.Data.ElementId != null
+ ? _questFunctions.GetQuestProgressInfo(_currentFight.Data.ElementId)?.Variables
+ : null;
+ /*
+ _logger.LogTrace("UpdateTargetData: {TargetId}; {QuestVariables}",
+ target?.GameObjectId.ToString("X8", CultureInfo.InvariantCulture) ?? "null",
+ _previousQuestVariables != null ? string.Join(", ", _previousQuestVariables) : "null");
+ */
+ }
+
public void Stop(string label)
{
using var scope = _logger.BeginScope(label);
@@ -422,6 +498,8 @@ internal sealed class CombatController : IDisposable
public sealed class CombatData
{
public required ElementId? ElementId { get; init; }
+ public required int Sequence { get; init; }
+ public required IList CompletionQuestVariablesFlags { get; init; }
public required EEnemySpawnType SpawnType { get; init; }
public required List KillEnemyDataIds { get; init; }
public required List ComplexCombatDatas { get; init; }
diff --git a/Questionable/Controller/CombatModules/ItemUseModule.cs b/Questionable/Controller/CombatModules/ItemUseModule.cs
index 26a92ff3..95dbea56 100644
--- a/Questionable/Controller/CombatModules/ItemUseModule.cs
+++ b/Questionable/Controller/CombatModules/ItemUseModule.cs
@@ -108,6 +108,7 @@ internal sealed class ItemUseModule : ICombatModule
_delegate.Stop();
unsafe
{
+ _logger.LogInformation("Using item {ItemId}", _combatData.CombatItemUse.ItemId);
AgentInventoryContext.Instance()->UseItem(_combatData.CombatItemUse.ItemId);
}
_continueAt = DateTime.Now.AddSeconds(2);
@@ -147,6 +148,9 @@ internal sealed class ItemUseModule : ICombatModule
if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.HealthPercent)
return (100f * battleChara->Health / battleChara->MaxHealth) < _combatData.CombatItemUse.Value;
+
+ if (_combatData.CombatItemUse.Condition == ECombatItemUseCondition.MissingStatus)
+ return !battleChara->StatusManager.HasStatus((uint)_combatData.CombatItemUse.Value);
}
return false;
diff --git a/Questionable/Controller/GatheringController.cs b/Questionable/Controller/GatheringController.cs
index bbe6d2e4..28de31f9 100644
--- a/Questionable/Controller/GatheringController.cs
+++ b/Questionable/Controller/GatheringController.cs
@@ -49,9 +49,10 @@ internal sealed unsafe class GatheringController : MiniTaskController logger,
ICondition condition,
IServiceProvider serviceProvider,
+ InterruptHandler interruptHandler,
IDataManager dataManager,
IPluginLog pluginLog)
- : base(chatGui, condition, serviceProvider, dataManager, logger)
+ : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
{
_movementController = movementController;
_gatheringPointRegistry = gatheringPointRegistry;
diff --git a/Questionable/Controller/InterruptHandler.cs b/Questionable/Controller/InterruptHandler.cs
new file mode 100644
index 00000000..9171432f
--- /dev/null
+++ b/Questionable/Controller/InterruptHandler.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using Dalamud.Game;
+using Dalamud.Hooking;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.Game.Character;
+using FFXIVClientStructs.FFXIV.Common.Math;
+using JetBrains.Annotations;
+using Microsoft.Extensions.Logging;
+using Questionable.Data;
+
+namespace Questionable.Controller;
+
+internal sealed unsafe class InterruptHandler : IDisposable
+{
+ private readonly Hook _processActionEffectHook;
+ private readonly IClientState _clientState;
+ private readonly TerritoryData _territoryData;
+ private readonly ILogger _logger;
+
+ private delegate void ProcessActionEffect(uint sourceId, Character* sourceCharacter, Vector3* pos,
+ EffectHeader* effectHeader, EffectEntry* effectArray, ulong* effectTail);
+
+ public InterruptHandler(IGameInteropProvider gameInteropProvider, IClientState clientState,
+ TerritoryData territoryData, ILogger logger)
+ {
+ _clientState = clientState;
+ _territoryData = territoryData;
+ _logger = logger;
+ _processActionEffectHook =
+ gameInteropProvider.HookFromSignature(Signatures.ActionEffect,
+ HandleProcessActionEffect);
+ _processActionEffectHook.Enable();
+ }
+
+ public event EventHandler? Interrupted;
+
+ private void HandleProcessActionEffect(uint sourceId, Character* sourceCharacter, Vector3* pos,
+ EffectHeader* effectHeader, EffectEntry* effectArray, ulong* effectTail)
+ {
+ try
+ {
+ if (!_territoryData.IsDutyInstance(_clientState.TerritoryType))
+ {
+ for (int i = 0; i < effectHeader->TargetCount; i++)
+ {
+ uint targetId = (uint)(effectTail[i] & uint.MaxValue);
+ EffectEntry* effect = effectArray + 8 * i;
+
+ if (targetId == _clientState.LocalPlayer?.GameObjectId &&
+ effect->Type is EActionEffectType.Damage or EActionEffectType.BlockedDamage
+ or EActionEffectType.ParriedDamage)
+ {
+ _logger.LogTrace("Damage action effect on self, from {SourceId} ({EffectType})", sourceId,
+ effect->Type);
+ Interrupted?.Invoke(this, EventArgs.Empty);
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.LogWarning(e, "Unable to process action effect");
+ }
+ finally
+ {
+ _processActionEffectHook.Original(sourceId, sourceCharacter, pos, effectHeader, effectArray, effectTail);
+ }
+ }
+
+ public void Dispose()
+ {
+ _processActionEffectHook.Disable();
+ _processActionEffectHook.Dispose();
+ }
+
+ private static class Signatures
+ {
+ internal const string ActionEffect = "40 ?? 56 57 41 ?? 41 ?? 41 ?? 48 ?? ?? ?? ?? ?? ?? ?? 48";
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct EffectEntry
+ {
+ [FieldOffset(0)] public EActionEffectType Type;
+ [FieldOffset(1)] public byte Param0;
+ [FieldOffset(2)] public byte Param1;
+ [FieldOffset(3)] public byte Param2;
+ [FieldOffset(4)] public byte Mult;
+ [FieldOffset(5)] public byte Flags;
+ [FieldOffset(6)] public ushort Value;
+
+ public byte AttackType => (byte)(Param1 & 0xF);
+ public uint Damage => Mult == 0 ? Value : Value + ((uint)ushort.MaxValue + 1) * Mult;
+
+ public override string ToString()
+ {
+ return
+ $"Type: {Type}, p0: {Param0:D3}, p1: {Param1:D3}, p2: {Param2:D3} 0x{Param2:X2} '{Convert.ToString(Param2, 2).PadLeft(8, '0')}', mult: {Mult:D3}, flags: {Flags:D3} | {Convert.ToString(Flags, 2).PadLeft(8, '0')}, value: {Value:D6} ATTACK TYPE: {AttackType}";
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct EffectHeader
+ {
+ [FieldOffset(0)] public ulong AnimationTargetId;
+ [FieldOffset(8)] public uint ActionID;
+ [FieldOffset(12)] public uint GlobalEffectCounter;
+ [FieldOffset(16)] public float AnimationLockTime;
+ [FieldOffset(20)] public uint SomeTargetID;
+ [FieldOffset(24)] public ushort SourceSequence;
+ [FieldOffset(26)] public ushort Rotation;
+ [FieldOffset(28)] public ushort AnimationId;
+ [FieldOffset(30)] public byte Variation;
+ [FieldOffset(31)] public ActionType ActionType;
+ [FieldOffset(33)] public byte TargetCount;
+ }
+
+ [UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ private enum EActionEffectType : byte
+ {
+ None = 0,
+ Miss = 1,
+ FullResist = 2,
+ Damage = 3,
+ Heal = 4,
+ BlockedDamage = 5,
+ ParriedDamage = 6,
+ Invulnerable = 7,
+ NoEffectText = 8,
+ Unknown0 = 9,
+ MpLoss = 10,
+ MpGain = 11,
+ TpLoss = 12,
+ TpGain = 13,
+ ApplyStatusEffectTarget = 14,
+ ApplyStatusEffectSource = 15,
+ RecoveredFromStatusEffect = 16,
+ LoseStatusEffectTarget = 17,
+ LoseStatusEffectSource = 18,
+ StatusNoEffect = 20,
+ ThreatPosition = 24,
+ EnmityAmountUp = 25,
+ EnmityAmountDown = 26,
+ StartActionCombo = 27,
+ ComboSucceed = 28,
+ Retaliation = 29,
+ Knockback = 32,
+ Attract1 = 33, //Here is an issue bout knockback. some is 32 some is 33.
+ Attract2 = 34,
+ Mount = 40,
+ FullResistStatus = 52,
+ FullResistStatus2 = 55,
+ VFX = 59,
+ Gauge = 60,
+ JobGauge = 61,
+ SetModelState = 72,
+ SetHP = 73,
+ PartialInvulnerable = 74,
+ Interrupt = 75,
+ }
+}
diff --git a/Questionable/Controller/MiniTaskController.cs b/Questionable/Controller/MiniTaskController.cs
index 06e5d874..c9aec197 100644
--- a/Questionable/Controller/MiniTaskController.cs
+++ b/Questionable/Controller/MiniTaskController.cs
@@ -17,26 +17,29 @@ using Mount = Questionable.Controller.Steps.Common.Mount;
namespace Questionable.Controller;
-internal abstract class MiniTaskController
+internal abstract class MiniTaskController : IDisposable
{
protected readonly TaskQueue _taskQueue = new();
private readonly IChatGui _chatGui;
private readonly ICondition _condition;
private readonly IServiceProvider _serviceProvider;
+ private readonly InterruptHandler _interruptHandler;
private readonly ILogger _logger;
private readonly string _actionCanceledText;
protected MiniTaskController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider,
- IDataManager dataManager, ILogger logger)
+ InterruptHandler interruptHandler, IDataManager dataManager, ILogger logger)
{
_chatGui = chatGui;
_logger = logger;
_serviceProvider = serviceProvider;
+ _interruptHandler = interruptHandler;
_condition = condition;
_actionCanceledText = dataManager.GetString(1314, x => x.Text)!;
+ _interruptHandler.Interrupted += HandleInterruption;
}
protected virtual void UpdateCurrentTask()
@@ -173,7 +176,7 @@ internal abstract class MiniTaskController
if (_condition[ConditionFlag.Mounted])
tasks.Add(new Mount.UnmountTask());
- tasks.Add(Combat.Factory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], [], null));
+ tasks.Add(Combat.Factory.CreateTask(null, -1, false, EEnemySpawnType.QuestInterruption, [], [], [], null));
tasks.Add(new WaitAtEnd.WaitDelay());
_taskQueue.InterruptWith(tasks);
}
@@ -198,8 +201,21 @@ internal abstract class MiniTaskController
if (!isHandled)
{
if (GameFunctions.GameStringEquals(_actionCanceledText, message.TextValue) &&
- !_condition[ConditionFlag.InFlight])
+ !_condition[ConditionFlag.InFlight] &&
+ _taskQueue.CurrentTaskExecutor?.ShouldInterruptOnDamage() == true)
InterruptQueueWithCombat();
}
}
+
+ protected virtual void HandleInterruption(object? sender, EventArgs e)
+ {
+ if (!_condition[ConditionFlag.InFlight] &&
+ _taskQueue.CurrentTaskExecutor?.ShouldInterruptOnDamage() == true)
+ InterruptQueueWithCombat();
+ }
+
+ public virtual void Dispose()
+ {
+ _interruptHandler.Interrupted -= HandleInterruption;
+ }
}
diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs
index 9d17fed1..d514f267 100644
--- a/Questionable/Controller/QuestController.cs
+++ b/Questionable/Controller/QuestController.cs
@@ -75,8 +75,9 @@ internal sealed class QuestController : MiniTaskController, IDi
YesAlreadyIpc yesAlreadyIpc,
TaskCreator taskCreator,
IServiceProvider serviceProvider,
+ InterruptHandler interruptHandler,
IDataManager dataManager)
- : base(chatGui, condition, serviceProvider, dataManager, logger)
+ : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
{
_clientState = clientState;
_gameFunctions = gameFunctions;
@@ -801,11 +802,23 @@ internal sealed class QuestController : MiniTaskController, IDi
_gatheringController.OnNormalToast(message);
}
- public void Dispose()
+ protected override void HandleInterruption(object? sender, EventArgs e)
+ {
+ if (!IsRunning)
+ return;
+
+ if (AutomationType == EAutomationType.Manual)
+ return;
+
+ base.HandleInterruption(sender, e);
+ }
+
+ public override void Dispose()
{
_toastGui.ErrorToast -= OnErrorToast;
_toastGui.Toast -= OnNormalToast;
_condition.ConditionChange -= OnConditionChange;
+ base.Dispose();
}
public sealed record StepProgress(
diff --git a/Questionable/Controller/QuestRegistry.cs b/Questionable/Controller/QuestRegistry.cs
index d6641073..7808b095 100644
--- a/Questionable/Controller/QuestRegistry.cs
+++ b/Questionable/Controller/QuestRegistry.cs
@@ -234,7 +234,11 @@ internal sealed class QuestRegistry
public List GetKnownClassJobQuests(EClassJob classJob)
{
- return _questData.GetClassJobQuests(classJob)
+ List allQuests = [.._questData.GetClassJobQuests(classJob)];
+ if (classJob.AsJob() != classJob)
+ allQuests.AddRange(_questData.GetClassJobQuests(classJob.AsJob()));
+
+ return allQuests
.Where(x => IsKnownQuest(x.QuestId))
.ToList();
}
diff --git a/Questionable/Controller/Steps/Common/Mount.cs b/Questionable/Controller/Steps/Common/Mount.cs
index 1e03d8e9..4f35f4d9 100644
--- a/Questionable/Controller/Steps/Common/Mount.cs
+++ b/Questionable/Controller/Steps/Common/Mount.cs
@@ -110,6 +110,8 @@ internal static class Mount
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal enum MountResult
@@ -197,6 +199,8 @@ internal static class Mount
return false;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
public enum EMountIf
diff --git a/Questionable/Controller/Steps/Common/NextQuest.cs b/Questionable/Controller/Steps/Common/NextQuest.cs
index 7f4b0261..3ac7758d 100644
--- a/Questionable/Controller/Steps/Common/NextQuest.cs
+++ b/Questionable/Controller/Steps/Common/NextQuest.cs
@@ -61,5 +61,7 @@ internal static class NextQuest
}
public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Common/SendNotification.cs b/Questionable/Controller/Steps/Common/SendNotification.cs
index cf116028..6d8bbcec 100644
--- a/Questionable/Controller/Steps/Common/SendNotification.cs
+++ b/Questionable/Controller/Steps/Common/SendNotification.cs
@@ -104,5 +104,7 @@ internal static class SendNotification
}
public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Common/WaitConditionTask.cs b/Questionable/Controller/Steps/Common/WaitConditionTask.cs
index 367fdfec..8203c056 100644
--- a/Questionable/Controller/Steps/Common/WaitConditionTask.cs
+++ b/Questionable/Controller/Steps/Common/WaitConditionTask.cs
@@ -25,5 +25,7 @@ internal static class WaitCondition
return DateTime.Now >= _continueAt ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Gathering/DoGather.cs b/Questionable/Controller/Steps/Gathering/DoGather.cs
index 0f4c8c7f..bf4ab4aa 100644
--- a/Questionable/Controller/Steps/Gathering/DoGather.cs
+++ b/Questionable/Controller/Steps/Gathering/DoGather.cs
@@ -236,6 +236,8 @@ internal static class DoGather
EAction action = PickAction(minerAction, botanistAction);
return ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action) == 0;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
diff --git a/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs b/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
index 2b91f353..fcd5efad 100644
--- a/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
+++ b/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
@@ -198,6 +198,8 @@ internal static class DoGatherCollectable
else
return botanistAction;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
[SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
diff --git a/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs b/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
index 38fa30cd..bc183013 100644
--- a/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
+++ b/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
@@ -59,5 +59,6 @@ internal static class MoveToLandingLocation
public override ETaskResult Update() => moveExecutor.Update();
public bool OnErrorToast(SeString message) => moveExecutor.OnErrorToast(message);
+ public override bool ShouldInterruptOnDamage() => moveExecutor.ShouldInterruptOnDamage();
}
}
diff --git a/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs b/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
index caf2b0f4..0483605b 100644
--- a/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
+++ b/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
@@ -80,5 +80,8 @@ internal static class TurnInDelivery
addon->FireCallback(2, pickGatheringItem);
return ETaskResult.StillRunning;
}
+
+ // not even sure if any turn-in npcs are NEAR mobs; but we also need to be on a gathering/crafting job
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Action.cs b/Questionable/Controller/Steps/Interactions/Action.cs
index 7255fa0b..f7f97502 100644
--- a/Questionable/Controller/Steps/Interactions/Action.cs
+++ b/Questionable/Controller/Steps/Interactions/Action.cs
@@ -124,6 +124,8 @@ internal static class Action
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record UseMudraOnObject(uint DataId, EAction Action) : ITask
@@ -187,5 +189,7 @@ internal static class Action
logger.LogError("Unable to find relevant combo for {Action}", Task.Action);
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/AetherCurrent.cs b/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
index b244bbea..7632e574 100644
--- a/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
+++ b/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
@@ -65,5 +65,7 @@ internal static class AetherCurrent
gameFunctions.IsAetherCurrentUnlocked(Task.AetherCurrentId)
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/AethernetShard.cs b/Questionable/Controller/Steps/Interactions/AethernetShard.cs
index b1af7fe9..db2c5212 100644
--- a/Questionable/Controller/Steps/Interactions/AethernetShard.cs
+++ b/Questionable/Controller/Steps/Interactions/AethernetShard.cs
@@ -53,5 +53,7 @@ internal static class AethernetShard
aetheryteFunctions.IsAetheryteUnlocked(Task.AetheryteLocation)
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Aetheryte.cs b/Questionable/Controller/Steps/Interactions/Aetheryte.cs
index d9754776..dd40fc69 100644
--- a/Questionable/Controller/Steps/Interactions/Aetheryte.cs
+++ b/Questionable/Controller/Steps/Interactions/Aetheryte.cs
@@ -52,5 +52,7 @@ internal static class Aetheryte
aetheryteFunctions.IsAetheryteUnlocked(Task.AetheryteLocation)
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Combat.cs b/Questionable/Controller/Steps/Interactions/Combat.cs
index a4d9594d..e63058a8 100644
--- a/Questionable/Controller/Steps/Interactions/Combat.cs
+++ b/Questionable/Controller/Steps/Interactions/Combat.cs
@@ -102,17 +102,30 @@ internal static class Combat
ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
bool isLastStep = sequence.Steps.Last() == step;
- return CreateTask(quest.Id, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
- step.CompletionQuestVariablesFlags, step.ComplexCombatData, step.CombatItemUse);
+ return CreateTask(quest.Id,
+ sequence.Sequence,
+ isLastStep,
+ step.EnemySpawnType.Value,
+ step.KillEnemyDataIds,
+ step.CompletionQuestVariablesFlags,
+ step.ComplexCombatData,
+ step.CombatItemUse);
}
- internal static Task CreateTask(ElementId? elementId, bool isLastStep, EEnemySpawnType enemySpawnType,
- IList killEnemyDataIds, IList completionQuestVariablesFlags,
- IList complexCombatData, CombatItemUse? combatItemUse)
+ internal static Task CreateTask(ElementId? elementId,
+ int sequence,
+ bool isLastStep,
+ EEnemySpawnType enemySpawnType,
+ IList killEnemyDataIds,
+ IList completionQuestVariablesFlags,
+ IList complexCombatData,
+ CombatItemUse? combatItemUse)
{
return new Task(new CombatController.CombatData
{
ElementId = elementId,
+ Sequence = sequence,
+ CompletionQuestVariablesFlags = completionQuestVariablesFlags,
SpawnType = enemySpawnType,
KillEnemyDataIds = killEnemyDataIds.ToList(),
ComplexCombatDatas = complexCombatData.ToList(),
@@ -177,5 +190,7 @@ internal static class Combat
return ETaskResult.TaskComplete;
}
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Dive.cs b/Questionable/Controller/Steps/Interactions/Dive.cs
index b5389774..eea9cd87 100644
--- a/Questionable/Controller/Steps/Interactions/Dive.cs
+++ b/Questionable/Controller/Steps/Interactions/Dive.cs
@@ -71,6 +71,8 @@ internal static class Dive
return base.Update();
}
+ public override bool ShouldInterruptOnDamage() => false;
+
protected override ETaskResult UpdateInternal()
{
if (condition[ConditionFlag.Diving])
diff --git a/Questionable/Controller/Steps/Interactions/Duty.cs b/Questionable/Controller/Steps/Interactions/Duty.cs
index 5e20accf..b59f8ce7 100644
--- a/Questionable/Controller/Steps/Interactions/Duty.cs
+++ b/Questionable/Controller/Steps/Interactions/Duty.cs
@@ -93,6 +93,8 @@ internal static class Duty
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask
@@ -117,6 +119,8 @@ internal static class Duty
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record OpenDutyFinderTask(uint ContentFinderConditionId) : ITask
@@ -138,5 +142,7 @@ internal static class Duty
}
public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Emote.cs b/Questionable/Controller/Steps/Interactions/Emote.cs
index 085b0356..d6dd7146 100644
--- a/Questionable/Controller/Steps/Interactions/Emote.cs
+++ b/Questionable/Controller/Steps/Interactions/Emote.cs
@@ -51,6 +51,8 @@ internal static class Emote
chatFunctions.UseEmote(Task.DataId, Task.Emote);
return true;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
internal sealed record UseOnSelf(EEmote Emote) : ITask
@@ -65,5 +67,7 @@ internal static class Emote
chatFunctions.UseEmote(Task.Emote);
return true;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/EquipItem.cs b/Questionable/Controller/Steps/Interactions/EquipItem.cs
index f5cd4e11..d761926d 100644
--- a/Questionable/Controller/Steps/Interactions/EquipItem.cs
+++ b/Questionable/Controller/Steps/Interactions/EquipItem.cs
@@ -183,5 +183,7 @@ internal static class EquipItem
return false;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/EquipRecommended.cs b/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
index 3b2be0f1..295bb8cc 100644
--- a/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
+++ b/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
@@ -98,5 +98,7 @@ internal static class EquipRecommended
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/Interact.cs b/Questionable/Controller/Steps/Interactions/Interact.cs
index 0ca70c11..0073349b 100644
--- a/Questionable/Controller/Steps/Interactions/Interact.cs
+++ b/Questionable/Controller/Steps/Interactions/Interact.cs
@@ -228,6 +228,8 @@ internal static class Interact
}
}
+ public override bool ShouldInterruptOnDamage() => true;
+
private enum EInteractionState
{
None,
diff --git a/Questionable/Controller/Steps/Interactions/Jump.cs b/Questionable/Controller/Steps/Interactions/Jump.cs
index f7b9892d..3238405c 100644
--- a/Questionable/Controller/Steps/Interactions/Jump.cs
+++ b/Questionable/Controller/Steps/Interactions/Jump.cs
@@ -80,6 +80,8 @@ internal static class Jump
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
internal sealed class DoSingleJump(
diff --git a/Questionable/Controller/Steps/Interactions/Say.cs b/Questionable/Controller/Steps/Interactions/Say.cs
index f13ab4ab..ffb56215 100644
--- a/Questionable/Controller/Steps/Interactions/Say.cs
+++ b/Questionable/Controller/Steps/Interactions/Say.cs
@@ -48,5 +48,7 @@ internal static class Say
chatFunctions.ExecuteCommand($"/say {Task.ChatMessage}");
return true;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/StatusOff.cs b/Questionable/Controller/Steps/Interactions/StatusOff.cs
index 746f7394..c9b2b4ca 100644
--- a/Questionable/Controller/Steps/Interactions/StatusOff.cs
+++ b/Questionable/Controller/Steps/Interactions/StatusOff.cs
@@ -43,5 +43,7 @@ internal static class StatusOff
{
return gameFunctions.HasStatus(Task.Status) ? ETaskResult.StillRunning : ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Interactions/UseItem.cs b/Questionable/Controller/Steps/Interactions/UseItem.cs
index 118c204a..abc427ad 100644
--- a/Questionable/Controller/Steps/Interactions/UseItem.cs
+++ b/Questionable/Controller/Steps/Interactions/UseItem.cs
@@ -29,7 +29,7 @@ internal static class UseItem
{
public IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{
- if (step.InteractionType is EInteractionType.SinglePlayerDuty)
+ if (step.InteractionType is EInteractionType.SinglePlayerDuty or EInteractionType.CompleteQuest)
{
if (step.ItemId == null)
return [];
@@ -205,6 +205,8 @@ internal static class UseItem
else
return TimeSpan.FromSeconds(5);
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
internal sealed record UseOnGround(
diff --git a/Questionable/Controller/Steps/Leves/InitiateLeve.cs b/Questionable/Controller/Steps/Leves/InitiateLeve.cs
index ab584cef..31cf4705 100644
--- a/Questionable/Controller/Steps/Leves/InitiateLeve.cs
+++ b/Questionable/Controller/Steps/Leves/InitiateLeve.cs
@@ -50,6 +50,8 @@ internal static class InitiateLeve
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record OpenJournal(ElementId ElementId) : ITask
@@ -85,6 +87,8 @@ internal static class InitiateLeve
return ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record Initiate(ElementId ElementId) : ITask
@@ -111,6 +115,8 @@ internal static class InitiateLeve
return ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed class SelectDifficulty : ITask
@@ -138,5 +144,7 @@ internal static class InitiateLeve
return ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/AethernetShortcut.cs b/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
index bfb2cc3f..460aa440 100644
--- a/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
+++ b/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
@@ -179,10 +179,12 @@ internal static class AethernetShortcut
}
}
}
- else
+ else if (clientState.TerritoryType == aetheryteData.TerritoryIds[Task.To])
logger.LogWarning(
- "Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), walking manually",
+ "Aethernet shortcut not unlocked (from: {FromAetheryte}, to: {ToAetheryte}), skipping as we are already in the destination territory",
Task.From, Task.To);
+ else
+ throw new TaskException($"Aethernet shortcut not unlocked (from: {Task.From}, to: {Task.To})");
return false;
}
@@ -267,5 +269,7 @@ internal static class AethernetShortcut
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
index af575333..b2748b18 100644
--- a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
+++ b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
@@ -221,6 +221,8 @@ internal static class AetheryteShortcut
}
public override bool WasInterrupted() => condition[ConditionFlag.InCombat] || base.WasInterrupted();
+
+ public override bool ShouldInterruptOnDamage() => true;
}
internal sealed record MoveAwayFromAetheryte(EAetheryteLocation TargetAetheryte) : ITask
@@ -264,5 +266,7 @@ internal static class AetheryteShortcut
}
public override ETaskResult Update() => moveExecutor.Update();
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Shared/Craft.cs b/Questionable/Controller/Steps/Shared/Craft.cs
index 26493ca0..d986368f 100644
--- a/Questionable/Controller/Steps/Shared/Craft.cs
+++ b/Questionable/Controller/Steps/Shared/Craft.cs
@@ -133,5 +133,8 @@ internal static class Craft
return inventoryManager->GetInventoryItemCount(Task.ItemId, isHq: false, checkEquipped: false)
+ inventoryManager->GetInventoryItemCount(Task.ItemId, isHq: true, checkEquipped: false);
}
+
+ // we're on a crafting class, so combat doesn't make much sense (we also can't change classes in combat...)
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/Gather.cs b/Questionable/Controller/Steps/Shared/Gather.cs
index 73dd8d12..f4aad9c9 100644
--- a/Questionable/Controller/Steps/Shared/Gather.cs
+++ b/Questionable/Controller/Steps/Shared/Gather.cs
@@ -100,6 +100,8 @@ internal static class Gather
minCollectability: (short)itemToGather.Collectability) >=
itemToGather.ItemCount;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record GatheringTask(
@@ -140,6 +142,9 @@ internal static class Gather
gatheringController.OnErrorToast(ref message, ref isHandled);
return isHandled;
}
+
+ // we're on a gathering class, so combat doesn't make much sense (we also can't change classes in combat...)
+ public override bool ShouldInterruptOnDamage() => false;
}
///
@@ -154,5 +159,7 @@ internal static class Gather
{
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/MoveTo.cs b/Questionable/Controller/Steps/Shared/MoveTo.cs
index 4df2870f..60d83aed 100644
--- a/Questionable/Controller/Steps/Shared/MoveTo.cs
+++ b/Questionable/Controller/Steps/Shared/MoveTo.cs
@@ -228,6 +228,17 @@ internal static class MoveTo
}
}
}
+ else if (!ShouldResolveCombatBeforeNextInteraction() &&
+ _movementController is { IsPathfinding: false, IsPathRunning: false } &&
+ mountExecutor.EvaluateMountState() == Mount.MountResult.DontMount)
+ {
+ // except for e.g. jumping which would maybe break if combat navigates us away, if we don't
+ // need a mount anymore we can just skip combat and assume that the interruption is handled
+ // later.
+ //
+ // without this, the character would just stand around while getting hit
+ _nestedExecutor = (new NoOpTaskExecutor(), new NoOpTask(), true);
+ }
}
else if (nestedExecutor.Executor.Update() == ETaskResult.TaskComplete)
{
@@ -286,6 +297,17 @@ internal static class MoveTo
return base.WasInterrupted();
}
+ public override bool ShouldInterruptOnDamage()
+ {
+ // have we stopped moving, and are we
+ // (a) waiting for a mount to complete, or
+ // (b) want combat to be done before any other interaction?
+ return _movementController is { IsPathfinding: false, IsPathRunning: false } &&
+ (_nestedExecutor is { Triggered: false, Executor: Mount.MountExecutor } || ShouldResolveCombatBeforeNextInteraction());
+ }
+
+ private bool ShouldResolveCombatBeforeNextInteraction() => Task.InteractionType is EInteractionType.Jump;
+
public bool OnErrorToast(SeString message)
{
if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue))
@@ -302,6 +324,8 @@ internal static class MoveTo
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record MoveTask(
@@ -361,6 +385,8 @@ internal static class MoveTo
return ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed class LandTask : ITask
@@ -421,5 +447,7 @@ internal static class MoveTo
return false;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs b/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
index 408b92f7..c7abcae7 100644
--- a/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
+++ b/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
@@ -74,5 +74,7 @@ internal static class RedeemRewardItems
return DateTime.Now <= _continueAt ? ETaskResult.StillRunning : ETaskResult.TaskComplete;
}
+
+ public override bool ShouldInterruptOnDamage() => true;
}
}
diff --git a/Questionable/Controller/Steps/Shared/SkipCondition.cs b/Questionable/Controller/Steps/Shared/SkipCondition.cs
index 5abab059..ebf1dc1f 100644
--- a/Questionable/Controller/Steps/Shared/SkipCondition.cs
+++ b/Questionable/Controller/Steps/Shared/SkipCondition.cs
@@ -315,5 +315,7 @@ internal static class SkipCondition
}
public override ETaskResult Update() => ETaskResult.SkipRemainingTasksForStep;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/StepDisabled.cs b/Questionable/Controller/Steps/Shared/StepDisabled.cs
index f7065359..de58cac3 100644
--- a/Questionable/Controller/Steps/Shared/StepDisabled.cs
+++ b/Questionable/Controller/Steps/Shared/StepDisabled.cs
@@ -31,5 +31,7 @@ internal static class StepDisabled
logger.LogInformation("Skipping step, as it is disabled");
return ETaskResult.SkipRemainingTasksForStep;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/SwitchClassJob.cs b/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
index 59477fec..18bfef7e 100644
--- a/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
+++ b/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
@@ -52,5 +52,8 @@ internal static class SwitchClassJob
}
protected override ETaskResult UpdateInternal() => ETaskResult.TaskComplete;
+
+ // can we even take damage while switching jobs? we should be out of combat...
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
index 0b3a02ba..d39c7c2a 100644
--- a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
+++ b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
@@ -157,6 +157,8 @@ internal static class WaitAtEnd
Delay = Task.Delay;
return true;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed class WaitNextStepOrSequence : ITask
@@ -169,6 +171,8 @@ internal static class WaitAtEnd
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.StillRunning;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record WaitForCompletionFlags(QuestId Quest, QuestStep Step) : ITask
@@ -190,6 +194,8 @@ internal static class WaitAtEnd
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record WaitObjectAtPosition(
@@ -209,6 +215,8 @@ internal static class WaitAtEnd
gameFunctions.IsObjectAtPosition(Task.DataId, Task.Destination, Task.Distance)
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record WaitQuestAccepted(ElementId ElementId) : ITask
@@ -226,6 +234,8 @@ internal static class WaitAtEnd
? ETaskResult.TaskComplete
: ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record WaitQuestCompleted(ElementId ElementId) : ITask
@@ -241,6 +251,8 @@ internal static class WaitAtEnd
{
return questFunctions.IsQuestComplete(Task.ElementId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
}
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed record NextStep(ElementId ElementId, int Sequence) : ILastTask
@@ -253,6 +265,8 @@ internal static class WaitAtEnd
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.NextStep;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
internal sealed class EndAutomation : ILastTask
@@ -268,5 +282,7 @@ internal static class WaitAtEnd
protected override bool Start() => true;
public override ETaskResult Update() => ETaskResult.End;
+
+ public override bool ShouldInterruptOnDamage() => false;
}
}
diff --git a/Questionable/Controller/Steps/Shared/WaitAtStart.cs b/Questionable/Controller/Steps/Shared/WaitAtStart.cs
index c2c304b4..8386e636 100644
--- a/Questionable/Controller/Steps/Shared/WaitAtStart.cs
+++ b/Questionable/Controller/Steps/Shared/WaitAtStart.cs
@@ -31,6 +31,7 @@ internal static class WaitAtStart
Delay = Task.Delay;
return true;
}
- }
+ public override bool ShouldInterruptOnDamage() => false;
+ }
}
diff --git a/Questionable/Controller/Steps/TaskExecutor.cs b/Questionable/Controller/Steps/TaskExecutor.cs
index d0315dbc..30e10b64 100644
--- a/Questionable/Controller/Steps/TaskExecutor.cs
+++ b/Questionable/Controller/Steps/TaskExecutor.cs
@@ -13,6 +13,8 @@ internal interface ITaskExecutor
bool Start(ITask task);
+ bool ShouldInterruptOnDamage();
+
bool WasInterrupted();
ETaskResult Update();
@@ -56,4 +58,6 @@ internal abstract class TaskExecutor : ITaskExecutor
}
public abstract ETaskResult Update();
+
+ public abstract bool ShouldInterruptOnDamage();
}
diff --git a/Questionable/External/AutoDutyIpc.cs b/Questionable/External/AutoDutyIpc.cs
index 71bae7fd..9d049062 100644
--- a/Questionable/External/AutoDutyIpc.cs
+++ b/Questionable/External/AutoDutyIpc.cs
@@ -67,7 +67,7 @@ internal sealed class AutoDutyIpc
try
{
- _run.InvokeAction(cfcData.TerritoryId, 0, true);
+ _run.InvokeAction(cfcData.TerritoryId, 1, true);
}
catch (IpcError e)
{
diff --git a/Questionable/External/TextAdvanceIpc.cs b/Questionable/External/TextAdvanceIpc.cs
index dd4fa395..f840cc18 100644
--- a/Questionable/External/TextAdvanceIpc.cs
+++ b/Questionable/External/TextAdvanceIpc.cs
@@ -22,13 +22,16 @@ internal sealed class TextAdvanceIpc : IDisposable
private readonly string _pluginName;
private readonly ExternalTerritoryConfig _externalTerritoryConfig = new();
- public TextAdvanceIpc(IDalamudPluginInterface pluginInterface, IFramework framework, QuestController questController, Configuration configuration)
+ public TextAdvanceIpc(IDalamudPluginInterface pluginInterface, IFramework framework,
+ QuestController questController, Configuration configuration)
{
_framework = framework;
_questController = questController;
_configuration = configuration;
_isInExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.IsInExternalControl");
- _enableExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.EnableExternalControl");
+ _enableExternalControl =
+ pluginInterface.GetIpcSubscriber(
+ "TextAdvance.EnableExternalControl");
_disableExternalControl = pluginInterface.GetIpcSubscriber("TextAdvance.DisableExternalControl");
_pluginName = pluginInterface.InternalName;
_framework.Update += OnUpdate;
@@ -37,7 +40,7 @@ internal sealed class TextAdvanceIpc : IDisposable
public void Dispose()
{
_framework.Update -= OnUpdate;
- if(_isExternalControlActivated)
+ if (_isExternalControlActivated)
{
_disableExternalControl.InvokeFunc(_pluginName);
}
@@ -45,11 +48,13 @@ internal sealed class TextAdvanceIpc : IDisposable
private void OnUpdate(IFramework framework)
{
- if(_configuration.General.ConfigureTextAdvance && _questController.IsRunning)
+ bool hasActiveQuest = _questController.IsRunning ||
+ _questController.AutomationType != QuestController.EAutomationType.Manual;
+ if (_configuration.General.ConfigureTextAdvance && hasActiveQuest)
{
- if(!_isInExternalControl.InvokeFunc())
+ if (!_isInExternalControl.InvokeFunc())
{
- if(_enableExternalControl.InvokeFunc(_pluginName, _externalTerritoryConfig))
+ if (_enableExternalControl.InvokeFunc(_pluginName, _externalTerritoryConfig))
{
_isExternalControlActivated = true;
}
@@ -57,9 +62,9 @@ internal sealed class TextAdvanceIpc : IDisposable
}
else
{
- if(_isExternalControlActivated)
+ if (_isExternalControlActivated)
{
- if(_disableExternalControl.InvokeFunc(_pluginName) || !_isInExternalControl.InvokeFunc())
+ if (_disableExternalControl.InvokeFunc(_pluginName) || !_isInExternalControl.InvokeFunc())
{
_isExternalControlActivated = false;
}
diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs
index a4b5bae9..e0794c2d 100644
--- a/Questionable/QuestionablePlugin.cs
+++ b/Questionable/QuestionablePlugin.cs
@@ -247,6 +247,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
diff --git a/Questionable/Windows/QuestComponents/QuickAccessButtonsComponent.cs b/Questionable/Windows/QuestComponents/QuickAccessButtonsComponent.cs
index 6093686a..0239d09b 100644
--- a/Questionable/Windows/QuestComponents/QuickAccessButtonsComponent.cs
+++ b/Questionable/Windows/QuestComponents/QuickAccessButtonsComponent.cs
@@ -91,8 +91,10 @@ internal sealed class QuickAccessButtonsComponent
Reload?.Invoke(this, EventArgs.Empty);
ImGui.SameLine();
- if (ImGuiComponents.IconButton(FontAwesomeIcon.ChartColumn))
+ if (ImGuiComponents.IconButton(FontAwesomeIcon.BookBookmark))
_journalProgressWindow.IsOpen = true;
+ if (ImGui.IsItemHovered())
+ ImGui.SetTooltip("Journal Progress");
if (_questRegistry.ValidationIssueCount > 0)