Pluggable Renderer selection
This commit is contained in:
parent
9263b9edfc
commit
74738cc9b5
@ -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
|
||||
{
|
||||
/// <see cref="Rendering.SimpleRenderer"/>
|
||||
Simple = 0,
|
||||
|
||||
/// <see cref="Rendering.SplatoonRenderer"/>
|
||||
Splatoon = 1,
|
||||
}
|
||||
|
||||
public class AccountInfo
|
||||
{
|
||||
[JsonConverter(typeof(AccountIdConverter))]
|
||||
|
@ -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)
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<Version>2.6</Version>
|
||||
<Version>2.7</Version>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -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<Marker.EType, MarkerConfig> _markerConfig = new Dictionary<Marker.EType, MarkerConfig>
|
||||
{
|
||||
{ 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<ushort, LocalState> FloorMarkers { get; } = new();
|
||||
@ -54,6 +43,7 @@ namespace Pal.Client
|
||||
internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new();
|
||||
internal Queue<IQueueOnFrameworkThread> LateEventQueue { get; } = new();
|
||||
internal ConcurrentQueue<nint> 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>();
|
||||
Service.Plugin = this;
|
||||
Service.Configuration = (Configuration?)pluginInterface.GetPluginConfig() ?? pluginInterface.Create<Configuration>()!;
|
||||
Service.Configuration.Migrate();
|
||||
|
||||
ResetRenderer();
|
||||
|
||||
Service.Hooks = new Hooks();
|
||||
|
||||
var agreementWindow = pluginInterface.Create<AgreementWindow>();
|
||||
@ -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<Marker> 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<Element> elements = new List<Element>();
|
||||
List<IRenderElement> 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<Marker> 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<IRenderElement> 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<Marker> visibleMarkers, bool recreateLayout)
|
||||
private void CreateRenderElement(Marker marker, List<IRenderElement> 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<Element> elements = new List<Element>();
|
||||
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<LogMessage>()?.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!";
|
||||
|
8
Pal.Client/Rendering/ELayer.cs
Normal file
8
Pal.Client/Rendering/ELayer.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Pal.Client.Rendering
|
||||
{
|
||||
internal enum ELayer
|
||||
{
|
||||
TrapHoard,
|
||||
RegularCoffers,
|
||||
}
|
||||
}
|
9
Pal.Client/Rendering/IDrawDebugItems.cs
Normal file
9
Pal.Client/Rendering/IDrawDebugItems.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Pal.Client.Rendering
|
||||
{
|
||||
internal interface IDrawDebugItems
|
||||
{
|
||||
void DrawDebugItems(Vector4 trapColor, Vector4 hoardColor);
|
||||
}
|
||||
}
|
9
Pal.Client/Rendering/IRenderElement.cs
Normal file
9
Pal.Client/Rendering/IRenderElement.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Pal.Client.Rendering
|
||||
{
|
||||
public interface IRenderElement
|
||||
{
|
||||
bool IsValid { get; }
|
||||
|
||||
uint Color { get; set; }
|
||||
}
|
||||
}
|
19
Pal.Client/Rendering/IRenderer.cs
Normal file
19
Pal.Client/Rendering/IRenderer.cs
Normal file
@ -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<IRenderElement> elements);
|
||||
|
||||
void ResetLayer(ELayer layer);
|
||||
|
||||
IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false);
|
||||
}
|
||||
}
|
21
Pal.Client/Rendering/MarkerConfig.cs
Normal file
21
Pal.Client/Rendering/MarkerConfig.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pal.Client.Rendering
|
||||
{
|
||||
internal class MarkerConfig
|
||||
{
|
||||
|
||||
private readonly static Dictionary<Marker.EType, MarkerConfig> _markerConfig = new Dictionary<Marker.EType, MarkerConfig>
|
||||
{
|
||||
{ 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();
|
||||
}
|
||||
}
|
163
Pal.Client/Rendering/SimpleRenderer.cs
Normal file
163
Pal.Client/Rendering/SimpleRenderer.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
internal class SimpleRenderer : IRenderer, IDisposable
|
||||
{
|
||||
private ConcurrentDictionary<ELayer, SimpleLayer> layers = new();
|
||||
|
||||
public void SetLayer(ELayer layer, IReadOnlyList<IRenderElement> elements)
|
||||
{
|
||||
layers[layer] = new SimpleLayer
|
||||
{
|
||||
TerritoryType = Service.ClientState.TerritoryType,
|
||||
Elements = elements.Cast<SimpleElement>().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<SimpleElement> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
149
Pal.Client/Rendering/SplatoonRenderer.cs
Normal file
149
Pal.Client/Rendering/SplatoonRenderer.cs
Normal file
@ -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<IRenderElement> 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<SplatoonElement>().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<IRenderElement>
|
||||
{
|
||||
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<SplatoonElement>().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<object>();
|
||||
|
||||
foreach (var t in installedPlugins)
|
||||
{
|
||||
AssemblyName? assemblyName = (AssemblyName?)t.GetType().GetProperty("AssemblyName")?.GetValue(t);
|
||||
string? pluginName = (string?)t.GetType().GetProperty("Name")?.GetValue(t);
|
||||
if (assemblyName?.Name == "Splatoon" && pluginName != "Splatoon")
|
||||
{
|
||||
Service.Chat.PrintError($"[Palace Pal] Splatoon is installed under the plugin name '{pluginName}', which is incompatible with the Splatoon API.");
|
||||
Service.Chat.Print("[Palace Pal] You need to install Splatoon from the official repository at https://github.com/NightmareXIV/MyDalamudPlugins.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
Service.Chat.PrintError("Could not draw markers, is Splatoon installed and enabled?");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ResetLayer(ELayer.TrapHoard);
|
||||
ResetLayer(ELayer.RegularCoffers);
|
||||
|
||||
ECommonsMain.Dispose();
|
||||
}
|
||||
|
||||
public class SplatoonElement : IRenderElement
|
||||
{
|
||||
public SplatoonElement(Element element)
|
||||
{
|
||||
Delegate = element;
|
||||
}
|
||||
|
||||
public Element Delegate { get; }
|
||||
|
||||
public bool IsValid => Delegate.IsValid();
|
||||
public uint Color
|
||||
{
|
||||
get => Delegate.color;
|
||||
set => Delegate.color = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@
|
||||
recreateLayout = true;
|
||||
saveMarkers = true;
|
||||
}
|
||||
|
||||
plugin.ResetRenderer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Pal.Client
|
||||
[PluginService] public static Condition Condition { get; set; } = null!;
|
||||
[PluginService] public static CommandManager CommandManager { get; set; } = null!;
|
||||
[PluginService] public static DataManager DataManager { get; set; } = null!;
|
||||
[PluginService] public static GameGui GameGui { get; set; } = null!;
|
||||
|
||||
public static Plugin Plugin { get; set; } = null!;
|
||||
public static WindowSystem WindowSystem { get; set; } = new(typeof(Service).AssemblyQualifiedName);
|
||||
|
@ -10,6 +10,7 @@ using ECommons.SplatoonAPI;
|
||||
using Google.Protobuf;
|
||||
using ImGuiNET;
|
||||
using Pal.Client.Net;
|
||||
using Pal.Client.Rendering;
|
||||
using Pal.Client.Scheduled;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@ -28,6 +29,7 @@ namespace Pal.Client.Windows
|
||||
internal class ConfigWindow : Window
|
||||
{
|
||||
private int _mode;
|
||||
private int _renderer;
|
||||
private bool _showTraps;
|
||||
private Vector4 _trapColor;
|
||||
private bool _onlyVisibleTrapsAfterPomander;
|
||||
@ -64,6 +66,7 @@ namespace Pal.Client.Windows
|
||||
{
|
||||
var config = Service.Configuration;
|
||||
_mode = (int)config.Mode;
|
||||
_renderer = (int)config.Renderer;
|
||||
_showTraps = config.ShowTraps;
|
||||
_trapColor = config.TrapColor;
|
||||
_onlyVisibleTrapsAfterPomander = config.OnlyVisibleTrapsAfterPomander;
|
||||
@ -92,6 +95,7 @@ namespace Pal.Client.Windows
|
||||
DrawCommunityTab(ref saveAndClose);
|
||||
DrawImportTab();
|
||||
DrawExportTab();
|
||||
DrawRenderTab(ref save, ref saveAndClose);
|
||||
DrawDebugTab();
|
||||
|
||||
ImGui.EndTabBar();
|
||||
@ -103,6 +107,7 @@ namespace Pal.Client.Windows
|
||||
{
|
||||
var config = Service.Configuration;
|
||||
config.Mode = (Configuration.EMode)_mode;
|
||||
config.Renderer = (Configuration.ERenderer)_renderer;
|
||||
config.ShowTraps = _showTraps;
|
||||
config.TrapColor = _trapColor;
|
||||
config.OnlyVisibleTrapsAfterPomander = _onlyVisibleTrapsAfterPomander;
|
||||
@ -277,6 +282,31 @@ namespace Pal.Client.Windows
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawRenderTab(ref bool save, ref bool saveAndClose)
|
||||
{
|
||||
if (ImGui.BeginTabItem("Renderer"))
|
||||
{
|
||||
ImGui.Text("Select which render backend to use for markers:");
|
||||
ImGui.RadioButton("Splatoon (default, required Splatoon to be installed)", ref _renderer, (int)Configuration.ERenderer.Splatoon);
|
||||
ImGui.RadioButton("Simple (experimental)", ref _renderer, (int)Configuration.ERenderer.Simple);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
save = ImGui.Button("Save");
|
||||
ImGui.SameLine();
|
||||
saveAndClose = ImGui.Button("Save & Close");
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.Text("Splatoon Test:");
|
||||
ImGui.BeginDisabled(!(Service.Plugin.Renderer is IDrawDebugItems));
|
||||
if (ImGui.Button("Draw trap & coffer circles around self"))
|
||||
(Service.Plugin.Renderer as IDrawDebugItems)?.DrawDebugItems(_trapColor, _hoardColor);
|
||||
ImGui.EndDisabled();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDebugTab()
|
||||
{
|
||||
if (ImGui.BeginTabItem("Debug"))
|
||||
@ -323,59 +353,10 @@ namespace Pal.Client.Windows
|
||||
else
|
||||
ImGui.Text("You are NOT in a deep dungeon.");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.Button("Draw trap & coffer circles around self"))
|
||||
DrawDebugItems();
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDebugItems()
|
||||
{
|
||||
try
|
||||
{
|
||||
Vector3? pos = Service.ClientState.LocalPlayer?.Position;
|
||||
if (pos != null)
|
||||
{
|
||||
var elements = new List<Element>
|
||||
{
|
||||
Plugin.CreateSplatoonElement(Marker.EType.Trap, pos.Value, _trapColor),
|
||||
Plugin.CreateSplatoonElement(Marker.EType.Hoard, pos.Value, _hoardColor),
|
||||
};
|
||||
|
||||
if (!Splatoon.AddDynamicElements("PalacePal.Test", elements.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<object>();
|
||||
|
||||
foreach (var t in installedPlugins)
|
||||
{
|
||||
AssemblyName? assemblyName = (AssemblyName?)t.GetType().GetProperty("AssemblyName")?.GetValue(t);
|
||||
string? pluginName = (string?)t.GetType().GetProperty("Name")?.GetValue(t);
|
||||
if (assemblyName?.Name == "Splatoon" && pluginName != "Splatoon")
|
||||
{
|
||||
Service.Chat.PrintError($"[Palace Pal] Splatoon is installed under the plugin name '{pluginName}', which is incompatible with the Splatoon API.");
|
||||
Service.Chat.Print("[Palace Pal] You need to install Splatoon from the official repository at https://github.com/NightmareXIV/MyDalamudPlugins.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
Service.Chat.PrintError("Could not draw markers, is Splatoon installed and enabled?");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// None of the default BeginTabItem methods allow using flags without making the tab have a close button for some reason.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user