}
else
{
- (targetFile, root) = CreateNewFile(gatheringPoint, target, string.Join(" ", arguments));
+ (targetFile, root) = CreateNewFile(gatheringPoint, target);
_chatGui.Print($"Creating new file under {targetFile.FullName}", "qG");
}
}
}
- public (FileInfo targetFile, GatheringRoot root) CreateNewFile(GatheringPoint gatheringPoint, IGameObject target,
- string fileName)
+ public (FileInfo targetFile, GatheringRoot root) CreateNewFile(GatheringPoint gatheringPoint, IGameObject target)
{
- if (string.IsNullOrEmpty(fileName))
- throw new ArgumentException(nameof(fileName));
-
// determine target folder
DirectoryInfo? targetFolder = _plugin.GetLocationsInTerritory(_clientState.TerritoryType).FirstOrDefault()
?.File.Directory;
FileInfo targetFile =
new FileInfo(
- Path.Combine(targetFolder.FullName, $"{gatheringPoint.GatheringPointBase.Row}_{fileName}.json"));
+ Path.Combine(targetFolder.FullName,
+ $"{gatheringPoint.GatheringPointBase.Row}_{gatheringPoint.PlaceName.Value!.Name}_{(_clientState.LocalPlayer!.ClassJob.Id == 16 ? "MIN" : "BTN")}.json"));
var root = new GatheringRoot
{
TerritoryId = _clientState.TerritoryType,
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
+using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using ImGuiNET;
private (RendererPlugin.GatheringLocationContext Context, GatheringNode Node, GatheringLocation Location)?
_targetLocation;
- private string _newFileName = string.Empty;
-
public EditorWindow(RendererPlugin plugin, EditorCommands editorCommands, IDataManager dataManager,
ITargetManager targetManager, IClientState clientState, IObjectTable objectTable)
: base("Gathering Path Editor###QuestionableGatheringPathEditor")
})
.Select(location => new { Context = context, Node = node, Location = location }))))
.FirstOrDefault();
- if (_target != null && _target.ObjectKind != ObjectKind.GatheringPoint || location == null)
+ if (_target != null && _target.ObjectKind != ObjectKind.GatheringPoint)
{
_target = null;
_targetLocation = null;
return;
}
+ if (location == null)
+ {
+ _targetLocation = null;
+ return;
+ }
+
_target ??= _objectTable.FirstOrDefault(
x => x.ObjectKind == ObjectKind.GatheringPoint &&
x.DataId == location.Node.DataId &&
_plugin.Redraw();
}
- ImGui.BeginDisabled(locationOverride.MinimumAngle == null && locationOverride.MaximumAngle == null);
+ bool unsaved = locationOverride is { MinimumAngle: not null, MaximumAngle: not null };
+ ImGui.BeginDisabled(!unsaved);
+ if (unsaved)
+ ImGui.PushStyleColor(ImGuiCol.Button, ImGuiColors.DalamudRed);
if (ImGui.Button("Save"))
{
location.MinimumAngle = locationOverride.MinimumAngle;
location.MaximumAngle = locationOverride.MaximumAngle;
_plugin.Save(context.File, context.Root);
}
+ if (unsaved)
+ ImGui.PopStyleColor();
ImGui.SameLine();
if (ImGui.Button("Reset"))
}
else
{
- ImGui.InputText("File Name", ref _newFileName, 128);
- ImGui.BeginDisabled(string.IsNullOrEmpty(_newFileName));
if (ImGui.Button("Create location"))
{
- var (targetFile, root) = _editorCommands.CreateNewFile(gatheringPoint, _target, _newFileName);
+ var (targetFile, root) = _editorCommands.CreateNewFile(gatheringPoint, _target);
_plugin.Save(targetFile, root);
- _newFileName = string.Empty;
}
-
- ImGui.EndDisabled();
}
}
}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": [],
+ "TerritoryId": 958,
+ "AetheryteShortcut": "Garlemald - Camp Broken Glass",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33932,
+ "Locations": [
+ {
+ "Position": {
+ "X": -80.95969,
+ "Y": -9.810837,
+ "Z": 462.2579
+ },
+ "MinimumAngle": 130,
+ "MaximumAngle": 260
+ }
+ ]
+ },
+ {
+ "DataId": 33933,
+ "Locations": [
+ {
+ "Position": {
+ "X": -72.11935,
+ "Y": -10.90324,
+ "Z": 471.2258
+ },
+ "MinimumAngle": 105,
+ "MaximumAngle": 250
+ },
+ {
+ "Position": {
+ "X": -98.97565,
+ "Y": -5.664787,
+ "Z": 463.9966
+ },
+ "MinimumAngle": 60,
+ "MaximumAngle": 230
+ },
+ {
+ "Position": {
+ "X": -63.49503,
+ "Y": -11.21235,
+ "Z": 469.3839
+ },
+ "MinimumAngle": 80,
+ "MaximumAngle": 255
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33931,
+ "Locations": [
+ {
+ "Position": {
+ "X": -61.34306,
+ "Y": 6.11244,
+ "Z": 318.3409
+ },
+ "MinimumAngle": -120,
+ "MaximumAngle": 70
+ },
+ {
+ "Position": {
+ "X": -61.47854,
+ "Y": 6.076105,
+ "Z": 281.4938
+ },
+ "MinimumAngle": 65,
+ "MaximumAngle": 240
+ },
+ {
+ "Position": {
+ "X": -73.25829,
+ "Y": 6.108262,
+ "Z": 302.9926
+ },
+ "MinimumAngle": 50,
+ "MaximumAngle": 220
+ }
+ ]
+ },
+ {
+ "DataId": 33930,
+ "Locations": [
+ {
+ "Position": {
+ "X": -51.28564,
+ "Y": 6.088318,
+ "Z": 318.0529
+ },
+ "MinimumAngle": -65,
+ "MaximumAngle": 110
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33935,
+ "Locations": [
+ {
+ "Position": {
+ "X": 72.58704,
+ "Y": -11.59895,
+ "Z": 354.757
+ },
+ "MinimumAngle": 75,
+ "MaximumAngle": 235
+ },
+ {
+ "Position": {
+ "X": 65.33016,
+ "Y": -11.61111,
+ "Z": 358.7321
+ },
+ "MinimumAngle": 65,
+ "MaximumAngle": 235
+ },
+ {
+ "Position": {
+ "X": 68.21196,
+ "Y": -11.81954,
+ "Z": 366.5172
+ },
+ "MinimumAngle": 5,
+ "MaximumAngle": 85
+ }
+ ]
+ },
+ {
+ "DataId": 33934,
+ "Locations": [
+ {
+ "Position": {
+ "X": 81.30492,
+ "Y": -11.53227,
+ "Z": 347.9922
+ },
+ "MinimumAngle": 50,
+ "MaximumAngle": 215
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": [],
+ "TerritoryId": 959,
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33929,
+ "Locations": [
+ {
+ "Position": {
+ "X": 304.4121,
+ "Y": 118.8077,
+ "Z": 673.4494
+ },
+ "MinimumAngle": 50,
+ "MaximumAngle": 230
+ },
+ {
+ "Position": {
+ "X": 297.7666,
+ "Y": 119.4976,
+ "Z": 679.5604
+ },
+ "MinimumAngle": 50,
+ "MaximumAngle": 220
+ },
+ {
+ "Position": {
+ "X": 322.163,
+ "Y": 119.0883,
+ "Z": 657.4384
+ },
+ "MinimumAngle": 55,
+ "MaximumAngle": 235
+ }
+ ]
+ },
+ {
+ "DataId": 33928,
+ "Locations": [
+ {
+ "Position": {
+ "X": 313.72,
+ "Y": 118.3442,
+ "Z": 664.8668
+ },
+ "MinimumAngle": 60,
+ "MaximumAngle": 230
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33927,
+ "Locations": [
+ {
+ "Position": {
+ "X": 394.3838,
+ "Y": 144.7951,
+ "Z": 820.7851
+ },
+ "MinimumAngle": 75,
+ "MaximumAngle": 250
+ },
+ {
+ "Position": {
+ "X": 421.0549,
+ "Y": 143.6111,
+ "Z": 805.9457
+ },
+ "MinimumAngle": 60,
+ "MaximumAngle": 225
+ },
+ {
+ "Position": {
+ "X": 414.2961,
+ "Y": 143.2405,
+ "Z": 811.3884
+ },
+ "MinimumAngle": 65,
+ "MaximumAngle": 230
+ }
+ ]
+ },
+ {
+ "DataId": 33926,
+ "Locations": [
+ {
+ "Position": {
+ "X": 405.2481,
+ "Y": 143.6621,
+ "Z": 816.6496
+ },
+ "MinimumAngle": 75,
+ "MaximumAngle": 230
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33925,
+ "Locations": [
+ {
+ "Position": {
+ "X": 474.679,
+ "Y": 143.4776,
+ "Z": 698.5961
+ },
+ "MinimumAngle": 20,
+ "MaximumAngle": 170
+ },
+ {
+ "Position": {
+ "X": 474.8585,
+ "Y": 144.2588,
+ "Z": 685.7468
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 155
+ },
+ {
+ "Position": {
+ "X": 467.506,
+ "Y": 144.9235,
+ "Z": 654.2
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 150
+ }
+ ]
+ },
+ {
+ "DataId": 33924,
+ "Locations": [
+ {
+ "Position": {
+ "X": 470.7754,
+ "Y": 144.8793,
+ "Z": 672.114
+ },
+ "MinimumAngle": -5,
+ "MaximumAngle": 165
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
- "Author": "liza",
- "TerritoryId": 957,
- "AetheryteShortcut": "Thavnair - Great Work",
- "Groups": [
- {
- "Nodes": [
- {
- "DataId": 33918,
- "Locations": [
- {
- "Position": {
- "X": -582.5132,
- "Y": 40.54578,
- "Z": -426.0171
- },
- "MinimumAngle": -50,
- "MaximumAngle": 90
- }
- ]
- },
- {
- "DataId": 33919,
- "Locations": [
- {
- "Position": {
- "X": -578.2101,
- "Y": 41.27147,
- "Z": -447.6376
- },
- "MinimumAngle": 130,
- "MaximumAngle": 220
- },
- {
- "Position": {
- "X": -546.2882,
- "Y": 44.52267,
- "Z": -435.8184
- },
- "MinimumAngle": 200,
- "MaximumAngle": 360
- },
- {
- "Position": {
- "X": -606.7445,
- "Y": 38.37634,
- "Z": -425.5284
- },
- "MinimumAngle": -80,
- "MaximumAngle": 70
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 33920,
- "Locations": [
- {
- "Position": {
- "X": -488.2276,
- "Y": 34.71221,
- "Z": -359.6945
- },
- "MinimumAngle": 20,
- "MaximumAngle": 128,
- "MinimumDistance": 1.3
- }
- ]
- },
- {
- "DataId": 33921,
- "Locations": [
- {
- "Position": {
- "X": -498.8687,
- "Y": 31.08014,
- "Z": -351.9397
- },
- "MinimumAngle": 40,
- "MaximumAngle": 190
- },
- {
- "Position": {
- "X": -490.7759,
- "Y": 28.70215,
- "Z": -344.4114
- },
- "MinimumAngle": -110,
- "MaximumAngle": 60
- },
- {
- "Position": {
- "X": -494.1286,
- "Y": 32.89971,
- "Z": -355.0208
- },
- "MinimumAngle": 80,
- "MaximumAngle": 230
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 33922,
- "Locations": [
- {
- "Position": {
- "X": -304.0609,
- "Y": 68.76999,
- "Z": -479.1875
- },
- "MinimumAngle": -110,
- "MaximumAngle": 70
- }
- ]
- },
- {
- "DataId": 33923,
- "Locations": [
- {
- "Position": {
- "X": -293.6989,
- "Y": 68.77935,
- "Z": -484.2256
- },
- "MinimumAngle": -30,
- "MaximumAngle": 110
- },
- {
- "Position": {
- "X": -295.0806,
- "Y": 69.12621,
- "Z": -498.1898
- },
- "MinimumAngle": 10,
- "MaximumAngle": 200
- },
- {
- "Position": {
- "X": -281.4858,
- "Y": 67.64153,
- "Z": -477.6673
- },
- "MinimumAngle": -105,
- "MaximumAngle": 75
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": "liza",
+ "TerritoryId": 957,
+ "AetheryteShortcut": "Thavnair - Great Work",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 33918,
+ "Locations": [
+ {
+ "Position": {
+ "X": -582.5132,
+ "Y": 40.54578,
+ "Z": -426.0171
+ },
+ "MinimumAngle": -50,
+ "MaximumAngle": 90
+ }
+ ]
+ },
+ {
+ "DataId": 33919,
+ "Locations": [
+ {
+ "Position": {
+ "X": -578.2101,
+ "Y": 41.27147,
+ "Z": -447.6376
+ },
+ "MinimumAngle": 130,
+ "MaximumAngle": 220
+ },
+ {
+ "Position": {
+ "X": -546.2882,
+ "Y": 44.52267,
+ "Z": -435.8184
+ },
+ "MinimumAngle": 200,
+ "MaximumAngle": 360
+ },
+ {
+ "Position": {
+ "X": -606.7445,
+ "Y": 38.37634,
+ "Z": -425.5284
+ },
+ "MinimumAngle": -80,
+ "MaximumAngle": 70
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33920,
+ "Locations": [
+ {
+ "Position": {
+ "X": -488.2276,
+ "Y": 34.71221,
+ "Z": -359.6945
+ },
+ "MinimumAngle": 20,
+ "MaximumAngle": 128,
+ "MinimumDistance": 1.3
+ }
+ ]
+ },
+ {
+ "DataId": 33921,
+ "Locations": [
+ {
+ "Position": {
+ "X": -498.8687,
+ "Y": 31.08014,
+ "Z": -351.9397
+ },
+ "MinimumAngle": 40,
+ "MaximumAngle": 190
+ },
+ {
+ "Position": {
+ "X": -490.7759,
+ "Y": 28.70215,
+ "Z": -344.4114
+ },
+ "MinimumAngle": -110,
+ "MaximumAngle": 60
+ },
+ {
+ "Position": {
+ "X": -494.1286,
+ "Y": 32.89971,
+ "Z": -355.0208
+ },
+ "MinimumAngle": 80,
+ "MaximumAngle": 230
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 33922,
+ "Locations": [
+ {
+ "Position": {
+ "X": -304.0609,
+ "Y": 68.76999,
+ "Z": -479.1875
+ },
+ "MinimumAngle": -110,
+ "MaximumAngle": 70
+ }
+ ]
+ },
+ {
+ "DataId": 33923,
+ "Locations": [
+ {
+ "Position": {
+ "X": -293.6989,
+ "Y": 68.77935,
+ "Z": -484.2256
+ },
+ "MinimumAngle": -30,
+ "MaximumAngle": 110
+ },
+ {
+ "Position": {
+ "X": -295.0806,
+ "Y": 69.12621,
+ "Z": -498.1898
+ },
+ "MinimumAngle": 10,
+ "MaximumAngle": 200
+ },
+ {
+ "Position": {
+ "X": -281.4858,
+ "Y": 67.64153,
+ "Z": -477.6673
+ },
+ "MinimumAngle": -105,
+ "MaximumAngle": 75
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": [],
+ "TerritoryId": 1187,
+ "AetheryteShortcut": "Urqopacha - Wachunpelo",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34749,
+ "Locations": [
+ {
+ "Position": {
+ "X": -392.813,
+ "Y": -47.04364,
+ "Z": -386.862
+ },
+ "MinimumAngle": -10,
+ "MaximumAngle": 240
+ }
+ ]
+ },
+ {
+ "DataId": 34750,
+ "Locations": [
+ {
+ "Position": {
+ "X": -402.8987,
+ "Y": -45.59287,
+ "Z": -390.7613
+ },
+ "MinimumAngle": 220,
+ "MaximumAngle": 305
+ },
+ {
+ "Position": {
+ "X": -388.9036,
+ "Y": -46.86702,
+ "Z": -381.3985
+ },
+ "MinimumAngle": -50,
+ "MaximumAngle": 210
+ },
+ {
+ "Position": {
+ "X": -394.2657,
+ "Y": -47.86026,
+ "Z": -394.9654
+ },
+ "MinimumAngle": -120,
+ "MaximumAngle": 120
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34753,
+ "Locations": [
+ {
+ "Position": {
+ "X": -541.7726,
+ "Y": -22.952,
+ "Z": -517.8604
+ },
+ "MinimumAngle": 215,
+ "MaximumAngle": 330
+ }
+ ]
+ },
+ {
+ "DataId": 34754,
+ "Locations": [
+ {
+ "Position": {
+ "X": -522.9433,
+ "Y": -25.87319,
+ "Z": -537.3257
+ },
+ "MinimumAngle": 225,
+ "MaximumAngle": 360
+ },
+ {
+ "Position": {
+ "X": -532.3487,
+ "Y": -22.79275,
+ "Z": -510.8069
+ },
+ "MinimumAngle": 135,
+ "MaximumAngle": 270
+ },
+ {
+ "Position": {
+ "X": -536.2922,
+ "Y": -23.79476,
+ "Z": -526.0406
+ },
+ "MinimumAngle": -110,
+ "MaximumAngle": 35
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34751,
+ "Locations": [
+ {
+ "Position": {
+ "X": -448.8079,
+ "Y": -14.9586,
+ "Z": -658.0133
+ },
+ "MinimumAngle": -45,
+ "MaximumAngle": 115
+ }
+ ]
+ },
+ {
+ "DataId": 34752,
+ "Locations": [
+ {
+ "Position": {
+ "X": -452.2813,
+ "Y": -12.43015,
+ "Z": -665.0275
+ },
+ "MinimumAngle": 0,
+ "MaximumAngle": 150
+ },
+ {
+ "Position": {
+ "X": -431.5875,
+ "Y": -16.68724,
+ "Z": -656.528
+ },
+ "MinimumAngle": -35,
+ "MaximumAngle": 90
+ },
+ {
+ "Position": {
+ "X": -439.8079,
+ "Y": -16.67447,
+ "Z": -654.6749
+ },
+ "MinimumAngle": -45,
+ "MaximumAngle": 85
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
- "Author": [],
- "TerritoryId": 1187,
- "AetheryteShortcut": "Urqopacha - Wachunpelo",
- "Groups": [
- {
- "Nodes": [
- {
- "DataId": 34749,
- "Locations": [
- {
- "Position": {
- "X": -392.813,
- "Y": -47.04364,
- "Z": -386.862
- },
- "MinimumAngle": -10,
- "MaximumAngle": 240
- }
- ]
- },
- {
- "DataId": 34750,
- "Locations": [
- {
- "Position": {
- "X": -402.8987,
- "Y": -45.59287,
- "Z": -390.7613
- },
- "MinimumAngle": 220,
- "MaximumAngle": 305
- },
- {
- "Position": {
- "X": -388.9036,
- "Y": -46.86702,
- "Z": -381.3985
- },
- "MinimumAngle": -50,
- "MaximumAngle": 210
- },
- {
- "Position": {
- "X": -394.2657,
- "Y": -47.86026,
- "Z": -394.9654
- },
- "MinimumAngle": -120,
- "MaximumAngle": 120
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34753,
- "Locations": [
- {
- "Position": {
- "X": -541.7726,
- "Y": -22.952,
- "Z": -517.8604
- },
- "MinimumAngle": 215,
- "MaximumAngle": 330
- }
- ]
- },
- {
- "DataId": 34754,
- "Locations": [
- {
- "Position": {
- "X": -522.9433,
- "Y": -25.87319,
- "Z": -537.3257
- },
- "MinimumAngle": 225,
- "MaximumAngle": 360
- },
- {
- "Position": {
- "X": -532.3487,
- "Y": -22.79275,
- "Z": -510.8069
- },
- "MinimumAngle": 135,
- "MaximumAngle": 270
- },
- {
- "Position": {
- "X": -536.2922,
- "Y": -23.79476,
- "Z": -526.0406
- },
- "MinimumAngle": -110,
- "MaximumAngle": 35
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34751,
- "Locations": [
- {
- "Position": {
- "X": -448.8079,
- "Y": -14.9586,
- "Z": -658.0133
- },
- "MinimumAngle": -45,
- "MaximumAngle": 115
- }
- ]
- },
- {
- "DataId": 34752,
- "Locations": [
- {
- "Position": {
- "X": -452.2813,
- "Y": -12.43015,
- "Z": -665.0275
- },
- "MinimumAngle": 0,
- "MaximumAngle": 150
- },
- {
- "Position": {
- "X": -431.5875,
- "Y": -16.68724,
- "Z": -656.528
- },
- "MinimumAngle": -35,
- "MaximumAngle": 90
- },
- {
- "Position": {
- "X": -439.8079,
- "Y": -16.67447,
- "Z": -654.6749
- },
- "MinimumAngle": -45,
- "MaximumAngle": 85
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": [],
+ "TerritoryId": 1187,
+ "AetheryteShortcut": "Urqopacha - Wachunpelo",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34857,
+ "Locations": [
+ {
+ "Position": {
+ "X": -12.48859,
+ "Y": -133.2091,
+ "Z": -427.7497
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34858,
+ "Locations": [
+ {
+ "Position": {
+ "X": -22.41956,
+ "Y": -129.3952,
+ "Z": -396.6573
+ }
+ },
+ {
+ "Position": {
+ "X": -16.08351,
+ "Y": -137.6674,
+ "Z": -464.35
+ },
+ "MinimumAngle": -65,
+ "MaximumAngle": 145
+ },
+ {
+ "Position": {
+ "X": -9.000858,
+ "Y": -134.9256,
+ "Z": -439.0332
+ },
+ "MinimumAngle": -125,
+ "MaximumAngle": 105
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34861,
+ "Locations": [
+ {
+ "Position": {
+ "X": -234.8222,
+ "Y": -99.01237,
+ "Z": -376.7287
+ },
+ "MinimumAngle": -180,
+ "MaximumAngle": 40
+ }
+ ]
+ },
+ {
+ "DataId": 34862,
+ "Locations": [
+ {
+ "Position": {
+ "X": -236.0182,
+ "Y": -97.50027,
+ "Z": -372.1523
+ },
+ "MinimumAngle": -180,
+ "MaximumAngle": 45
+ },
+ {
+ "Position": {
+ "X": -249.7221,
+ "Y": -96.55618,
+ "Z": -386.2397
+ },
+ "MinimumAngle": 35,
+ "MaximumAngle": 280
+ },
+ {
+ "Position": {
+ "X": -241.8424,
+ "Y": -99.37369,
+ "Z": -386.2889
+ },
+ "MinimumAngle": -300,
+ "MaximumAngle": -45
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34860,
+ "Locations": [
+ {
+ "Position": {
+ "X": -169.8177,
+ "Y": -85.61841,
+ "Z": -240.1007
+ }
+ },
+ {
+ "Position": {
+ "X": -116.6446,
+ "Y": -93.99508,
+ "Z": -274.6102
+ },
+ "MinimumAngle": -140,
+ "MaximumAngle": 150
+ },
+ {
+ "Position": {
+ "X": -133.936,
+ "Y": -91.54122,
+ "Z": -273.3963
+ },
+ "MinimumAngle": -155,
+ "MaximumAngle": 85
+ }
+ ]
+ },
+ {
+ "DataId": 34859,
+ "Locations": [
+ {
+ "Position": {
+ "X": -131.9198,
+ "Y": -89.88039,
+ "Z": -249.5422
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
- "Author": [],
- "TerritoryId": 1187,
- "AetheryteShortcut": "Urqopacha - Wachunpelo",
- "Groups": [
- {
- "Nodes": [
- {
- "DataId": 34857,
- "Locations": [
- {
- "Position": {
- "X": -12.48859,
- "Y": -133.2091,
- "Z": -427.7497
- }
- }
- ]
- },
- {
- "DataId": 34858,
- "Locations": [
- {
- "Position": {
- "X": -22.41956,
- "Y": -129.3952,
- "Z": -396.6573
- }
- },
- {
- "Position": {
- "X": -16.08351,
- "Y": -137.6674,
- "Z": -464.35
- },
- "MinimumAngle": -65,
- "MaximumAngle": 145
- },
- {
- "Position": {
- "X": -9.000858,
- "Y": -134.9256,
- "Z": -439.0332
- },
- "MinimumAngle": -125,
- "MaximumAngle": 105
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34861,
- "Locations": [
- {
- "Position": {
- "X": -234.8222,
- "Y": -99.01237,
- "Z": -376.7287
- },
- "MinimumAngle": -180,
- "MaximumAngle": 40
- }
- ]
- },
- {
- "DataId": 34862,
- "Locations": [
- {
- "Position": {
- "X": -236.0182,
- "Y": -97.50027,
- "Z": -372.1523
- },
- "MinimumAngle": -180,
- "MaximumAngle": 45
- },
- {
- "Position": {
- "X": -249.7221,
- "Y": -96.55618,
- "Z": -386.2397
- },
- "MinimumAngle": 35,
- "MaximumAngle": 280
- },
- {
- "Position": {
- "X": -241.8424,
- "Y": -99.37369,
- "Z": -386.2889
- },
- "MinimumAngle": -300,
- "MaximumAngle": -45
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34860,
- "Locations": [
- {
- "Position": {
- "X": -169.8177,
- "Y": -85.61841,
- "Z": -240.1007
- }
- },
- {
- "Position": {
- "X": -116.6446,
- "Y": -93.99508,
- "Z": -274.6102
- },
- "MinimumAngle": -140,
- "MaximumAngle": 150
- },
- {
- "Position": {
- "X": -133.936,
- "Y": -91.54122,
- "Z": -273.3963
- },
- "MinimumAngle": -155,
- "MaximumAngle": 85
- }
- ]
- },
- {
- "DataId": 34859,
- "Locations": [
- {
- "Position": {
- "X": -131.9198,
- "Y": -89.88039,
- "Z": -249.5422
- }
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
+ "Author": [],
+ "TerritoryId": 1187,
+ "AetheryteShortcut": "Urqopacha - Wachunpelo",
+ "Groups": [
+ {
+ "Nodes": [
+ {
+ "DataId": 34866,
+ "Locations": [
+ {
+ "Position": {
+ "X": 242.7737,
+ "Y": -135.9734,
+ "Z": -431.2313
+ },
+ "MinimumAngle": -55,
+ "MaximumAngle": 100
+ },
+ {
+ "Position": {
+ "X": 302.1836,
+ "Y": -135.4149,
+ "Z": -359.7965
+ },
+ "MinimumAngle": 5,
+ "MaximumAngle": 155
+ },
+ {
+ "Position": {
+ "X": 256.1657,
+ "Y": -135.744,
+ "Z": -414.7577
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34865,
+ "Locations": [
+ {
+ "Position": {
+ "X": 269.7338,
+ "Y": -134.0488,
+ "Z": -381.6242
+ },
+ "MinimumAngle": -85,
+ "MaximumAngle": 145
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34868,
+ "Locations": [
+ {
+ "Position": {
+ "X": 389.1952,
+ "Y": -154.3099,
+ "Z": -368.3658
+ },
+ "MinimumAngle": 105,
+ "MaximumAngle": 345
+ },
+ {
+ "Position": {
+ "X": 401.9319,
+ "Y": -150.0004,
+ "Z": -408.114
+ },
+ "MinimumAngle": -70,
+ "MaximumAngle": 85
+ },
+ {
+ "Position": {
+ "X": 406.1098,
+ "Y": -152.2166,
+ "Z": -364.7227
+ },
+ "MinimumAngle": -210,
+ "MaximumAngle": 35
+ }
+ ]
+ },
+ {
+ "DataId": 34867,
+ "Locations": [
+ {
+ "Position": {
+ "X": 399.1297,
+ "Y": -152.1141,
+ "Z": -394.71
+ },
+ "MinimumAngle": 120,
+ "MaximumAngle": 330
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "Nodes": [
+ {
+ "DataId": 34864,
+ "Locations": [
+ {
+ "Position": {
+ "X": 359.517,
+ "Y": -161.1972,
+ "Z": -644.0471
+ }
+ },
+ {
+ "Position": {
+ "X": 307.4235,
+ "Y": -159.1669,
+ "Z": -622.6444
+ }
+ },
+ {
+ "Position": {
+ "X": 348.5925,
+ "Y": -165.3805,
+ "Z": -671.4193
+ }
+ }
+ ]
+ },
+ {
+ "DataId": 34863,
+ "Locations": [
+ {
+ "Position": {
+ "X": 323.8758,
+ "Y": -162.9682,
+ "Z": -648.8156
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
+++ /dev/null
-{
- "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
- "Author": [],
- "TerritoryId": 1187,
- "AetheryteShortcut": "Urqopacha - Wachunpelo",
- "Groups": [
- {
- "Nodes": [
- {
- "DataId": 34866,
- "Locations": [
- {
- "Position": {
- "X": 242.7737,
- "Y": -135.9734,
- "Z": -431.2313
- },
- "MinimumAngle": -55,
- "MaximumAngle": 100
- },
- {
- "Position": {
- "X": 302.1836,
- "Y": -135.4149,
- "Z": -359.7965
- },
- "MinimumAngle": 5,
- "MaximumAngle": 155
- },
- {
- "Position": {
- "X": 256.1657,
- "Y": -135.744,
- "Z": -414.7577
- }
- }
- ]
- },
- {
- "DataId": 34865,
- "Locations": [
- {
- "Position": {
- "X": 269.7338,
- "Y": -134.0488,
- "Z": -381.6242
- },
- "MinimumAngle": -85,
- "MaximumAngle": 145
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34868,
- "Locations": [
- {
- "Position": {
- "X": 389.1952,
- "Y": -154.3099,
- "Z": -368.3658
- },
- "MinimumAngle": 105,
- "MaximumAngle": 345
- },
- {
- "Position": {
- "X": 401.9319,
- "Y": -150.0004,
- "Z": -408.114
- },
- "MinimumAngle": -70,
- "MaximumAngle": 85
- },
- {
- "Position": {
- "X": 406.1098,
- "Y": -152.2166,
- "Z": -364.7227
- },
- "MinimumAngle": -210,
- "MaximumAngle": 35
- }
- ]
- },
- {
- "DataId": 34867,
- "Locations": [
- {
- "Position": {
- "X": 399.1297,
- "Y": -152.1141,
- "Z": -394.71
- },
- "MinimumAngle": 120,
- "MaximumAngle": 330
- }
- ]
- }
- ]
- },
- {
- "Nodes": [
- {
- "DataId": 34864,
- "Locations": [
- {
- "Position": {
- "X": 359.517,
- "Y": -161.1972,
- "Z": -644.0471
- }
- },
- {
- "Position": {
- "X": 307.4235,
- "Y": -159.1669,
- "Z": -622.6444
- }
- },
- {
- "Position": {
- "X": 348.5925,
- "Y": -165.3805,
- "Z": -671.4193
- }
- }
- ]
- },
- {
- "DataId": 34863,
- "Locations": [
- {
- "Position": {
- "X": 323.8758,
- "Y": -162.9682,
- "Z": -648.8156
- }
- }
- ]
- }
- ]
- }
- ]
-}
\ No newline at end of file
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "Position": {
+ "X": -435.39066,
+ "Y": -9.809827,
+ "Z": -594.5472
+ },
+ "TerritoryId": 1187,
+ "InteractionType": "WalkTo",
+ "Fly": true,
+ "RequiredGatheredItems": [
+ {
+ "ItemId": 43992,
+ "ItemCount": 1234
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
},
"StopDistance": 7,
"TerritoryId": 962,
- "InteractionType": "CompleteQuest"
+ "InteractionType": "CompleteQuest",
+ "NextQuestId": 4154
}
]
}
{
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
"Author": "liza",
- "Disabled": true,
"QuestSequence": [
{
"Sequence": 0,
},
"TerritoryId": 962,
"InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Old Sharlayan",
+ "AethernetShortcut": [
+ "[Old Sharlayan] Aetheryte Plaza",
+ "[Old Sharlayan] The Studium"
+ ],
"RequiredGatheredItems": [
{
"ItemId": 35600,
"ItemCount": 6,
"Collectability": 600
}
- ]
+ ],
+ "NextQuestId": 4155
}
]
}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1038501,
+ "Position": {
+ "X": -367.3305,
+ "Y": 21.846018,
+ "Z": -102.983154
+ },
+ "TerritoryId": 962,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1038501,
+ "Position": {
+ "X": -367.3305,
+ "Y": 21.846018,
+ "Z": -102.983154
+ },
+ "TerritoryId": 962,
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Old Sharlayan",
+ "AethernetShortcut": [
+ "[Old Sharlayan] Aetheryte Plaza",
+ "[Old Sharlayan] The Studium"
+ ],
+ "RequiredGatheredItems": [
+ {
+ "ItemId": 35601,
+ "ItemCount": 6,
+ "Collectability": 600
+ }
+ ],
+ "NextQuestId": 4156
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1038501,
+ "Position": {
+ "X": -367.3305,
+ "Y": 21.846018,
+ "Z": -102.983154
+ },
+ "TerritoryId": 962,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ },
+ {
+ "Sequence": 255,
+ "Steps": [
+ {
+ "DataId": 1038501,
+ "Position": {
+ "X": -367.3305,
+ "Y": 21.846018,
+ "Z": -102.983154
+ },
+ "TerritoryId": 962,
+ "InteractionType": "CompleteQuest",
+ "AetheryteShortcut": "Old Sharlayan",
+ "AethernetShortcut": [
+ "[Old Sharlayan] Aetheryte Plaza",
+ "[Old Sharlayan] The Studium"
+ ],
+ "RequiredGatheredItems": [
+ {
+ "ItemId": 35602,
+ "ItemCount": 6,
+ "Collectability": 600
+ }
+ ],
+ "NextQuestId": 4157
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/QuestPaths/quest-v1.json",
+ "Author": "liza",
+ "QuestSequence": [
+ {
+ "Sequence": 0,
+ "Steps": [
+ {
+ "DataId": 1038501,
+ "Position": {
+ "X": -367.3305,
+ "Y": 21.846018,
+ "Z": -102.983154
+ },
+ "TerritoryId": 962,
+ "InteractionType": "AcceptQuest"
+ }
+ ]
+ }
+ ]
+}
RedGulal = 29382,
YellowGulal = 29383,
BlueGulal = 29384,
+
+ CollectMiner = 240,
+ ScourMiner = 22182,
+ MeticulousMiner = 22184,
+ ScrutinyMiner = 22185,
+
+ CollectBotanist = 815,
+ ScourBotanist = 22186,
+ MeticulousBotanist = 22188,
+ ScrutinyBotanist = 22189,
+
+
}
public static class EActionExtensions
{
public uint ItemId { get; set; }
public int ItemCount { get; set; }
- public short Collectability { get; set; }
+ public ushort Collectability { get; set; }
}
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
+using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using Questionable.Controller.Steps.Gathering;
using Questionable.Controller.Steps.Interactions;
using Questionable.Controller.Steps.Shared;
-using Questionable.Data;
using Questionable.External;
using Questionable.GatheringPaths;
using Questionable.Model.Gathering;
internal sealed unsafe class GatheringController : MiniTaskController<GatheringController>
{
private readonly MovementController _movementController;
- private readonly GatheringData _gatheringData;
private readonly GameFunctions _gameFunctions;
private readonly NavmeshIpc _navmeshIpc;
private readonly IObjectTable _objectTable;
private readonly IServiceProvider _serviceProvider;
+ private readonly ICondition _condition;
private CurrentRequest? _currentRequest;
- public GatheringController(MovementController movementController, GatheringData gatheringData,
- GameFunctions gameFunctions, NavmeshIpc navmeshIpc, IObjectTable objectTable, IChatGui chatGui,
- ILogger<GatheringController> logger, IServiceProvider serviceProvider)
+ public GatheringController(
+ MovementController movementController,
+ GameFunctions gameFunctions,
+ NavmeshIpc navmeshIpc,
+ IObjectTable objectTable,
+ IChatGui chatGui,
+ ILogger<GatheringController> logger,
+ IServiceProvider serviceProvider,
+ ICondition condition)
: base(chatGui, logger)
{
_movementController = movementController;
- _gatheringData = gatheringData;
_gameFunctions = gameFunctions;
_navmeshIpc = navmeshIpc;
_objectTable = objectTable;
_serviceProvider = serviceProvider;
+ _condition = condition;
}
public bool Start(GatheringRequest gatheringRequest)
Data = gatheringRequest,
Root = gatheringRoot,
Nodes = gatheringRoot.Groups
- .SelectMany(x => x.Nodes)
+ // at least in EW-ish, there's one node with 1 fixed location and one node with 3 random locations
+ .SelectMany(x => x.Nodes.OrderBy(y => y.Locations.Count))
.ToList(),
};
if (_movementController.IsPathfinding || _movementController.IsPathfinding)
return EStatus.Moving;
- if (HasRequestedItems())
+ if (HasRequestedItems() && !_condition[ConditionFlag.Gathering])
return EStatus.Complete;
if (_currentTask == null && _taskQueue.Count == 0)
Y = currentNode.Locations.Select(x => x.Position.Y).Max() + 5f,
Z = currentNode.Locations.Sum(x => x.Position.Z) / currentNode.Locations.Count,
};
- Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(averagePosition);
+ bool fly = _gameFunctions.IsFlyingUnlocked(_currentRequest.Root.TerritoryId);
+ Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(averagePosition, true);
if (pointOnFloor != null)
- pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + 3f };
+ pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 3f : 0f) };
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<Move.MoveInternal>()
- .With(_currentRequest.Root.TerritoryId, pointOnFloor ?? averagePosition, 50f, fly: true,
+ .With(_currentRequest.Root.TerritoryId, pointOnFloor ?? averagePosition, 50f, fly: fly,
ignoreDistanceToObject: true));
}
.With(_currentRequest.Root.TerritoryId, currentNode));
_taskQueue.Enqueue(_serviceProvider.GetRequiredService<Interact.DoInteract>()
.With(currentNode.DataId, true));
- _taskQueue.Enqueue(_serviceProvider.GetRequiredService<WaitGather>());
+ _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGather>()
+ .With(_currentRequest.Data, currentNode));
+ if (_currentRequest.Data.Collectability > 0)
+ {
+ _taskQueue.Enqueue(_serviceProvider.GetRequiredService<DoGatherCollectable>()
+ .With(_currentRequest.Data, currentNode));
+ }
}
private bool HasRequestedItems()
return false;
return inventoryManager->GetInventoryItemCount(_currentRequest.Data.ItemId,
- minCollectability: _currentRequest.Data.Collectability) >= _currentRequest.Data.Quantity;
+ minCollectability: (short)_currentRequest.Data.Collectability) >= _currentRequest.Data.Quantity;
+ }
+
+ public bool HasNodeDisappeared(GatheringNode node)
+ {
+ return !_objectTable.Any(x =>
+ x.ObjectKind == ObjectKind.GatheringPoint && x.IsTargetable && x.DataId == node.DataId);
}
public override IList<string> GetRemainingTaskNames()
public int CurrentIndex { get; set; }
}
- public sealed record GatheringRequest(ushort GatheringPointId, uint ItemId, int Quantity, short Collectability = 0);
+ public sealed record GatheringRequest(
+ ushort GatheringPointId,
+ uint ItemId,
+ int Quantity,
+ ushort Collectability = 0);
public enum EStatus
{
private bool IsOnFlightPath(Vector3 p)
{
- Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(p);
+ Vector3? pointOnFloor = _navmeshIpc.GetPointOnFloor(p, true);
return pointOnFloor != null && Math.Abs(pointOnFloor.Value.Y - p.Y) > 0.5f;
}
_combatController.Stop("Execute next step");
_gatheringController.Stop("Execute next step");
- var newTasks = _taskFactories
- .SelectMany(x =>
- {
- IList<ITask> tasks = x.CreateAllTasks(CurrentQuest.Quest, seq, step).ToList();
-
- if (tasks.Count > 0 && _logger.IsEnabled(LogLevel.Trace))
+ try
+ {
+ var newTasks = _taskFactories
+ .SelectMany(x =>
{
- string factoryName = x.GetType().FullName ?? x.GetType().Name;
- if (factoryName.Contains('.', StringComparison.Ordinal))
- factoryName = factoryName[(factoryName.LastIndexOf('.') + 1)..];
+ IList<ITask> tasks = x.CreateAllTasks(CurrentQuest.Quest, seq, step).ToList();
- _logger.LogTrace("Factory {FactoryName} created Task {TaskNames}",
- factoryName, string.Join(", ", tasks.Select(y => y.ToString())));
- }
+ if (tasks.Count > 0 && _logger.IsEnabled(LogLevel.Trace))
+ {
+ string factoryName = x.GetType().FullName ?? x.GetType().Name;
+ if (factoryName.Contains('.', StringComparison.Ordinal))
+ factoryName = factoryName[(factoryName.LastIndexOf('.') + 1)..];
+
+ _logger.LogTrace("Factory {FactoryName} created Task {TaskNames}",
+ factoryName, string.Join(", ", tasks.Select(y => y.ToString())));
+ }
- return tasks;
- })
- .ToList();
- if (newTasks.Count == 0)
+ return tasks;
+ })
+ .ToList();
+ if (newTasks.Count == 0)
+ {
+ _logger.LogInformation("Nothing to execute for step?");
+ return;
+ }
+
+ _logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}",
+ CurrentQuest.Quest.QuestId, seq.Sequence, seq.Steps.IndexOf(step),
+ string.Join(", ", newTasks.Select(x => x.ToString())));
+ foreach (var task in newTasks)
+ _taskQueue.Enqueue(task);
+ }
+ catch (Exception e)
{
- _logger.LogInformation("Nothing to execute for step?");
- return;
+ _logger.LogError(e, "Failed to create tasks");
+ _chatGui.PrintError("[Questionable] Failed to start next task sequence, please check /xllog for details.");
+ Stop("Tasks failed to create");
}
-
- _logger.LogInformation("Tasks for {QuestId}, {Sequence}, {Step}: {Tasks}",
- CurrentQuest.Quest.QuestId, seq.Sequence, seq.Steps.IndexOf(step),
- string.Join(", ", newTasks.Select(x => x.ToString())));
- foreach (var task in newTasks)
- _taskQueue.Enqueue(task);
}
public string ToStatString()
--- /dev/null
+using System.Collections.Generic;
+using System.Linq;
+using Dalamud.Game.ClientState.Conditions;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+using LLib.GameUI;
+using Questionable.Model.Gathering;
+
+namespace Questionable.Controller.Steps.Gathering;
+
+internal sealed class DoGather(
+ GatheringController gatheringController,
+ IGameGui gameGui,
+ ICondition condition) : ITask
+{
+ private GatheringController.GatheringRequest _currentRequest = null!;
+ private GatheringNode _currentNode = null!;
+ private bool _wasGathering;
+ private List<SlotInfo>? _slots;
+
+
+ public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode)
+ {
+ _currentRequest = currentRequest;
+ _currentNode = currentNode;
+ return this;
+ }
+
+ public bool Start() => true;
+
+ public unsafe ETaskResult Update()
+ {
+ if (gatheringController.HasNodeDisappeared(_currentNode))
+ return ETaskResult.TaskComplete;
+
+ if (condition[ConditionFlag.Gathering])
+ {
+ if (gameGui.TryGetAddonByName("GatheringMasterpiece", out AtkUnitBase* _))
+ return ETaskResult.TaskComplete;
+
+ _wasGathering = true;
+
+ if (gameGui.TryGetAddonByName("Gathering", out AtkUnitBase* atkUnitBase))
+ {
+ _slots ??= ReadSlots(atkUnitBase);
+ var slot = _slots.Single(x => x.ItemId == _currentRequest.ItemId);
+ atkUnitBase->FireCallbackInt(slot.Index);
+ }
+ }
+
+ return _wasGathering && !condition[ConditionFlag.Gathering]
+ ? ETaskResult.TaskComplete
+ : ETaskResult.StillRunning;
+ }
+
+ private unsafe List<SlotInfo> ReadSlots(AtkUnitBase* atkUnitBase)
+ {
+ var atkValues = atkUnitBase->AtkValues;
+ List<SlotInfo> slots = new List<SlotInfo>();
+ for (int i = 0; i < 8; ++i)
+ {
+ // +8 = new item?
+ uint itemId = atkValues[i * 11 + 7].UInt;
+ if (itemId == 0)
+ continue;
+
+ var slot = new SlotInfo(i, itemId);
+ slots.Add(slot);
+ }
+
+ return slots;
+ }
+
+ public override string ToString() => "DoGather";
+
+ private sealed record SlotInfo(int Index, uint ItemId);
+}
--- /dev/null
+using System.Collections.Generic;
+using Dalamud.Game.Text;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+using LLib.GameUI;
+using Microsoft.Extensions.Logging;
+using Questionable.Model.Gathering;
+using Questionable.Model.Questing;
+
+namespace Questionable.Controller.Steps.Gathering;
+
+internal sealed class DoGatherCollectable(
+ GatheringController gatheringController,
+ GameFunctions gameFunctions,
+ IClientState clientState,
+ IGameGui gameGui,
+ ILogger<DoGatherCollectable> logger) : ITask
+{
+ private GatheringController.GatheringRequest _currentRequest = null!;
+ private GatheringNode _currentNode = null!;
+ private Queue<EAction>? _actionQueue;
+
+ public ITask With(GatheringController.GatheringRequest currentRequest, GatheringNode currentNode)
+ {
+ _currentRequest = currentRequest;
+ _currentNode = currentNode;
+ return this;
+ }
+
+ public bool Start() => true;
+
+ public ETaskResult Update()
+ {
+ if (gatheringController.HasNodeDisappeared(_currentNode))
+ return ETaskResult.TaskComplete;
+
+ NodeCondition? nodeCondition = GetNodeCondition();
+ if (nodeCondition == null)
+ return ETaskResult.TaskComplete;
+
+ if (_actionQueue != null && _actionQueue.TryPeek(out EAction nextAction))
+ {
+ if (gameFunctions.UseAction(nextAction))
+ {
+ logger.LogInformation("Used action {Action} on node", nextAction);
+ _actionQueue.Dequeue();
+ }
+
+ return ETaskResult.StillRunning;
+ }
+
+ if (nodeCondition.CollectabilityToGoal(_currentRequest.Collectability) > 0)
+ {
+ _actionQueue = GetNextActions(nodeCondition);
+ if (_actionQueue != null)
+ {
+ foreach (var action in _actionQueue)
+ logger.LogInformation("Next Actions {Action}", action);
+ return ETaskResult.StillRunning;
+ }
+ }
+
+ _actionQueue = new Queue<EAction>();
+ _actionQueue.Enqueue(PickAction(EAction.CollectMiner, EAction.CollectBotanist));
+ return ETaskResult.StillRunning;
+ }
+
+ private unsafe NodeCondition? GetNodeCondition()
+ {
+ if (gameGui.TryGetAddonByName("GatheringMasterpiece", out AtkUnitBase* atkUnitBase))
+ {
+ var atkValues = atkUnitBase->AtkValues;
+ return new NodeCondition(
+ CurrentCollectability: atkValues[13].UInt,
+ MaxCollectability: atkValues[14].UInt,
+ CurrentIntegrity: atkValues[62].UInt,
+ MaxIntegrity: atkValues[63].UInt,
+ ScrutinyActive: atkValues[80].Bool,
+ CollectabilityFromScour: atkValues[48].UInt,
+ CollectabilityFromMeticulous: atkValues[51].UInt
+ );
+ }
+
+ return null;
+ }
+
+ private Queue<EAction>? GetNextActions(NodeCondition nodeCondition)
+ {
+ uint gp = clientState.LocalPlayer!.CurrentGp;
+ Queue<EAction> actions = new();
+
+ uint neededCollectability = nodeCondition.CollectabilityToGoal(_currentRequest.Collectability);
+ if (neededCollectability <= nodeCondition.CollectabilityFromMeticulous)
+ {
+ actions.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
+ return actions;
+ }
+
+ if (neededCollectability <= nodeCondition.CollectabilityFromScour)
+ {
+ actions.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
+ return actions;
+ }
+
+ // neither action directly solves our problem
+ if (!nodeCondition.ScrutinyActive && gp >= 200)
+ {
+ actions.Enqueue(PickAction(EAction.ScrutinyMiner, EAction.ScrutinyBotanist));
+ return actions;
+ }
+
+ if (nodeCondition.ScrutinyActive)
+ {
+ actions.Enqueue(PickAction(EAction.MeticulousMiner, EAction.MeticulousBotanist));
+ return actions;
+ }
+ else
+ {
+ actions.Enqueue(PickAction(EAction.ScourMiner, EAction.ScourBotanist));
+ return actions;
+ }
+ }
+
+ private EAction PickAction(EAction minerAction, EAction botanistAction)
+ {
+ if (clientState.LocalPlayer?.ClassJob.Id == 16)
+ return minerAction;
+ else
+ return botanistAction;
+ }
+
+ public override string ToString() =>
+ $"DoGatherCollectable({SeIconChar.Collectible.ToIconString()} {_currentRequest.Collectability})";
+
+ private sealed record NodeCondition(
+ uint CurrentCollectability,
+ uint MaxCollectability,
+ uint CurrentIntegrity,
+ uint MaxIntegrity,
+ bool ScrutinyActive,
+ uint CollectabilityFromScour,
+ uint CollectabilityFromMeticulous)
+ {
+ public uint CollectabilityToGoal(uint goal)
+ {
+ if (goal >= CurrentCollectability)
+ return goal - CurrentCollectability;
+ return CurrentCollectability == 0 ? 1u : 0u;
+ }
+ }
+}
internal sealed class MoveToLandingLocation(
IServiceProvider serviceProvider,
+ GameFunctions gameFunctions,
IObjectTable objectTable,
NavmeshIpc navmeshIpc,
ILogger<MoveToLandingLocation> logger) : ITask
var location = _gatheringNode.Locations.First();
if (_gatheringNode.Locations.Count > 1)
{
- var gameObject = objectTable.Single(x =>
+ var gameObject = objectTable.SingleOrDefault(x =>
x.ObjectKind == ObjectKind.GatheringPoint && x.DataId == _gatheringNode.DataId && x.IsTargetable);
+ if (gameObject == null)
+ return false;
+
location = _gatheringNode.Locations.Single(x => Vector3.Distance(x.Position, gameObject.Position) < 0.1f);
}
logger.LogInformation("Preliminary landing location: {Location}, with degrees = {Degrees}, range = {Range}",
target.ToString("G", CultureInfo.InvariantCulture), degrees, range);
- Vector3? pointOnFloor = navmeshIpc.GetPointOnFloor(target with { Y = target.Y + 5f });
+ bool fly = gameFunctions.IsFlyingUnlocked(_territoryId);
+ Vector3? pointOnFloor = navmeshIpc.GetPointOnFloor(target with { Y = target.Y + 5f }, false);
if (pointOnFloor != null)
- pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + 0.5f };
+ pointOnFloor = pointOnFloor.Value with { Y = pointOnFloor.Value.Y + (fly ? 0.5f : 0f) };
+
+ // since we only allow points that can be landed on, the distance is important but the angle shouldn't matter
+ if (pointOnFloor != null && Vector3.Distance(pointOnFloor.Value, location.Position) >
+ location.CalculateMaximumDistance())
+ {
+ pointOnFloor = location.Position + Vector3.Normalize(pointOnFloor.Value - location.Position) * location.CalculateMaximumDistance();
+ logger.LogInformation("Adjusted landing location: {Location}", pointOnFloor.Value.ToString("G", CultureInfo.InvariantCulture)); }
+ else
+ {
+ logger.LogInformation("Final landing location: {Location}",
+ (pointOnFloor ?? target).ToString("G", CultureInfo.InvariantCulture));
+ }
- logger.LogInformation("Final landing location: {Location}",
- (pointOnFloor ?? target).ToString("G", CultureInfo.InvariantCulture));
_moveTask = serviceProvider.GetRequiredService<Move.MoveInternal>()
- .With(_territoryId, pointOnFloor ?? target, 0.25f, dataId: _gatheringNode.DataId, fly: true,
+ .With(_territoryId, pointOnFloor ?? target, 0.25f, dataId: _gatheringNode.DataId, fly: fly,
ignoreDistanceToObject: true);
return _moveTask.Start();
}
+++ /dev/null
-using Dalamud.Game.ClientState.Conditions;
-using Dalamud.Plugin.Services;
-
-namespace Questionable.Controller.Steps.Gathering;
-
-internal sealed class WaitGather(ICondition condition) : ITask
-{
- private bool _wasGathering;
-
- public bool Start() => true;
-
- public ETaskResult Update()
- {
- if (condition[ConditionFlag.Gathering])
- {
- _wasGathering = true;
- }
-
- return _wasGathering && !condition[ConditionFlag.Gathering]
- ? ETaskResult.TaskComplete
- : ETaskResult.StillRunning;
- }
-
- public override string ToString() => "WaitGather";
-}
using System;
using System.Collections.Generic;
+using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.DependencyInjection;
using Questionable.Data;
return ETaskResult.StillRunning;
}
- public override string ToString() => $"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId})";
+ public override string ToString()
+ {
+ if (_gatheredItem.Collectability == 0)
+ return $"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId})";
+ else
+ return $"Gather({_gatheredItem.ItemCount}x {_gatheredItem.ItemId} {SeIconChar.Collectible.ToIconString()} {_gatheredItem.Collectability})";
+ }
}
}
}
}
- public Vector3? GetPointOnFloor(Vector3 position)
+ public Vector3? GetPointOnFloor(Vector3 position, bool unlandable)
{
try
{
- return _queryPointOnFloor.InvokeFunc(position, true, 1);
+ return _queryPointOnFloor.InvokeFunc(position, unlandable, 0.2f);
}
catch (IpcError)
{
_condition[ConditionFlag.OccupiedInQuestEvent] || _condition[ConditionFlag.OccupiedInCutSceneEvent] ||
_condition[ConditionFlag.Casting] || _condition[ConditionFlag.Unknown57] ||
_condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51] ||
- _condition[ConditionFlag.Jumping61];
+ _condition[ConditionFlag.Jumping61] || _condition[ConditionFlag.Gathering42];
}
public bool IsLoadingScreenVisible()
serviceCollection.AddTransient<MountTask>();
serviceCollection.AddTransient<UnmountTask>();
serviceCollection.AddTransient<MoveToLandingLocation>();
- serviceCollection.AddTransient<WaitGather>();
+ serviceCollection.AddTransient<DoGather>();
+ serviceCollection.AddTransient<DoGatherCollectable>();
// task factories
serviceCollection.AddTaskWithFactory<StepDisabled.Factory, StepDisabled.Task>();