diff --git a/Pal.Client/Configuration.cs b/Pal.Client/Configuration.cs
index ec0b97e..f8e5fbf 100644
--- a/Pal.Client/Configuration.cs
+++ b/Pal.Client/Configuration.cs
@@ -22,6 +22,7 @@ namespace Pal.Client
#region Saved configuration values
public bool FirstUse { get; set; } = true;
public EMode Mode { get; set; } = EMode.Offline;
+ public ERenderer Renderer { get; set; } = ERenderer.Splatoon;
[Obsolete]
public string? DebugAccountId { private get; set; }
@@ -159,6 +160,15 @@ namespace Pal.Client
Offline = 2,
}
+ public enum ERenderer
+ {
+ ///
+ Simple = 0,
+
+ ///
+ Splatoon = 1,
+ }
+
public class AccountInfo
{
[JsonConverter(typeof(AccountIdConverter))]
diff --git a/Pal.Client/Marker.cs b/Pal.Client/Marker.cs
index 29f4932..72d2551 100644
--- a/Pal.Client/Marker.cs
+++ b/Pal.Client/Marker.cs
@@ -1,4 +1,5 @@
using ECommons.SplatoonAPI;
+using Pal.Client.Rendering;
using Pal.Common;
using Palace;
using System;
@@ -57,6 +58,9 @@ namespace Pal.Client
public string? SinceVersion { get; set; }
[JsonIgnore]
+ public IRenderElement? RenderElement { get; set; }
+
+ [Obsolete]
public Element? SplatoonElement { get; set; }
public Marker(EType type, Vector3 position, Guid? networkId = null)
diff --git a/Pal.Client/Pal.Client.csproj b/Pal.Client/Pal.Client.csproj
index dd5f9bf..ab9ba20 100644
--- a/Pal.Client/Pal.Client.csproj
+++ b/Pal.Client/Pal.Client.csproj
@@ -3,7 +3,7 @@
net7.0-windows
11.0
- 2.6
+ 2.7
enable
diff --git a/Pal.Client/Plugin.cs b/Pal.Client/Plugin.cs
index 4d8031a..d133938 100644
--- a/Pal.Client/Plugin.cs
+++ b/Pal.Client/Plugin.cs
@@ -9,11 +9,10 @@ using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Plugin;
using ECommons;
-using ECommons.Schedulers;
-using ECommons.SplatoonAPI;
using Grpc.Core;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
+using Pal.Client.Rendering;
using Pal.Client.Scheduled;
using Pal.Client.Windows;
using Pal.Common;
@@ -30,18 +29,8 @@ namespace Pal.Client
{
public class Plugin : IDalamudPlugin
{
- private const long ON_TERRITORY_CHANGE = -2;
- private const uint COLOR_INVISIBLE = 0;
- private const string SPLATOON_TRAP_HOARD = "PalacePal.TrapHoard";
- private const string SPLATOON_REGULAR_COFFERS = "PalacePal.RegularCoffers";
+ internal const uint COLOR_INVISIBLE = 0;
- private readonly static Dictionary _markerConfig = new Dictionary
- {
- { Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
- { Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
- { Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
- { Marker.EType.Debug, new MarkerConfig { Radius = 1.5f } },
- };
private LocalizedChatMessages _localizedChatMessages = new();
internal ConcurrentDictionary FloorMarkers { get; } = new();
@@ -54,6 +43,7 @@ namespace Pal.Client
internal Queue EarlyEventQueue { get; } = new();
internal Queue LateEventQueue { get; } = new();
internal ConcurrentQueue NextUpdateObjects { get; } = new();
+ internal IRenderer Renderer { get; private set; } = null!;
public string Name => "Palace Pal";
@@ -74,12 +64,13 @@ namespace Pal.Client
}
#endif
- ECommonsMain.Init(pluginInterface, this, Module.SplatoonAPI);
-
pluginInterface.Create();
Service.Plugin = this;
Service.Configuration = (Configuration?)pluginInterface.GetPluginConfig() ?? pluginInterface.Create()!;
Service.Configuration.Migrate();
+
+ ResetRenderer();
+
Service.Hooks = new Hooks();
var agreementWindow = pluginInterface.Create();
@@ -101,7 +92,7 @@ namespace Pal.Client
Service.WindowSystem.AddWindow(statisticsWindow);
}
- pluginInterface.UiBuilder.Draw += Service.WindowSystem.Draw;
+ pluginInterface.UiBuilder.Draw += Draw;
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
Service.Framework.Update += OnFrameworkUpdate;
Service.Chat.ChatMessage += OnChatMessage;
@@ -197,7 +188,7 @@ namespace Pal.Client
if (!disposing) return;
Service.CommandManager.RemoveHandler("/pal");
- Service.PluginInterface.UiBuilder.Draw -= Service.WindowSystem.Draw;
+ Service.PluginInterface.UiBuilder.Draw -= Draw;
Service.PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
Service.Framework.Update -= OnFrameworkUpdate;
Service.Chat.ChatMessage -= OnChatMessage;
@@ -207,16 +198,8 @@ namespace Pal.Client
Service.RemoteApi.Dispose();
Service.Hooks.Dispose();
- try
- {
- Splatoon.RemoveDynamicElements(SPLATOON_TRAP_HOARD);
- Splatoon.RemoveDynamicElements(SPLATOON_REGULAR_COFFERS);
- }
- catch
- {
- // destroyed on territory change either way
- }
- ECommonsMain.Dispose();
+ if (Renderer is IDisposable disposable)
+ disposable.Dispose();
}
public void Dispose()
@@ -321,6 +304,7 @@ namespace Pal.Client
return FloorMarkers.GetOrAdd(territoryType, tt => LocalState.Load(tt) ?? new LocalState(tt));
}
+ #region Rendering markers
private void HandlePersistentMarkers(LocalState currentFloor, IList visibleMarkers, bool saveMarkers, bool recreateLayout)
{
var config = Service.Configuration;
@@ -360,14 +344,14 @@ namespace Pal.Client
foreach (var marker in currentFloorMarkers)
{
uint desiredColor = DetermineColor(marker, visibleMarkers);
- if (marker.SplatoonElement == null || !marker.SplatoonElement.IsValid())
+ if (marker.RenderElement == null || !marker.RenderElement.IsValid)
{
recreateLayout = true;
break;
}
- if (marker.SplatoonElement.color != desiredColor)
- marker.SplatoonElement.color = desiredColor;
+ if (marker.RenderElement.Color != desiredColor)
+ marker.RenderElement.Color = desiredColor;
}
}
catch (Exception e)
@@ -403,30 +387,24 @@ namespace Pal.Client
if (recreateLayout)
{
- Splatoon.RemoveDynamicElements(SPLATOON_TRAP_HOARD);
+ Renderer.ResetLayer(ELayer.TrapHoard);
- List elements = new List();
+ List elements = new();
foreach (var marker in currentFloorMarkers)
{
if (marker.Seen || config.Mode == Configuration.EMode.Online || (marker.WasImported && marker.Imports.Count > 0))
{
if (marker.Type == Marker.EType.Trap && config.ShowTraps)
{
- var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
- marker.SplatoonElement = element;
- elements.Add(element);
+ CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers));
}
else if (marker.Type == Marker.EType.Hoard && config.ShowHoard)
{
- var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
- marker.SplatoonElement = element;
- elements.Add(element);
+ CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers));
}
else if (marker.Type == Marker.EType.Debug && Service.Configuration.BetaKey == "VFX")
{
- var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
- marker.SplatoonElement = element;
- elements.Add(element);
+ CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers));
}
}
}
@@ -434,18 +412,37 @@ namespace Pal.Client
if (elements.Count == 0)
return;
- // we need to delay this, as the current framework update could be before splatoon's, in which case it would immediately delete the layout
- new TickScheduler(delegate
+ Renderer.SetLayer(ELayer.TrapHoard, elements);
+ }
+ }
+
+ private void HandleEphemeralMarkers(IList visibleMarkers, bool recreateLayout)
+ {
+ recreateLayout |= EphemeralMarkers.Any(existingMarker => !visibleMarkers.Any(x => x == existingMarker));
+ recreateLayout |= visibleMarkers.Any(visibleMarker => !EphemeralMarkers.Any(x => x == visibleMarker));
+
+ if (recreateLayout)
+ {
+ Renderer.ResetLayer(ELayer.RegularCoffers);
+ EphemeralMarkers.Clear();
+
+ var config = Service.Configuration;
+
+ List elements = new();
+ foreach (var marker in visibleMarkers)
{
- try
+ EphemeralMarkers.Add(marker);
+
+ if (marker.Type == Marker.EType.SilverCoffer && config.ShowSilverCoffers)
{
- Splatoon.AddDynamicElements(SPLATOON_TRAP_HOARD, elements.ToArray(), new long[] { Environment.TickCount64 + 60 * 60 * 1000, ON_TERRITORY_CHANGE });
+ CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers), config.FillSilverCoffers);
}
- catch (Exception e)
- {
- DebugMessage = $"{DateTime.Now}\n{e}";
- }
- });
+ }
+
+ if (elements.Count == 0)
+ return;
+
+ Renderer.SetLayer(ELayer.RegularCoffers, elements);
}
}
@@ -465,51 +462,19 @@ namespace Pal.Client
else
return COLOR_INVISIBLE;
}
+ else if (marker.Type == Marker.EType.SilverCoffer)
+ return ImGui.ColorConvertFloat4ToU32(Service.Configuration.SilverCofferColor);
else
return ImGui.ColorConvertFloat4ToU32(new Vector4(1, 0.5f, 1, 0.4f));
}
- private void HandleEphemeralMarkers(IList visibleMarkers, bool recreateLayout)
+ private void CreateRenderElement(Marker marker, List elements, uint color, bool fill = false)
{
- recreateLayout |= EphemeralMarkers.Any(existingMarker => !visibleMarkers.Any(x => x == existingMarker));
- recreateLayout |= visibleMarkers.Any(visibleMarker => !EphemeralMarkers.Any(x => x == visibleMarker));
-
- if (recreateLayout)
- {
- Splatoon.RemoveDynamicElements(SPLATOON_REGULAR_COFFERS);
- EphemeralMarkers.Clear();
-
- var config = Service.Configuration;
-
- List elements = new List();
- foreach (var marker in visibleMarkers)
- {
- EphemeralMarkers.Add(marker);
-
- if (marker.Type == Marker.EType.SilverCoffer && config.ShowSilverCoffers)
- {
- var element = CreateSplatoonElement(marker.Type, marker.Position, config.SilverCofferColor, config.FillSilverCoffers);
- marker.SplatoonElement = element;
- elements.Add(element);
- }
- }
-
- if (elements.Count == 0)
- return;
-
- new TickScheduler(delegate
- {
- try
- {
- Splatoon.AddDynamicElements(SPLATOON_REGULAR_COFFERS, elements.ToArray(), new long[] { Environment.TickCount64 + 60 * 60 * 1000, ON_TERRITORY_CHANGE });
- }
- catch (Exception e)
- {
- DebugMessage = $"{DateTime.Now}\n{e}";
- }
- });
- }
+ var element = Renderer.CreateElement(marker.Type, marker.Position, color, fill);
+ marker.RenderElement = element;
+ elements.Add(element);
}
+ #endregion
#region Up-/Download
private async Task DownloadMarkersForTerritory(ushort territoryId)
@@ -616,7 +581,7 @@ namespace Pal.Client
var nearbyMarkers = state.Markers
.Where(m => predicate(m))
- .Where(m => m.SplatoonElement != null && m.SplatoonElement.color != COLOR_INVISIBLE)
+ .Where(m => m.RenderElement != null && m.RenderElement.Color != COLOR_INVISIBLE)
.Select(m => new { m = m, distance = (playerPosition - m.Position)?.Length() ?? float.MaxValue })
.OrderBy(m => m.distance)
.Take(5)
@@ -672,27 +637,6 @@ namespace Pal.Client
&& Service.Condition[ConditionFlag.InDeepDungeon]
&& typeof(ETerritoryType).IsEnumDefined(Service.ClientState.TerritoryType);
- internal static Element CreateSplatoonElement(Marker.EType type, Vector3 pos, Vector4 color, bool fill = false)
- => CreateSplatoonElement(type, pos, ImGui.ColorConvertFloat4ToU32(color), fill);
-
- internal static Element CreateSplatoonElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
- {
- return new Element(ElementType.CircleAtFixedCoordinates)
- {
- refX = pos.X,
- refY = pos.Z, // z and y are swapped
- refZ = pos.Y,
- offX = 0,
- offY = 0,
- offZ = _markerConfig[type].OffsetY,
- Filled = fill,
- radius = _markerConfig[type].Radius,
- FillStep = 1,
- color = color,
- thicc = 2,
- };
- }
-
private void ReloadLanguageStrings()
{
_localizedChatMessages = new LocalizedChatMessages
@@ -706,6 +650,30 @@ namespace Pal.Client
};
}
+ internal void ResetRenderer()
+ {
+ if (Renderer is SplatoonRenderer && Service.Configuration.Renderer == Configuration.ERenderer.Splatoon)
+ return;
+ else if (Renderer is SimpleRenderer && Service.Configuration.Renderer == Configuration.ERenderer.Simple)
+ return;
+
+ if (Renderer is IDisposable disposable)
+ disposable.Dispose();
+
+ if (Service.Configuration.Renderer == Configuration.ERenderer.Splatoon)
+ Renderer = new SplatoonRenderer(Service.PluginInterface, this);
+ else
+ Renderer = new SimpleRenderer();
+ }
+
+ private void Draw()
+ {
+ if (Renderer is SimpleRenderer sr)
+ sr.DrawLayers();
+
+ Service.WindowSystem.Draw();
+ }
+
private string GetLocalizedString(uint id)
{
return Service.DataManager.GetExcelSheet()?.GetRow(id)?.Text?.ToString() ?? "Unknown";
@@ -719,12 +687,6 @@ namespace Pal.Client
PomanderOfSafetyUsed,
}
- private class MarkerConfig
- {
- public float OffsetY { get; set; } = 0;
- public float Radius { get; set; } = 0.25f;
- }
-
private class LocalizedChatMessages
{
public string MapRevealed { get; set; } = "???"; //"The map for this floor has been revealed!";
diff --git a/Pal.Client/Rendering/ELayer.cs b/Pal.Client/Rendering/ELayer.cs
new file mode 100644
index 0000000..be7881e
--- /dev/null
+++ b/Pal.Client/Rendering/ELayer.cs
@@ -0,0 +1,8 @@
+namespace Pal.Client.Rendering
+{
+ internal enum ELayer
+ {
+ TrapHoard,
+ RegularCoffers,
+ }
+}
diff --git a/Pal.Client/Rendering/IDrawDebugItems.cs b/Pal.Client/Rendering/IDrawDebugItems.cs
new file mode 100644
index 0000000..e584545
--- /dev/null
+++ b/Pal.Client/Rendering/IDrawDebugItems.cs
@@ -0,0 +1,9 @@
+using System.Numerics;
+
+namespace Pal.Client.Rendering
+{
+ internal interface IDrawDebugItems
+ {
+ void DrawDebugItems(Vector4 trapColor, Vector4 hoardColor);
+ }
+}
diff --git a/Pal.Client/Rendering/IRenderElement.cs b/Pal.Client/Rendering/IRenderElement.cs
new file mode 100644
index 0000000..8f11a82
--- /dev/null
+++ b/Pal.Client/Rendering/IRenderElement.cs
@@ -0,0 +1,9 @@
+namespace Pal.Client.Rendering
+{
+ public interface IRenderElement
+ {
+ bool IsValid { get; }
+
+ uint Color { get; set; }
+ }
+}
diff --git a/Pal.Client/Rendering/IRenderer.cs b/Pal.Client/Rendering/IRenderer.cs
new file mode 100644
index 0000000..9ecf7d2
--- /dev/null
+++ b/Pal.Client/Rendering/IRenderer.cs
@@ -0,0 +1,19 @@
+using ImGuiNET;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Pal.Client.Rendering
+{
+ internal interface IRenderer
+ {
+ void SetLayer(ELayer layer, IReadOnlyList elements);
+
+ void ResetLayer(ELayer layer);
+
+ IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false);
+ }
+}
diff --git a/Pal.Client/Rendering/MarkerConfig.cs b/Pal.Client/Rendering/MarkerConfig.cs
new file mode 100644
index 0000000..77ac161
--- /dev/null
+++ b/Pal.Client/Rendering/MarkerConfig.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace Pal.Client.Rendering
+{
+ internal class MarkerConfig
+ {
+
+ private readonly static Dictionary _markerConfig = new Dictionary
+ {
+ { Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
+ { Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
+ { Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
+ { Marker.EType.Debug, new MarkerConfig { Radius = 1.7f, OffsetY = 0.1f } },
+ };
+
+ public float OffsetY { get; set; } = 0;
+ public float Radius { get; set; } = 0.25f;
+
+ public static MarkerConfig ForType(Marker.EType type) => _markerConfig[type] ?? new MarkerConfig();
+ }
+}
diff --git a/Pal.Client/Rendering/SimpleRenderer.cs b/Pal.Client/Rendering/SimpleRenderer.cs
new file mode 100644
index 0000000..08f5657
--- /dev/null
+++ b/Pal.Client/Rendering/SimpleRenderer.cs
@@ -0,0 +1,163 @@
+using Dalamud.Game.Gui;
+using Dalamud.Interface;
+using Dalamud.Plugin;
+using ECommons.ExcelServices.TerritoryEnumeration;
+using ImGuiNET;
+using Lumina.Excel.GeneratedSheets;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Xml.Linq;
+
+namespace Pal.Client.Rendering
+{
+ ///
+ /// Simple renderer that only draws basic stuff.
+ ///
+ /// This is based on what SliceIsRight uses, and what PalacePal used before it was
+ /// remade into PalacePal (which is the third or fourth iteration on the same idea
+ /// I made, just with a clear vision).
+ ///
+ internal class SimpleRenderer : IRenderer, IDisposable
+ {
+ private ConcurrentDictionary layers = new();
+
+ public void SetLayer(ELayer layer, IReadOnlyList elements)
+ {
+ layers[layer] = new SimpleLayer
+ {
+ TerritoryType = Service.ClientState.TerritoryType,
+ Elements = elements.Cast().ToList()
+ };
+ }
+
+ public void ResetLayer(ELayer layer)
+ {
+ if (layers.Remove(layer, out var l))
+ l.Dispose();
+ }
+
+ public IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
+ {
+ var config = MarkerConfig.ForType(type);
+ return new SimpleElement
+ {
+ Type = type,
+ Position = pos + new Vector3(0, config.OffsetY, 0),
+ Color = color,
+ Radius = config.Radius,
+ Fill = fill,
+ };
+ }
+
+ public void DrawLayers()
+ {
+ if (layers.Count == 0)
+ return;
+
+ ImGuiHelpers.ForceNextWindowMainViewport();
+ ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
+ ImGuiHelpers.SetNextWindowPosRelativeMainViewport(Vector2.Zero, ImGuiCond.None, Vector2.Zero);
+ ImGui.SetNextWindowSize(ImGuiHelpers.MainViewport.Size);
+ if (ImGui.Begin("###PalacePalSimpleRender", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoInputs | ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.AlwaysUseWindowPadding))
+ {
+ ushort territoryType = Service.ClientState.TerritoryType;
+
+ foreach (var layer in layers.Values.Where(l => l.TerritoryType == territoryType))
+ layer.Draw();
+
+ foreach (var key in layers.Where(l => l.Value.TerritoryType != territoryType).Select(l => l.Key).ToList())
+ ResetLayer(key);
+
+ ImGui.End();
+ }
+ ImGui.PopStyleVar();
+ }
+
+ public void Dispose()
+ {
+ foreach (var l in layers.Values)
+ l.Dispose();
+ }
+
+ public class SimpleLayer : IDisposable
+ {
+ public required ushort TerritoryType { get; init; }
+ public required IReadOnlyList Elements { get; set; }
+
+ public void Draw()
+ {
+ foreach (var element in Elements)
+ element.Draw();
+ }
+
+ public void Dispose()
+ {
+ foreach (var e in Elements)
+ e.IsValid = false;
+ }
+ }
+
+ public class SimpleElement : IRenderElement
+ {
+ private const int segmentCount = 20;
+
+ public bool IsValid { get; set; } = true;
+ public required Marker.EType Type { get; set; }
+ public required Vector3 Position { get; set; }
+ public required uint Color { get; set; }
+ public required float Radius { get; set; }
+ public required bool Fill { get; set; }
+
+ public void Draw()
+ {
+ if (Color == Plugin.COLOR_INVISIBLE)
+ return;
+
+ switch (Type)
+ {
+ case Marker.EType.Hoard:
+ // ignore distance if this is a found hoard coffer
+ if (Service.Plugin.PomanderOfIntuition == Plugin.PomanderState.Active && Service.Configuration.OnlyVisibleHoardAfterPomander)
+ break;
+
+ goto case Marker.EType.Trap;
+
+ case Marker.EType.Trap:
+ case Marker.EType.Debug:
+ var playerPos = Service.ClientState.LocalPlayer?.Position;
+ if (playerPos == null)
+ return;
+
+ if ((playerPos.Value - Position).Length() > 65)
+ return;
+ break;
+ }
+
+ bool onScreen = false;
+ for (int index = 0; index < 2 * segmentCount; ++index)
+ {
+ onScreen |= Service.GameGui.WorldToScreen(new Vector3(
+ Position.X + Radius * (float)Math.Sin(Math.PI / segmentCount * index),
+ Position.Y,
+ Position.Z + Radius * (float)Math.Cos(Math.PI / segmentCount * index)),
+ out Vector2 vector2);
+
+ ImGui.GetWindowDrawList().PathLineTo(vector2);
+ }
+
+ if (onScreen)
+ {
+ if (Fill)
+ ImGui.GetWindowDrawList().PathFillConvex(Color);
+ else
+ ImGui.GetWindowDrawList().PathStroke(Color, ImDrawFlags.Closed, 2);
+ }
+ else
+ ImGui.GetWindowDrawList().PathClear();
+ }
+ }
+ }
+}
diff --git a/Pal.Client/Rendering/SplatoonRenderer.cs b/Pal.Client/Rendering/SplatoonRenderer.cs
new file mode 100644
index 0000000..4865eb5
--- /dev/null
+++ b/Pal.Client/Rendering/SplatoonRenderer.cs
@@ -0,0 +1,149 @@
+using Dalamud.Logging;
+using Dalamud.Plugin;
+using ECommons;
+using ECommons.Reflection;
+using ECommons.Schedulers;
+using ECommons.SplatoonAPI;
+using ImGuiNET;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Pal.Client.Rendering
+{
+ internal class SplatoonRenderer : IRenderer, IDrawDebugItems, IDisposable
+ {
+ private const long ON_TERRITORY_CHANGE = -2;
+
+ public SplatoonRenderer(DalamudPluginInterface pluginInterface, IDalamudPlugin plugin)
+ {
+ ECommonsMain.Init(pluginInterface, plugin, ECommons.Module.SplatoonAPI);
+ }
+
+ public void SetLayer(ELayer layer, IReadOnlyList elements)
+ {
+ // we need to delay this, as the current framework update could be before splatoon's, in which case it would immediately delete the layout
+ new TickScheduler(delegate
+ {
+ try
+ {
+ Splatoon.AddDynamicElements(ToLayerName(layer), elements.Cast().Select(x => x.Delegate).ToArray(), new long[] { Environment.TickCount64 + 60 * 60 * 1000, ON_TERRITORY_CHANGE });
+ }
+ catch (Exception e)
+ {
+ PluginLog.Error(e, $"Could not create splatoon layer {layer} with {elements.Count} elements");
+ Service.Plugin.DebugMessage = $"{DateTime.Now}\n{e}";
+ }
+ });
+ }
+
+ public void ResetLayer(ELayer layer)
+ {
+ try
+ {
+ Splatoon.RemoveDynamicElements(ToLayerName(layer));
+ }
+ catch (Exception e)
+ {
+ PluginLog.Error(e, $"Could not reset splatoon layer {layer}");
+ }
+ }
+
+ private string ToLayerName(ELayer layer)
+ => $"PalacePal.{layer}";
+
+ public IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
+ {
+ MarkerConfig config = MarkerConfig.ForType(type);
+ Element element = new Element(ElementType.CircleAtFixedCoordinates)
+ {
+ refX = pos.X,
+ refY = pos.Z, // z and y are swapped
+ refZ = pos.Y,
+ offX = 0,
+ offY = 0,
+ offZ = config.OffsetY,
+ Filled = fill,
+ radius = config.Radius,
+ FillStep = 1,
+ color = color,
+ thicc = 2,
+ };
+ return new SplatoonElement(element);
+ }
+
+ public void DrawDebugItems(Vector4 trapColor, Vector4 hoardColor)
+ {
+ try
+ {
+ Vector3? pos = Service.ClientState.LocalPlayer?.Position;
+ if (pos != null)
+ {
+ var elements = new List
+ {
+ CreateElement(Marker.EType.Trap, pos.Value, ImGui.ColorConvertFloat4ToU32(trapColor)),
+ CreateElement(Marker.EType.Hoard, pos.Value, ImGui.ColorConvertFloat4ToU32(hoardColor)),
+ };
+
+ if (!Splatoon.AddDynamicElements("PalacePal.Test", elements.Cast().Select(x => x.Delegate).ToArray(), new long[] { Environment.TickCount64 + 10000 }))
+ {
+ Service.Chat.PrintError("Could not draw markers :(");
+ }
+ }
+ }
+ catch (Exception)
+ {
+ try
+ {
+ var pluginManager = DalamudReflector.GetPluginManager();
+ IList installedPlugins = pluginManager.GetType().GetProperty("InstalledPlugins")?.GetValue(pluginManager) as IList ?? new List