GE update
This commit is contained in:
parent
9bfbc99144
commit
ff7ee27fde
218
GatheringPathRenderer/EditorCommands.cs
Normal file
218
GatheringPathRenderer/EditorCommands.cs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json.Serialization.Metadata;
|
||||||
|
using Dalamud.Game.ClientState.Objects;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Questionable.Model;
|
||||||
|
using Questionable.Model.Gathering;
|
||||||
|
|
||||||
|
namespace GatheringPathRenderer;
|
||||||
|
|
||||||
|
internal sealed class EditorCommands : IDisposable
|
||||||
|
{
|
||||||
|
private readonly RendererPlugin _plugin;
|
||||||
|
private readonly IDataManager _dataManager;
|
||||||
|
private readonly ICommandManager _commandManager;
|
||||||
|
private readonly ITargetManager _targetManager;
|
||||||
|
private readonly IClientState _clientState;
|
||||||
|
private readonly IChatGui _chatGui;
|
||||||
|
|
||||||
|
public EditorCommands(RendererPlugin plugin, IDataManager dataManager, ICommandManager commandManager,
|
||||||
|
ITargetManager targetManager, IClientState clientState, IChatGui chatGui)
|
||||||
|
{
|
||||||
|
_plugin = plugin;
|
||||||
|
_dataManager = dataManager;
|
||||||
|
_commandManager = commandManager;
|
||||||
|
_targetManager = targetManager;
|
||||||
|
_clientState = clientState;
|
||||||
|
_chatGui = chatGui;
|
||||||
|
|
||||||
|
_commandManager.AddHandler("/qg", new CommandInfo(ProcessCommand));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCommand(string command, string argument)
|
||||||
|
{
|
||||||
|
string[] parts = argument.Split(' ');
|
||||||
|
string subCommand = parts[0];
|
||||||
|
List<string> arguments = parts.Skip(1).ToList();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (subCommand)
|
||||||
|
{
|
||||||
|
case "add":
|
||||||
|
CreateOrAddLocationToGroup(arguments);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_chatGui.PrintError(e.ToString(), "qG");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateOrAddLocationToGroup(List<string> arguments)
|
||||||
|
{
|
||||||
|
var target = _targetManager.Target;
|
||||||
|
if (target == null || target.ObjectKind != ObjectKind.GatheringPoint)
|
||||||
|
throw new Exception("No valid target");
|
||||||
|
|
||||||
|
var gatheringPoint = _dataManager.GetExcelSheet<GatheringPoint>()!.GetRow(target.DataId);
|
||||||
|
if (gatheringPoint == null)
|
||||||
|
throw new Exception("Invalid gathering point");
|
||||||
|
|
||||||
|
FileInfo targetFile;
|
||||||
|
GatheringRoot root;
|
||||||
|
var locationsInTerritory = _plugin.GetLocationsInTerritory(_clientState.TerritoryType).ToList();
|
||||||
|
var location = locationsInTerritory.SingleOrDefault(x => x.Id == gatheringPoint.GatheringPointBase.Row);
|
||||||
|
if (location != null)
|
||||||
|
{
|
||||||
|
targetFile = location.File;
|
||||||
|
root = location.Root;
|
||||||
|
|
||||||
|
// if this is an existing node, ignore it
|
||||||
|
var existingNode = root.Groups.SelectMany(x => x.Nodes.Where(y => y.DataId == target.DataId))
|
||||||
|
.Any(x => x.Locations.Any(y => Vector3.Distance(y.Position, target.Position) < 0.1f));
|
||||||
|
if (existingNode)
|
||||||
|
throw new Exception("Node already exists");
|
||||||
|
|
||||||
|
if (arguments.Contains("group"))
|
||||||
|
AddToNewGroup(root, target);
|
||||||
|
else
|
||||||
|
AddToExistingGroup(root, target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(targetFile, root) = CreateNewFile(gatheringPoint, target, string.Join(" ", arguments));
|
||||||
|
_chatGui.Print($"Creating new file under {targetFile.FullName}", "qG");
|
||||||
|
}
|
||||||
|
|
||||||
|
_plugin.Save(targetFile, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddToNewGroup(GatheringRoot root, IGameObject target)
|
||||||
|
{
|
||||||
|
root.Groups.Add(new GatheringNodeGroup
|
||||||
|
{
|
||||||
|
Nodes =
|
||||||
|
[
|
||||||
|
new GatheringNode
|
||||||
|
{
|
||||||
|
DataId = target.DataId,
|
||||||
|
Locations =
|
||||||
|
[
|
||||||
|
new GatheringLocation
|
||||||
|
{
|
||||||
|
Position = target.Position,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
_chatGui.Print("Added group.", "qG");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddToExistingGroup(GatheringRoot root, IGameObject target)
|
||||||
|
{
|
||||||
|
// find the same data id
|
||||||
|
var node = root.Groups.SelectMany(x => x.Nodes)
|
||||||
|
.SingleOrDefault(x => x.DataId == target.DataId);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
node.Locations.Add(new GatheringLocation
|
||||||
|
{
|
||||||
|
Position = target.Position,
|
||||||
|
});
|
||||||
|
_chatGui.Print($"Added location to existing node {target.DataId}.", "qG");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// find the closest group
|
||||||
|
var closestGroup = root.Groups
|
||||||
|
.Select(group => new
|
||||||
|
{
|
||||||
|
Group = group,
|
||||||
|
Distance = group.Nodes.Min(x =>
|
||||||
|
x.Locations.Min(y =>
|
||||||
|
Vector3.Distance(_clientState.LocalPlayer!.Position, y.Position)))
|
||||||
|
})
|
||||||
|
.OrderBy(x => x.Distance)
|
||||||
|
.First();
|
||||||
|
|
||||||
|
closestGroup.Group.Nodes.Add(new GatheringNode
|
||||||
|
{
|
||||||
|
DataId = target.DataId,
|
||||||
|
Locations =
|
||||||
|
[
|
||||||
|
new GatheringLocation
|
||||||
|
{
|
||||||
|
Position = target.Position,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
_chatGui.Print($"Added new node {target.DataId}.", "qG");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public (FileInfo targetFile, GatheringRoot root) CreateNewFile(GatheringPoint gatheringPoint, IGameObject target,
|
||||||
|
string fileName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fileName))
|
||||||
|
throw new ArgumentException(nameof(fileName));
|
||||||
|
|
||||||
|
// determine target folder
|
||||||
|
DirectoryInfo? targetFolder = _plugin.GetLocationsInTerritory(_clientState.TerritoryType).FirstOrDefault()
|
||||||
|
?.File.Directory;
|
||||||
|
if (targetFolder == null)
|
||||||
|
{
|
||||||
|
var territoryInfo = _dataManager.GetExcelSheet<TerritoryType>()!.GetRow(_clientState.TerritoryType)!;
|
||||||
|
targetFolder = _plugin.PathsDirectory
|
||||||
|
.CreateSubdirectory(ExpansionData.ExpansionFolders[(byte)territoryInfo.ExVersion.Row])
|
||||||
|
.CreateSubdirectory(territoryInfo.PlaceName.Value!.Name.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo targetFile =
|
||||||
|
new FileInfo(
|
||||||
|
Path.Combine(targetFolder.FullName, $"{gatheringPoint.GatheringPointBase.Row}_{fileName}.json"));
|
||||||
|
var root = new GatheringRoot
|
||||||
|
{
|
||||||
|
TerritoryId = _clientState.TerritoryType,
|
||||||
|
Groups =
|
||||||
|
[
|
||||||
|
new GatheringNodeGroup
|
||||||
|
{
|
||||||
|
Nodes =
|
||||||
|
[
|
||||||
|
new GatheringNode
|
||||||
|
{
|
||||||
|
DataId = target.DataId,
|
||||||
|
Locations =
|
||||||
|
[
|
||||||
|
new GatheringLocation
|
||||||
|
{
|
||||||
|
Position = target.Position
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
return (targetFile, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_commandManager.RemoveHandler("/qg");
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,16 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Dalamud.Game.ClientState.Objects;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using ECommons;
|
using ECommons;
|
||||||
using ECommons.Schedulers;
|
using ECommons.Schedulers;
|
||||||
using ECommons.SplatoonAPI;
|
using ECommons.SplatoonAPI;
|
||||||
|
using GatheringPathRenderer.Windows;
|
||||||
|
using Questionable.Model;
|
||||||
using Questionable.Model.Gathering;
|
using Questionable.Model.Gathering;
|
||||||
|
|
||||||
namespace GatheringPathRenderer;
|
namespace GatheringPathRenderer;
|
||||||
@ -16,73 +21,83 @@ namespace GatheringPathRenderer;
|
|||||||
public sealed class RendererPlugin : IDalamudPlugin
|
public sealed class RendererPlugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
private const long OnTerritoryChange = -2;
|
private const long OnTerritoryChange = -2;
|
||||||
|
|
||||||
|
private readonly WindowSystem _windowSystem = new(nameof(RendererPlugin));
|
||||||
|
private readonly List<uint> _colors = [0xFFFF2020, 0xFF20FF20, 0xFF2020FF, 0xFFFFFF20, 0xFFFF20FF, 0xFF20FFFF];
|
||||||
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly IDalamudPluginInterface _pluginInterface;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
private readonly List<(ushort Id, GatheringRoot Root)> _gatheringLocations = [];
|
|
||||||
|
|
||||||
public RendererPlugin(IDalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog)
|
private readonly EditorCommands _editorCommands;
|
||||||
|
private readonly EditorWindow _editorWindow;
|
||||||
|
|
||||||
|
private readonly List<GatheringLocationContext> _gatheringLocations = [];
|
||||||
|
|
||||||
|
public RendererPlugin(IDalamudPluginInterface pluginInterface, IClientState clientState,
|
||||||
|
ICommandManager commandManager, IDataManager dataManager, ITargetManager targetManager, IChatGui chatGui,
|
||||||
|
IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
|
|
||||||
|
_editorCommands = new EditorCommands(this, dataManager, commandManager, targetManager, clientState, chatGui);
|
||||||
|
_editorWindow = new EditorWindow(this, _editorCommands, dataManager, targetManager, clientState) { IsOpen = true };
|
||||||
|
_windowSystem.AddWindow(_editorWindow);
|
||||||
|
|
||||||
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
|
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
|
||||||
.Subscribe(Reload);
|
.Subscribe(Reload);
|
||||||
|
|
||||||
ECommonsMain.Init(pluginInterface, this, Module.SplatoonAPI);
|
ECommonsMain.Init(pluginInterface, this, Module.SplatoonAPI);
|
||||||
LoadGatheringLocationsFromDirectory();
|
LoadGatheringLocationsFromDirectory();
|
||||||
|
|
||||||
|
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||||
_clientState.TerritoryChanged += TerritoryChanged;
|
_clientState.TerritoryChanged += TerritoryChanged;
|
||||||
if (_clientState.IsLoggedIn)
|
if (_clientState.IsLoggedIn)
|
||||||
TerritoryChanged(_clientState.TerritoryType);
|
TerritoryChanged(_clientState.TerritoryType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Reload()
|
internal DirectoryInfo PathsDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Parent;
|
||||||
|
if (solutionDirectory != null)
|
||||||
|
{
|
||||||
|
DirectoryInfo pathProjectDirectory =
|
||||||
|
new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "GatheringPaths"));
|
||||||
|
if (pathProjectDirectory.Exists)
|
||||||
|
return pathProjectDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Unable to resolve project path");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Reload()
|
||||||
{
|
{
|
||||||
LoadGatheringLocationsFromDirectory();
|
LoadGatheringLocationsFromDirectory();
|
||||||
TerritoryChanged(_clientState.TerritoryType);
|
Redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadGatheringLocationsFromDirectory()
|
private void LoadGatheringLocationsFromDirectory()
|
||||||
{
|
{
|
||||||
_gatheringLocations.Clear();
|
_gatheringLocations.Clear();
|
||||||
|
|
||||||
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Parent;
|
try
|
||||||
if (solutionDirectory != null)
|
|
||||||
{
|
{
|
||||||
DirectoryInfo pathProjectDirectory =
|
foreach (var expansionFolder in ExpansionData.ExpansionFolders.Values)
|
||||||
new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "GatheringPaths"));
|
LoadFromDirectory(
|
||||||
if (pathProjectDirectory.Exists)
|
new DirectoryInfo(Path.Combine(PathsDirectory.FullName, expansionFolder)));
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "2.x - A Realm Reborn")));
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "3.x - Heavensward")));
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "4.x - Stormblood")));
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "5.x - Shadowbringers")));
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "6.x - Endwalker")));
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "7.x - Dawntrail")));
|
|
||||||
|
|
||||||
_pluginLog.Information(
|
_pluginLog.Information(
|
||||||
$"Loaded {_gatheringLocations.Count} gathering root locations from project directory");
|
$"Loaded {_gatheringLocations.Count} gathering root locations from project directory");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_pluginLog.Error(e, "Failed to load quests from project directory");
|
_pluginLog.Error(e, "Failed to load paths from project directory");
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_pluginLog.Warning($"Project directory {pathProjectDirectory} does not exist");
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
_pluginLog.Warning($"Solution directory {solutionDirectory} does not exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFromDirectory(DirectoryInfo directory)
|
private void LoadFromDirectory(DirectoryInfo directory)
|
||||||
@ -96,7 +111,7 @@ public sealed class RendererPlugin : IDalamudPlugin
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
|
||||||
LoadLocationFromStream(fileInfo.Name, stream);
|
LoadLocationFromStream(fileInfo, stream);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -108,26 +123,78 @@ public sealed class RendererPlugin : IDalamudPlugin
|
|||||||
LoadFromDirectory(childDirectory);
|
LoadFromDirectory(childDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadLocationFromStream(string fileName, Stream stream)
|
private void LoadLocationFromStream(FileInfo fileInfo, Stream stream)
|
||||||
{
|
{
|
||||||
var locationNode = JsonNode.Parse(stream)!;
|
var locationNode = JsonNode.Parse(stream)!;
|
||||||
GatheringRoot root = locationNode.Deserialize<GatheringRoot>()!;
|
GatheringRoot root = locationNode.Deserialize<GatheringRoot>()!;
|
||||||
_gatheringLocations.Add((ushort.Parse(fileName.Split('_')[0]), root));
|
_gatheringLocations.Add(new GatheringLocationContext(fileInfo, ushort.Parse(fileInfo.Name.Split('_')[0]),
|
||||||
|
root));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TerritoryChanged(ushort territoryId)
|
internal IEnumerable<GatheringLocationContext> GetLocationsInTerritory(ushort territoryId)
|
||||||
|
=> _gatheringLocations.Where(x => x.Root.TerritoryId == territoryId);
|
||||||
|
|
||||||
|
internal void Save(FileInfo targetFile, GatheringRoot root)
|
||||||
|
{
|
||||||
|
JsonSerializerOptions options = new()
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
|
||||||
|
WriteIndented = true,
|
||||||
|
};
|
||||||
|
using (var stream = File.Create(targetFile.FullName))
|
||||||
|
{
|
||||||
|
var jsonNode = (JsonObject)JsonSerializer.SerializeToNode(root, options)!;
|
||||||
|
var newNode = new JsonObject();
|
||||||
|
newNode.Add("$schema",
|
||||||
|
"https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json");
|
||||||
|
foreach (var (key, value) in jsonNode)
|
||||||
|
newNode.Add(key, value?.DeepClone());
|
||||||
|
|
||||||
|
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions
|
||||||
|
{
|
||||||
|
Indented = true
|
||||||
|
});
|
||||||
|
newNode.WriteTo(writer, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TerritoryChanged(ushort territoryId) => Redraw();
|
||||||
|
|
||||||
|
internal void Redraw()
|
||||||
{
|
{
|
||||||
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
|
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
|
||||||
|
|
||||||
var elements = _gatheringLocations
|
var elements = GetLocationsInTerritory(_clientState.TerritoryType)
|
||||||
.Where(x => x.Root.TerritoryId == territoryId)
|
.SelectMany(location =>
|
||||||
.SelectMany(v =>
|
location.Root.Groups.SelectMany(group =>
|
||||||
v.Root.Groups.SelectMany(group =>
|
|
||||||
group.Nodes.SelectMany(node => node.Locations
|
group.Nodes.SelectMany(node => node.Locations
|
||||||
.SelectMany(x =>
|
.SelectMany(x =>
|
||||||
new List<Element>
|
{
|
||||||
|
bool isCone = false;
|
||||||
|
int minimumAngle = 0;
|
||||||
|
int maximumAngle = 0;
|
||||||
|
if (_editorWindow.TryGetOverride(x.InternalId, out LocationOverride? locationOverride) && locationOverride != null)
|
||||||
{
|
{
|
||||||
new Element(x.IsCone()
|
if (locationOverride.IsCone())
|
||||||
|
{
|
||||||
|
isCone = true;
|
||||||
|
minimumAngle = locationOverride.MinimumAngle.GetValueOrDefault();
|
||||||
|
maximumAngle = locationOverride.MaximumAngle.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isCone && x.IsCone())
|
||||||
|
{
|
||||||
|
isCone = true;
|
||||||
|
minimumAngle = x.MinimumAngle.GetValueOrDefault();
|
||||||
|
maximumAngle = x.MaximumAngle.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<Element>
|
||||||
|
{
|
||||||
|
new Element(isCone
|
||||||
? ElementType.ConeAtFixedCoordinates
|
? ElementType.ConeAtFixedCoordinates
|
||||||
: ElementType.CircleAtFixedCoordinates)
|
: ElementType.CircleAtFixedCoordinates)
|
||||||
{
|
{
|
||||||
@ -135,12 +202,13 @@ public sealed class RendererPlugin : IDalamudPlugin
|
|||||||
refY = x.Position.Z,
|
refY = x.Position.Z,
|
||||||
refZ = x.Position.Y,
|
refZ = x.Position.Y,
|
||||||
Filled = true,
|
Filled = true,
|
||||||
radius = x.MinimumDistance,
|
radius = x.CalculateMinimumDistance(),
|
||||||
Donut = x.MaximumDistance - x.MinimumDistance,
|
Donut = x.CalculateMaximumDistance() - x.CalculateMinimumDistance(),
|
||||||
color = 0x2020FF80,
|
color = _colors[location.Root.Groups.IndexOf(group) % _colors.Count],
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
coneAngleMin = x.IsCone() ? (int)x.MinimumAngle.GetValueOrDefault() : 0,
|
coneAngleMin = minimumAngle,
|
||||||
coneAngleMax = x.IsCone() ? (int)x.MaximumAngle.GetValueOrDefault() : 0
|
coneAngleMax = maximumAngle,
|
||||||
|
tether = false,
|
||||||
},
|
},
|
||||||
new Element(ElementType.CircleAtFixedCoordinates)
|
new Element(ElementType.CircleAtFixedCoordinates)
|
||||||
{
|
{
|
||||||
@ -149,9 +217,11 @@ public sealed class RendererPlugin : IDalamudPlugin
|
|||||||
refZ = x.Position.Y,
|
refZ = x.Position.Y,
|
||||||
color = 0x00000000,
|
color = 0x00000000,
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
overlayText = $"{v.Id} // {node.DataId} / {node.Locations.IndexOf(x)}"
|
overlayText =
|
||||||
|
$"{location.Root.Groups.IndexOf(group)} // {node.DataId} / {node.Locations.IndexOf(x)}",
|
||||||
}
|
}
|
||||||
}))))
|
};
|
||||||
|
}))))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (elements.Count == 0)
|
if (elements.Count == 0)
|
||||||
@ -179,11 +249,16 @@ public sealed class RendererPlugin : IDalamudPlugin
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_clientState.TerritoryChanged -= TerritoryChanged;
|
_clientState.TerritoryChanged -= TerritoryChanged;
|
||||||
|
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||||
|
|
||||||
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
|
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
|
||||||
ECommonsMain.Dispose();
|
ECommonsMain.Dispose();
|
||||||
|
|
||||||
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
|
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
|
||||||
.Unsubscribe(Reload);
|
.Unsubscribe(Reload);
|
||||||
|
|
||||||
|
_editorCommands.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal sealed record GatheringLocationContext(FileInfo File, ushort Id, GatheringRoot Root);
|
||||||
}
|
}
|
||||||
|
190
GatheringPathRenderer/Windows/EditorWindow.cs
Normal file
190
GatheringPathRenderer/Windows/EditorWindow.cs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Dalamud.Game.ClientState.Objects;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using ImGuiNET;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Questionable.Model.Gathering;
|
||||||
|
|
||||||
|
namespace GatheringPathRenderer.Windows;
|
||||||
|
|
||||||
|
internal sealed class EditorWindow : Window
|
||||||
|
{
|
||||||
|
private readonly RendererPlugin _plugin;
|
||||||
|
private readonly EditorCommands _editorCommands;
|
||||||
|
private readonly IDataManager _dataManager;
|
||||||
|
private readonly ITargetManager _targetManager;
|
||||||
|
private readonly IClientState _clientState;
|
||||||
|
|
||||||
|
private readonly Dictionary<Guid, LocationOverride> _changes = [];
|
||||||
|
|
||||||
|
private IGameObject? _target;
|
||||||
|
private (RendererPlugin.GatheringLocationContext, GatheringLocation)? _targetLocation;
|
||||||
|
private string _newFileName = string.Empty;
|
||||||
|
|
||||||
|
public EditorWindow(RendererPlugin plugin, EditorCommands editorCommands, IDataManager dataManager,
|
||||||
|
ITargetManager targetManager, IClientState clientState)
|
||||||
|
: base("Gathering Path Editor###QuestionableGatheringPathEditor")
|
||||||
|
{
|
||||||
|
_plugin = plugin;
|
||||||
|
_editorCommands = editorCommands;
|
||||||
|
_dataManager = dataManager;
|
||||||
|
_targetManager = targetManager;
|
||||||
|
_clientState = clientState;
|
||||||
|
|
||||||
|
SizeConstraints = new WindowSizeConstraints
|
||||||
|
{
|
||||||
|
MinimumSize = new Vector2(300, 300),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
_target = _targetManager.Target;
|
||||||
|
if (_target == null || _target.ObjectKind != ObjectKind.GatheringPoint)
|
||||||
|
{
|
||||||
|
_targetLocation = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gatheringLocations = _plugin.GetLocationsInTerritory(_clientState.TerritoryType);
|
||||||
|
var location = gatheringLocations.SelectMany(context =>
|
||||||
|
context.Root.Groups.SelectMany(group =>
|
||||||
|
group.Nodes
|
||||||
|
.Where(node => node.DataId == _target.DataId)
|
||||||
|
.SelectMany(node => node.Locations)
|
||||||
|
.Where(location => Vector3.Distance(location.Position, _target.Position) < 0.1f)
|
||||||
|
.Select(location => new { Context = context, Location = location })))
|
||||||
|
.FirstOrDefault();
|
||||||
|
if (location == null)
|
||||||
|
{
|
||||||
|
_targetLocation = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_targetLocation = (location.Context, location.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool DrawConditions()
|
||||||
|
{
|
||||||
|
return _target != null || _targetLocation != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (_target != null && _targetLocation != null)
|
||||||
|
{
|
||||||
|
var context = _targetLocation.Value.Item1;
|
||||||
|
var location = _targetLocation.Value.Item2;
|
||||||
|
ImGui.Text(context.File.Directory?.Name ?? string.Empty);
|
||||||
|
ImGui.Indent();
|
||||||
|
ImGui.Text(context.File.Name);
|
||||||
|
ImGui.Unindent();
|
||||||
|
ImGui.Text($"{_target.DataId} // {location.InternalId}");
|
||||||
|
ImGui.Text(string.Create(CultureInfo.InvariantCulture, $"{location.Position:G}"));
|
||||||
|
|
||||||
|
if (!_changes.TryGetValue(location.InternalId, out LocationOverride? locationOverride))
|
||||||
|
{
|
||||||
|
locationOverride = new LocationOverride();
|
||||||
|
_changes[location.InternalId] = locationOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minAngle = locationOverride.MinimumAngle ?? location.MinimumAngle.GetValueOrDefault();
|
||||||
|
if (ImGui.DragInt("Min Angle", ref minAngle, 5, -180, 360))
|
||||||
|
{
|
||||||
|
locationOverride.MinimumAngle = minAngle;
|
||||||
|
locationOverride.MaximumAngle ??= location.MaximumAngle.GetValueOrDefault();
|
||||||
|
_plugin.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxAngle = locationOverride.MaximumAngle ?? location.MaximumAngle.GetValueOrDefault();
|
||||||
|
if (ImGui.DragInt("Max Angle", ref maxAngle, 5, -180, 360))
|
||||||
|
{
|
||||||
|
locationOverride.MinimumAngle ??= location.MinimumAngle.GetValueOrDefault();
|
||||||
|
locationOverride.MaximumAngle = maxAngle;
|
||||||
|
_plugin.Redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.BeginDisabled(locationOverride.MinimumAngle == null && locationOverride.MaximumAngle == null);
|
||||||
|
if (ImGui.Button("Save"))
|
||||||
|
{
|
||||||
|
location.MinimumAngle = locationOverride.MinimumAngle;
|
||||||
|
location.MaximumAngle = locationOverride.MaximumAngle;
|
||||||
|
_plugin.Save(context.File, context.Root);
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Reset"))
|
||||||
|
{
|
||||||
|
_changes[location.InternalId] = new LocationOverride();
|
||||||
|
_plugin.Redraw();
|
||||||
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (_target != null)
|
||||||
|
{
|
||||||
|
var gatheringPoint = _dataManager.GetExcelSheet<GatheringPoint>()!.GetRow(_target.DataId);
|
||||||
|
if (gatheringPoint == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var locationsInTerritory = _plugin.GetLocationsInTerritory(_clientState.TerritoryType).ToList();
|
||||||
|
var location = locationsInTerritory.SingleOrDefault(x => x.Id == gatheringPoint.GatheringPointBase.Row);
|
||||||
|
if (location != null)
|
||||||
|
{
|
||||||
|
var targetFile = location.File;
|
||||||
|
var root = location.Root;
|
||||||
|
|
||||||
|
if (ImGui.Button("Add to closest group"))
|
||||||
|
{
|
||||||
|
_editorCommands.AddToExistingGroup(root, _target);
|
||||||
|
_plugin.Save(targetFile, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.BeginDisabled(root.Groups.Any(group => group.Nodes.Any(node => node.DataId == _target.DataId)));
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Add as new group"))
|
||||||
|
{
|
||||||
|
_editorCommands.AddToNewGroup(root, _target);
|
||||||
|
_plugin.Save(targetFile, root);
|
||||||
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
_plugin.Save(targetFile, root);
|
||||||
|
_newFileName = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetOverride(Guid internalId, out LocationOverride? locationOverride)
|
||||||
|
=> _changes.TryGetValue(internalId, out locationOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LocationOverride
|
||||||
|
{
|
||||||
|
public int? MinimumAngle { get; set; }
|
||||||
|
public int? MaximumAngle { get; set; }
|
||||||
|
public float? MinimumDistance { get; set; }
|
||||||
|
public float? MaximumDistance { get; set; }
|
||||||
|
|
||||||
|
public bool IsCone()
|
||||||
|
{
|
||||||
|
return MinimumAngle != null && MaximumAngle != null && MinimumAngle != MaximumAngle;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
|
||||||
|
"Author": [],
|
||||||
|
"TerritoryId": 1187,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
|
||||||
|
"Author": [],
|
||||||
|
"TerritoryId": 1187,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 34860,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -169.8177,
|
||||||
|
"Y": -85.61841,
|
||||||
|
"Z": -240.1007
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 34859,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": -131.9198,
|
||||||
|
"Y": -89.88039,
|
||||||
|
"Z": -249.5422
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://git.carvel.li/liza/Questionable/raw/branch/master/GatheringPaths/gatheringlocation-v1.json",
|
||||||
|
"Author": [],
|
||||||
|
"TerritoryId": 1187,
|
||||||
|
"Groups": [
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 34866,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 242.7737,
|
||||||
|
"Y": -135.9734,
|
||||||
|
"Z": -431.2313
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 34865,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 269.7338,
|
||||||
|
"Y": -134.0488,
|
||||||
|
"Z": -381.6242
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Nodes": [
|
||||||
|
{
|
||||||
|
"DataId": 34868,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 389.1952,
|
||||||
|
"Y": -154.3099,
|
||||||
|
"Z": -368.3658
|
||||||
|
},
|
||||||
|
"MinimumAngle": 105,
|
||||||
|
"MaximumAngle": 345
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DataId": 34863,
|
||||||
|
"Locations": [
|
||||||
|
{
|
||||||
|
"Position": {
|
||||||
|
"X": 323.8758,
|
||||||
|
"Y": -162.9682,
|
||||||
|
"Z": -648.8156
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -92,8 +92,7 @@
|
|||||||
"$schema",
|
"$schema",
|
||||||
"Author",
|
"Author",
|
||||||
"TerritoryId",
|
"TerritoryId",
|
||||||
"AetheryteShortcut",
|
"Groups"
|
||||||
"Nodes"
|
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"$defs": {
|
"$defs": {
|
||||||
|
@ -57,8 +57,8 @@ public sealed class VectorConverter : JsonConverter<Vector3>
|
|||||||
{
|
{
|
||||||
writer.WriteStartObject();
|
writer.WriteStartObject();
|
||||||
writer.WriteNumber(nameof(Vector3.X), value.X);
|
writer.WriteNumber(nameof(Vector3.X), value.X);
|
||||||
writer.WriteNumber(nameof(Vector3.Y), value.X);
|
writer.WriteNumber(nameof(Vector3.Y), value.Y);
|
||||||
writer.WriteNumber(nameof(Vector3.Z), value.X);
|
writer.WriteNumber(nameof(Vector3.Z), value.Z);
|
||||||
writer.WriteEndObject();
|
writer.WriteEndObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
Questionable.Model/ExpansionVersion.cs
Normal file
16
Questionable.Model/ExpansionVersion.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Questionable.Model;
|
||||||
|
|
||||||
|
public static class ExpansionData
|
||||||
|
{
|
||||||
|
public static IReadOnlyDictionary<byte, string> ExpansionFolders = new Dictionary<byte, string>()
|
||||||
|
{
|
||||||
|
{ 0, "2.x - A Realm Reborn" },
|
||||||
|
{ 1, "3.x - Heavensward" },
|
||||||
|
{ 2, "4.x - Stormblood" },
|
||||||
|
{ 3, "5.x - Shadowbringers" },
|
||||||
|
{ 4, "6.x - Endwalker" },
|
||||||
|
{ 5, "7.x - Dawntrail" }
|
||||||
|
};
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Numerics;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Questionable.Model.Common.Converter;
|
using Questionable.Model.Common.Converter;
|
||||||
|
|
||||||
@ -6,16 +7,22 @@ namespace Questionable.Model.Gathering;
|
|||||||
|
|
||||||
public sealed class GatheringLocation
|
public sealed class GatheringLocation
|
||||||
{
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public Guid InternalId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
[JsonConverter(typeof(VectorConverter))]
|
[JsonConverter(typeof(VectorConverter))]
|
||||||
public Vector3 Position { get; set; }
|
public Vector3 Position { get; set; }
|
||||||
|
|
||||||
public float? MinimumAngle { get; set; }
|
public int? MinimumAngle { get; set; }
|
||||||
public float? MaximumAngle { get; set; }
|
public int? MaximumAngle { get; set; }
|
||||||
public float MinimumDistance { get; set; } = 1f;
|
public float? MinimumDistance { get; set; }
|
||||||
public float MaximumDistance { get; set; } = 3f;
|
public float? MaximumDistance { get; set; }
|
||||||
|
|
||||||
public bool IsCone()
|
public bool IsCone()
|
||||||
{
|
{
|
||||||
return MinimumAngle != null && MaximumAngle != null;
|
return MinimumAngle != null && MaximumAngle != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float CalculateMinimumDistance() => MinimumDistance ?? 1f;
|
||||||
|
public float CalculateMaximumDistance() => MaximumDistance ?? 3f;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,16 @@ internal sealed class QuestRegistry
|
|||||||
|
|
||||||
ValidateQuests();
|
ValidateQuests();
|
||||||
Reloaded?.Invoke(this, EventArgs.Empty);
|
Reloaded?.Invoke(this, EventArgs.Empty);
|
||||||
_reloadDataIpc.SendMessage();
|
try
|
||||||
|
{
|
||||||
|
_reloadDataIpc.SendMessage();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// why does this even throw
|
||||||
|
_logger.LogWarning(e, "Error during Reload.SendMessage IPC");
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Loaded {Count} quests in total", _quests.Count);
|
_logger.LogInformation("Loaded {Count} quests in total", _quests.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,24 +114,10 @@ internal sealed class QuestRegistry
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadFromDirectory(
|
foreach (var expansionFolder in ExpansionData.ExpansionFolders.Values)
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "2.x - A Realm Reborn")),
|
LoadFromDirectory(
|
||||||
LogLevel.Trace);
|
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, expansionFolder)),
|
||||||
LoadFromDirectory(
|
LogLevel.Trace);
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "3.x - Heavensward")),
|
|
||||||
LogLevel.Trace);
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "4.x - Stormblood")),
|
|
||||||
LogLevel.Trace);
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "5.x - Shadowbringers")),
|
|
||||||
LogLevel.Trace);
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "6.x - Endwalker")),
|
|
||||||
LogLevel.Trace);
|
|
||||||
LoadFromDirectory(
|
|
||||||
new DirectoryInfo(Path.Combine(pathProjectDirectory.FullName, "7.x - Dawntrail")),
|
|
||||||
LogLevel.Trace);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user