diff --git a/Directory.Build.targets b/Directory.Build.targets
index 9c7a8c24a..cfa9441ec 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 000000000..a60a458a9
--- /dev/null
+++ b/GatheringPathRenderer/.gitignore
@@ -0,0 +1 @@
+/dist
diff --git a/GatheringPathRenderer/DalamudPackager.targets b/GatheringPathRenderer/DalamudPackager.targets
new file mode 100644
index 000000000..7f129a87a
--- /dev/null
+++ b/GatheringPathRenderer/DalamudPackager.targets
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/GatheringPathRenderer/GatheringPathRenderer.csproj b/GatheringPathRenderer/GatheringPathRenderer.csproj
index 6009a5183..73d05cbeb 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 8d68d1d89..56f917099 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 5ef430eae..93c666a8c 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 000000000..0e9ba04cf
--- /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 1150558fa..c117f4a9b 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 000000000..44d84892c
--- /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 000000000..93e5d6962
--- /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 6b76bb95e..2d3ec8034 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 c5fb47f5b..2c1df09fb 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 27a250197..19fac78d1 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 fc0042f57..b55434171 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 f3401f9b2..66891113e 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 b9550d585..924c6a9d0 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 6d2279b9b..f4d1379fe 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 8266ce99b..23eb5a024 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 d1a4fdbf3..1fcf9d81f 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 daaad8965..0629a6822 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 845367804..03953144b 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 000000000..2955b31fb
--- /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 000000000..be941a7ed
--- /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 000000000..8d59d5441
--- /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 000000000..c3906c89e
--- /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 000000000..e84881f97
--- /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 000000000..ef384a78e
--- /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 000000000..689b47bbd
--- /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 000000000..ce12a0943
--- /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 000000000..42a0325a2
--- /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 000000000..18eef643f
--- /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 000000000..1541b26df
--- /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 000000000..e9a8aff84
--- /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 000000000..701a787c8
--- /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 000000000..b41f31b3e
--- /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 000000000..449db054c
--- /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 000000000..c526f6737
--- /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 000000000..4bea24395
--- /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 000000000..aadaebf07
--- /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 000000000..41a0e1d5c
--- /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 000000000..2072032e2
--- /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 000000000..683419858
--- /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 000000000..03490c145
--- /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 000000000..d0ee8c1ca
--- /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 000000000..c5a003fc7
--- /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 000000000..fb9687275
--- /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 000000000..0be23e3f5
--- /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 000000000..2016dfb6d
--- /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 000000000..c603466de
--- /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 000000000..e53516035
--- /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 bdbb16961..864bef9ba 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 903ee73e9..c02008d7f 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 d04fd82c5..f0f826370 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 1b4086ffd..deadc4f6b 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 26a92ff32..95dbea568 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 bbe6d2e49..28de31f9e 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 000000000..9171432fd
--- /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 06e5d874d..c9aec197c 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 9d17fed1d..d514f267b 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 d6641073c..7808b0950 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 1e03d8e97..4f35f4d97 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 7f4b02614..3ac7758d7 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 cf116028e..6d8bbcec6 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 367fdfec0..8203c0568 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 0f4c8c7f2..bf4ab4aa9 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 2b91f3538..fcd5efadf 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 38fa30cd6..bc183013e 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 caf2b0f46..0483605bd 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 7255fa0bf..f7f975020 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 b244bbea9..7632e5749 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 b1af7fe9a..db2c5212a 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 d97547763..dd40fc69d 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 a4d9594d0..e63058a81 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 b5389774c..eea9cd871 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 5e20accf0..b59f8ce70 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 085b0356e..d6dd71467 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 f5cd4e111..d761926d6 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 3b2be0f17..295bb8cc1 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 0ca70c11f..0073349b3 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 f7b9892d5..3238405c8 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 f13ab4ab9..ffb562152 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 746f7394c..c9b2b4ca3 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 118c204aa..abc427ad4 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 ab584cefc..31cf47055 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 bfb2cc3ff..460aa440b 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 af5753334..b2748b183 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 26493ca02..d986368f6 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 73dd8d12f..f4aad9c94 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 4df2870fa..60d83aedd 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 408b92f7e..c7abcae7b 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 5abab0594..ebf1dc1f2 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 f70653592..de58cac36 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 59477feca..18bfef7e5 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 0b3a02ba9..d39c7c2a3 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 c2c304b45..8386e6365 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 d0315dbcd..30e10b643 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 71bae7fd1..9d0490629 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 dd4fa3959..f840cc18f 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 a4b5bae9e..e0794c2d7 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 6093686a8..0239d09b1 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)