Questionable/GatheringPathRenderer/RendererPlugin.cs

190 lines
7.3 KiB
C#
Raw Normal View History

2024-08-02 18:04:45 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ECommons;
using ECommons.Schedulers;
using ECommons.SplatoonAPI;
using Questionable.Model.Gathering;
2024-08-02 16:30:21 +00:00
namespace GatheringPathRenderer;
public sealed class RendererPlugin : IDalamudPlugin
{
2024-08-02 18:04:45 +00:00
private const long OnTerritoryChange = -2;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly IPluginLog _pluginLog;
private readonly List<(ushort Id, GatheringRoot Root)> _gatheringLocations = [];
public RendererPlugin(IDalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog)
{
_pluginInterface = pluginInterface;
_clientState = clientState;
_pluginLog = pluginLog;
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
.Subscribe(Reload);
ECommonsMain.Init(pluginInterface, this, Module.SplatoonAPI);
LoadGatheringLocationsFromDirectory();
_clientState.TerritoryChanged += TerritoryChanged;
if (_clientState.IsLoggedIn)
TerritoryChanged(_clientState.TerritoryType);
}
private void Reload()
{
LoadGatheringLocationsFromDirectory();
TerritoryChanged(_clientState.TerritoryType);
}
private void LoadGatheringLocationsFromDirectory()
{
_gatheringLocations.Clear();
DirectoryInfo? solutionDirectory = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Parent;
if (solutionDirectory != null)
{
DirectoryInfo pathProjectDirectory =
new DirectoryInfo(Path.Combine(solutionDirectory.FullName, "GatheringPaths"));
if (pathProjectDirectory.Exists)
{
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(
$"Loaded {_gatheringLocations.Count} gathering root locations from project directory");
}
catch (Exception e)
{
_pluginLog.Error(e, "Failed to load quests 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)
{
if (!directory.Exists)
return;
_pluginLog.Information($"Loading locations from {directory}");
foreach (FileInfo fileInfo in directory.GetFiles("*.json"))
{
try
{
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
LoadLocationFromStream(fileInfo.Name, stream);
}
catch (Exception e)
{
throw new InvalidDataException($"Unable to load file {fileInfo.FullName}", e);
}
}
foreach (DirectoryInfo childDirectory in directory.GetDirectories())
LoadFromDirectory(childDirectory);
}
private void LoadLocationFromStream(string fileName, Stream stream)
{
var locationNode = JsonNode.Parse(stream)!;
GatheringRoot root = locationNode.Deserialize<GatheringRoot>()!;
_gatheringLocations.Add((ushort.Parse(fileName.Split('_')[0]), root));
}
private void TerritoryChanged(ushort territoryId)
{
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
var elements = _gatheringLocations
.Where(x => x.Root.TerritoryId == territoryId)
.SelectMany(v =>
v.Root.Groups.SelectMany(group =>
group.Nodes.SelectMany(node => node.Locations
.SelectMany(x =>
new List<Element>
{
new Element(x.IsCone()
? ElementType.ConeAtFixedCoordinates
: ElementType.CircleAtFixedCoordinates)
{
refX = x.Position.X,
refY = x.Position.Z,
refZ = x.Position.Y,
Filled = true,
radius = x.MinimumDistance,
Donut = x.MaximumDistance - x.MinimumDistance,
color = 0x2020FF80,
Enabled = true,
coneAngleMin = x.IsCone() ? (int)x.MinimumAngle.GetValueOrDefault() : 0,
coneAngleMax = x.IsCone() ? (int)x.MaximumAngle.GetValueOrDefault() : 0
},
new Element(ElementType.CircleAtFixedCoordinates)
{
refX = x.Position.X,
refY = x.Position.Z,
refZ = x.Position.Y,
color = 0x00000000,
Enabled = true,
overlayText = $"{v.Id} // {node.DataId} / {node.Locations.IndexOf(x)}"
}
}))))
.ToList();
if (elements.Count == 0)
{
_pluginLog.Information("No new elements to render.");
return;
}
_ = new TickScheduler(delegate
{
try
{
Splatoon.AddDynamicElements("GatheringPathRenderer",
elements.ToArray(),
new[] { OnTerritoryChange });
_pluginLog.Information($"Created {elements.Count} splatoon elements.");
}
catch (Exception e)
{
_pluginLog.Error(e, "Unable to create splatoon layer");
}
});
}
2024-08-02 16:30:21 +00:00
public void Dispose()
{
2024-08-02 18:04:45 +00:00
_clientState.TerritoryChanged -= TerritoryChanged;
Splatoon.RemoveDynamicElements("GatheringPathRenderer");
ECommonsMain.Dispose();
2024-08-02 16:30:21 +00:00
2024-08-02 18:04:45 +00:00
_pluginInterface.GetIpcSubscriber<object>("Questionable.ReloadData")
.Unsubscribe(Reload);
2024-08-02 16:30:21 +00:00
}
}