Hide traps/coffer locations if a pomander is used and they're not currently shown

This commit is contained in:
Liza 2022-10-26 23:38:29 +02:00
parent 4bd37bdefd
commit 16a2305892
5 changed files with 171 additions and 12 deletions

View File

@ -1,7 +1,4 @@
using Dalamud.Configuration;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.Graphics;
using System.Numerics;
namespace Pal.Client
@ -18,8 +15,12 @@ namespace Pal.Client
public bool ShowTraps { get; set; } = true;
public Vector4 TrapColor { get; set; } = new Vector4(1, 0, 0, 0.4f);
public bool OnlyVisibleTrapsAfterPomander { get; set; } = true;
public bool ShowHoard { get; set; } = true;
public Vector4 HoardColor { get; set; } = new Vector4(0, 1, 1, 0.4f);
public bool OnlyVisibleHoardAfterPomander { get; set; } = true;
public bool ShowSilverCoffers { get; set; } = false;
public Vector4 SilverCofferColor { get; set; } = new Vector4(1, 1, 1, 0.4f);
public bool FillSilverCoffers { get; set; } = true;

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>9.0</LangVersion>
<Version>1.5.0.0</Version>
<Version>1.6.0.0</Version>
</PropertyGroup>
<PropertyGroup>

View File

