diff --git a/Pal.Client/Configuration.cs b/Pal.Client/Configuration.cs index c90dc76..d3d2477 100644 --- a/Pal.Client/Configuration.cs +++ b/Pal.Client/Configuration.cs @@ -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; diff --git a/Pal.Client/Pal.Client.csproj b/Pal.Client/Pal.Client.csproj index 70f3f93..63070a4 100644 --- a/Pal.Client/Pal.Client.csproj +++ b/Pal.Client/Pal.Client.csproj @@ -3,7 +3,7 @@ net6.0-windows 9.0 - 1.5.0.0 + 1.6.0.0 diff --git a/Pal.Client/Plugin.cs b/Pal.Client/Plugin.cs index ffdc715..e9687d7 100644 --- a/Pal.Client/Plugin.cs +++ b/Pal.Client/Plugin.cs @@ -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 markers)> _remoteDownloads = new(); private readonly static Dictionary _markerConfig = new Dictionary @@ -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> FloorMarkers { get; } = new(); internal ConcurrentBag 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(LoadSavedMarkers()); EphemeralMarkers.Clear(); + PomanderOfSight = PomanderState.Inactive; + PomanderOfIntuition = PomanderState.Inactive; recreateLayout = true; DebugMessage = null; } @@ -212,6 +266,7 @@ namespace Pal.Client private void HandlePersistentMarkers(ConcurrentBag currentFloorMarkers, IList 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 elements = new List(); 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 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 visibleMarkers, bool recreateLayout) { recreateLayout |= EphemeralMarkers.Any(existingMarker => !visibleMarkers.Any(x => x == existingMarker)); @@ -457,7 +557,10 @@ 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) + 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().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+)$"); + } } } diff --git a/Pal.Client/Service.cs b/Pal.Client/Service.cs index 4024e5e..1f375ab 100644 --- a/Pal.Client/Service.cs +++ b/Pal.Client/Service.cs @@ -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); diff --git a/Pal.Client/Windows/ConfigWindow.cs b/Pal.Client/Windows/ConfigWindow.cs index 92c8969..7046f46 100644 --- a/Pal.Client/Windows/ConfigWindow.cs +++ b/Pal.Client/Windows/ConfigWindow.cs @@ -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;