@ -2,13 +2,18 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Command;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface.Colors;
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.Windows;
using System;
using System.Collections.Concurrent;
@ -18,6 +23,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Pal.Client
@ -25,6 +31,7 @@ namespace Pal.Client
public class Plugin : IDalamudPlugin
{
private const long ON_TERRITORY_CHANGE = -2;
private const uint COLOR_INVISIBLE = 0;
private readonly ConcurrentQueue<(ushort territoryId, bool success, IList<Marker> markers)> _remoteDownloads = new();
private readonly static Dictionary<Marker.EType, MarkerConfig> _markerConfig = new Dictionary<Marker.EType, MarkerConfig>
@ -34,11 +41,15 @@ namespace Pal.Client
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
};
private bool _configUpdated = false;
private bool _pomandersUpdated = false;
private LocalizedChatMessages _localizedChatMessages = new();
internal ConcurrentDictionary<ushort, ConcurrentBag<Marker>> FloorMarkers { get; } = new();
internal ConcurrentBag<Marker> EphemeralMarkers { get; set; } = new();
internal ushort LastTerritory { get; private set; }
public SyncState TerritorySyncState { get; set; }
public PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
public string DebugMessage { get; set; }
public string Name => "Palace Pal";
@ -75,10 +86,13 @@ namespace Pal.Client
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
Service.Framework.Update += OnFrameworkUpdate;
Service.Configuration.Saved += OnConfigSaved;
Service.Chat.ChatMessage += OnChatMessage;
Service.CommandManager.AddHandler("/pal", new CommandInfo(OnCommand)
{
HelpMessage = "Open the configuration/debug window"
});
ReloadLanguageStrings();
}
public void OnOpenConfigUi()
@ -123,6 +137,7 @@ namespace Pal.Client
Service.PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
Service.Framework.Update -= OnFrameworkUpdate;
Service.Configuration.Saved -= OnConfigSaved;
Service.Chat.ChatMessage -= OnChatMessage;
Service.WindowSystem.RemoveAllWindows();
@ -142,6 +157,43 @@ namespace Pal.Client
_configUpdated = true;
}
private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString seMessage, ref bool isHandled)
{
if (type != (XivChatType)2105)
return;
string message = seMessage.ToString();
if (_localizedChatMessages.FloorChanged.IsMatch(message))
{
PomanderOfSight = PomanderState.Inactive;
if (PomanderOfIntuition == PomanderState.FoundOnCurrentFloor)
PomanderOfIntuition = PomanderState.Inactive;
}
else if (message.EndsWith(_localizedChatMessages.MapRevealed))
{
PomanderOfSight = PomanderState.Active;
}
else if (message.EndsWith(_localizedChatMessages.AllTrapsRemoved))
{
PomanderOfSight = PomanderState.PomanderOfSafetyUsed;
}
else if (message.EndsWith(_localizedChatMessages.HoardNotOnCurrentFloor) || message.EndsWith(_localizedChatMessages.HoardOnCurrentFloor))
{
// There is no functional difference between these - if you don't open the marked coffer,
// going to higher floors will keep the pomander active.
PomanderOfIntuition = PomanderState.Active;
}
else if (message.EndsWith(_localizedChatMessages.HoardCofferOpened))
{
PomanderOfIntuition = PomanderState.FoundOnCurrentFloor;
}
else
return;
_pomandersUpdated = true;
}
private void OnFrameworkUpdate(Framework framework)
{
try
@ -177,6 +229,8 @@ namespace Pal.Client
if (IsInPotdOrHoh())
FloorMarkers[LastTerritory] = new ConcurrentBag<Marker>(LoadSavedMarkers());
EphemeralMarkers.Clear();
PomanderOfSight = PomanderState.Inactive;
PomanderOfIntuition = PomanderState.Inactive;
recreateLayout = true;
DebugMessage = null;
}
@ -212,6 +266,7 @@ namespace Pal.Client
private void HandlePersistentMarkers(ConcurrentBag<Marker> currentFloorMarkers, IList<Marker> visibleMarkers, bool saveMarkers, bool recreateLayout)
{
var config = Service.Configuration;
foreach (var visibleMarker in visibleMarkers)
{
@ -231,6 +286,35 @@ namespace Pal.Client
saveMarkers = true;
}
if (_pomandersUpdated)
{
if (currentFloorMarkers.Count > 0 && (config.OnlyVisibleTrapsAfterPomander || config.OnlyVisibleHoardAfterPomander))
{
try
{
foreach (var marker in currentFloorMarkers)
{
uint desiredColor = DetermineColor(marker, visibleMarkers);
if (marker.SplatoonElement == null || !marker.SplatoonElement.IsValid())
{
recreateLayout = true;
break;
}
if (marker.SplatoonElement.color != desiredColor)
marker.SplatoonElement.color = desiredColor;
}
}
catch (Exception e)
{
DebugMessage = $"{DateTime.Now}\n{e}";
recreateLayout = true;
}
}
_pomandersUpdated = false;
}
if (saveMarkers)
{
SaveMarkers();
@ -246,8 +330,6 @@ namespace Pal.Client
{
Splatoon.RemoveDynamicElements("PalacePal.TrapHoard");
var config = Service.Configuration;
List<Element> elements = new List<Element>();
foreach (var marker in currentFloorMarkers)
{
@ -255,13 +337,13 @@ namespace Pal.Client
{
if (marker.Type == Marker.EType.Trap && config.ShowTraps)
{
var element = CreateSplatoonElement(marker.Type, marker.Position, config.TrapColor);
var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
marker.SplatoonElement = element;
elements.Add(element);
}
else if (marker.Type == Marker.EType.Hoard && config.ShowHoard)
{
var element = CreateSplatoonElement(marker.Type, marker.Position, config.HoardColor);
var element = CreateSplatoonElement(marker.Type, marker.Position, DetermineColor(marker, visibleMarkers));
marker.SplatoonElement = element;
elements.Add(element);
}
@ -286,6 +368,24 @@ namespace Pal.Client
}
}
private uint DetermineColor(Marker marker, IList<Marker> visibleMarkers)
{
if (marker.Type == Marker.EType.Trap)
{
if (PomanderOfSight == PomanderState.Inactive || !Service.Configuration.OnlyVisibleTrapsAfterPomander || visibleMarkers.Any(x => x == marker))
return ImGui.ColorConvertFloat4ToU32(Service.Configuration.TrapColor);
else
return COLOR_INVISIBLE;
}
else
{
if (PomanderOfIntuition == PomanderState.Inactive || !Service.Configuration.OnlyVisibleHoardAfterPomander || visibleMarkers.Any(x => x == marker))
return ImGui.ColorConvertFloat4ToU32(Service.Configuration.HoardColor);
else
return COLOR_INVISIBLE;
}
}
private void HandleEphemeralMarkers(IList<Marker> visibleMarkers, bool recreateLayout)
{
recreateLayout |= EphemeralMarkers.Any(existingMarker => !visibleMarkers.Any(x => x == existingMarker));
@ -458,6 +558,9 @@ namespace Pal.Client
internal bool IsInPotdOrHoh() => Service.ClientState.IsLoggedIn && Service.Condition[ConditionFlag.InDeepDungeon];
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)
{
@ -470,11 +573,29 @@ namespace Pal.Client
Filled = fill,
radius = _markerConfig[type].Radius,
FillStep = 1,
color = ImGui.ColorConvertFloat4ToU32(color),
color = color,
thicc = 2,
};
}
private void ReloadLanguageStrings()
{
_localizedChatMessages = new LocalizedChatMessages
{
MapRevealed = GetLocalizedString(7256),
AllTrapsRemoved = GetLocalizedString(7255),
HoardOnCurrentFloor = GetLocalizedString(7272),
HoardNotOnCurrentFloor = GetLocalizedString(7273),
HoardCofferOpened = GetLocalizedString(7274),
FloorChanged = new Regex("^" + GetLocalizedString(7270).Replace("\u0002 \u0003\ufffd\u0002\u0003", @"(\d+)") + "$"),
};
}
private string GetLocalizedString(uint id)
{
return Service.DataManager.GetExcelSheet<LogMessage>().GetRow(id).Text?.ToString() ?? "Unknown";
}
public enum SyncState
{
NotAttempted,
@ -483,10 +604,28 @@ namespace Pal.Client
Failed,
}
public enum PomanderState
{
Inactive,
Active,
FoundOnCurrentFloor,
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!";
public string AllTrapsRemoved { get; set; } = "???"; // "All the traps on this floor have disappeared!";
public string HoardOnCurrentFloor { get; set; } = "???"; // "You sense the Accursed Hoard calling you...";
public string HoardNotOnCurrentFloor { get; set; } = "???"; // "You do not sense the call of the Accursed Hoard on this floor...";
public string HoardCofferOpened { get; set; } = "???"; // "You discover a piece of the Accursed Hoard!";
public Regex FloorChanged { get; set; } = new Regex(@"This isn't a game message, but will be replaced"); // new Regex(@"^Floor (\d+)$");
}
}
}

View File

@ -1,4 +1,5 @@
using Dalamud.Game;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
@ -19,6 +20,7 @@ namespace Pal.Client
[PluginService] public static Framework Framework { get; set; } = null!;
[PluginService] public static Condition Condition { get; set; } = null!;
[PluginService] public static CommandManager CommandManager { get; set; } = null!;
[PluginService] public static DataManager DataManager { get; set; } = null!;
public static Plugin Plugin { get; set; } = null!;
public static WindowSystem WindowSystem { get; set; } = new(typeof(Service).AssemblyQualifiedName);

View File

@ -15,16 +15,20 @@ namespace Pal.Client.Windows
private int _mode;
private bool _showTraps;
private Vector4 _trapColor;
private bool _onlyVisibleTrapsAfterPomander;
private bool _showHoard;
private Vector4 _hoardColor;
private bool _onlyVisibleHoardAfterPomander;
private bool _showSilverCoffers;
private Vector4 _silverCofferColor;
private bool _fillSilverCoffers;
private string _connectionText;
public ConfigWindow() : base("Palace Pal - Configuration###PalPalaceConfig")
public ConfigWindow() : base("Palace Pal###PalPalaceConfig")
{
var version = typeof(Plugin).Assembly.GetName().Version.ToString(2);
WindowName = $"Palace Pal v{version}###PalPalaceConfig";
Size = new Vector2(500, 400);
SizeCondition = ImGuiCond.FirstUseEver;
Position = new Vector2(300, 300);
@ -37,8 +41,10 @@ namespace Pal.Client.Windows
_mode = (int)config.Mode;
_showTraps = config.ShowTraps;
_trapColor = config.TrapColor;
_onlyVisibleTrapsAfterPomander = config.OnlyVisibleTrapsAfterPomander;
_showHoard = config.ShowHoard;
_hoardColor = config.HoardColor;
_onlyVisibleHoardAfterPomander = config.OnlyVisibleHoardAfterPomander;
_showSilverCoffers = config.ShowSilverCoffers;
_silverCofferColor = config.SilverCofferColor;
_fillSilverCoffers = config.FillSilverCoffers;
@ -58,6 +64,9 @@ namespace Pal.Client.Windows
ImGui.BeginDisabled(!_showTraps);
ImGui.Spacing();
ImGui.ColorEdit4("Trap color", ref _trapColor, ImGuiColorEditFlags.NoInputs);
ImGui.Checkbox("Hide traps not on current floor", ref _onlyVisibleTrapsAfterPomander);
ImGui.SameLine();
ImGuiComponents.HelpMarker("When using a Pomander of sight, only the actual trap locations are visible, all other traps are hidden.");
ImGui.EndDisabled();
ImGui.Unindent();
@ -68,6 +77,9 @@ namespace Pal.Client.Windows
ImGui.BeginDisabled(!_showHoard);
ImGui.Spacing();
ImGui.ColorEdit4("Hoard Coffer color", ref _hoardColor, ImGuiColorEditFlags.NoInputs);
ImGui.Checkbox("Hide hoard coffers not on current floor", ref _onlyVisibleHoardAfterPomander);
ImGui.SameLine();
ImGuiComponents.HelpMarker("When using a Pomander of intuition, only the actual hoard coffer location is visible, all other (potential) hoard coffers are hidden.");
ImGui.EndDisabled();
ImGui.Unindent();
@ -154,6 +166,9 @@ namespace Pal.Client.Windows
int silverCoffers = plugin.EphemeralMarkers.Count(x => x != null && x.Type == Marker.EType.SilverCoffer);
ImGui.Text($"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor");
}
ImGui.Text($"Pomander of Sight: {plugin.PomanderOfSight}");
ImGui.Text($"Pomander of Intuition: {plugin.PomanderOfIntuition}");
}
else
ImGui.Text("Could not query current trap/coffer count.");
@ -199,8 +214,10 @@ namespace Pal.Client.Windows
config.Mode = (Configuration.EMode)_mode;
config.ShowTraps = _showTraps;
config.TrapColor = _trapColor;
config.OnlyVisibleTrapsAfterPomander = _onlyVisibleTrapsAfterPomander;
config.ShowHoard = _showHoard;
config.HoardColor = _hoardColor;
config.OnlyVisibleHoardAfterPomander = _onlyVisibleHoardAfterPomander;
config.ShowSilverCoffers = _showSilverCoffers;
config.SilverCofferColor = _silverCofferColor;
config.FillSilverCoffers = _fillSilverCoffers;