diff --git a/Globetrotter/Configuration.cs b/Globetrotter/Configuration.cs index bf7ad04..58298bb 100644 --- a/Globetrotter/Configuration.cs +++ b/Globetrotter/Configuration.cs @@ -5,19 +5,20 @@ using System; namespace Globetrotter { [Serializable] internal class Configuration : IPluginConfiguration { - private DalamudPluginInterface _pi; + private DalamudPluginInterface Plugin { get; set; } = null!; public int Version { get; set; } = 1; public bool ShowOnHover { get; set; } = true; + public bool ShowOnDecipher { get; set; } = true; public bool ShowOnOpen { get; set; } = true; public void Initialize(DalamudPluginInterface pi) { - this._pi = pi ?? throw new ArgumentNullException(nameof(pi), "DalamudPluginInterface cannot be null"); + this.Plugin = pi ?? throw new ArgumentNullException(nameof(pi), "DalamudPluginInterface cannot be null"); } public void Save() { - this._pi.SavePluginConfig(this); + this.Plugin.SavePluginConfig(this); } } } diff --git a/Globetrotter/Globetrotter.csproj b/Globetrotter/Globetrotter.csproj index f8f2753..d7e54b0 100755 --- a/Globetrotter/Globetrotter.csproj +++ b/Globetrotter/Globetrotter.csproj @@ -4,6 +4,7 @@ 1.1.4 net48 latest + enable @@ -28,6 +29,6 @@ - + diff --git a/Globetrotter/PluginUi.cs b/Globetrotter/PluginUi.cs index ef60727..32278c6 100644 --- a/Globetrotter/PluginUi.cs +++ b/Globetrotter/PluginUi.cs @@ -1,5 +1,6 @@ using ImGuiNET; using System; +using System.Numerics; namespace Globetrotter { internal class PluginUi { @@ -25,28 +26,52 @@ namespace Globetrotter { return; } + ImGui.SetNextWindowSize(new Vector2(350, 250), ImGuiCond.FirstUseEver); + if (!ImGui.Begin("Globetrotter settings", ref this._displaySettings)) { + ImGui.End(); return; } ImGui.TextUnformatted("Use /tmap to open your current treasure map."); - ImGui.TextUnformatted("If you have a map and this plugin isn't working, change zone."); + + ImGui.Separator(); + + var showOnDecipher = this.Config.ShowOnDecipher; + if (HelpCheckbox("Show on decipher", "Open the map with a flag set after deciphering a map.", ref showOnDecipher)) { + this.Config.ShowOnDecipher = showOnDecipher; + this.Config.Save(); + } + + ImGui.Separator(); + + var showOnOpen = this.Config.ShowOnOpen; + if (HelpCheckbox("Show on open", "Open the map with a flag set instead of the normal treasure map window.", ref showOnOpen)) { + this.Config.ShowOnOpen = showOnOpen; + this.Config.Save(); + } ImGui.Separator(); var showOnHover = this.Config.ShowOnHover; - if (ImGui.Checkbox("Show on hover", ref showOnHover)) { + if (HelpCheckbox("Show on hover", "Open the map with a flag set when hovering over a deciphered map.", ref showOnHover)) { this.Config.ShowOnHover = showOnHover; this.Config.Save(); } - var showOnOpen = this.Config.ShowOnOpen; - if (ImGui.Checkbox("Show on open", ref showOnOpen)) { - this.Config.ShowOnOpen = showOnOpen; - this.Config.Save(); - } - ImGui.End(); } + + private static bool HelpCheckbox(string label, string help, ref bool isChecked) { + var ret = ImGui.Checkbox(label, ref isChecked); + + ImGui.TreePush(); + ImGui.PushTextWrapPos(); + ImGui.TextUnformatted(help); + ImGui.PopTextWrapPos(); + ImGui.TreePop(); + + return ret; + } } } diff --git a/Globetrotter/TreasureMaps.cs b/Globetrotter/TreasureMaps.cs index c6cdcea..18df6f6 100644 --- a/Globetrotter/TreasureMaps.cs +++ b/Globetrotter/TreasureMaps.cs @@ -3,6 +3,7 @@ using Dalamud.Plugin; using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using Dalamud.Game.Text.SeStringHandling.Payloads; @@ -10,7 +11,7 @@ namespace Globetrotter { internal sealed class TreasureMaps : IDisposable { private const uint TreasureMapsCode = 0x54; - private static Dictionary _mapToRow; + private static Dictionary? _mapToRow; private Dictionary MapToRow { get { @@ -26,7 +27,7 @@ namespace Globetrotter { continue; } - EventItem opened; + EventItem? opened; // FIXME: remove this try/catch when lumina is fixed try { opened = rank.KeyItemName.Value; @@ -49,24 +50,31 @@ namespace Globetrotter { private DalamudPluginInterface Interface { get; } private Configuration Config { get; } - private TreasureMapPacket _lastMap; + private TreasureMapPacket? _lastMap; private delegate char HandleActorControlSelfDelegate(long a1, long a2, IntPtr dataPtr); + private delegate IntPtr ShowTreasureMapDelegate(IntPtr manager, ushort rowId, ushort subRowId, byte a4); + private readonly Hook _acsHook; + private readonly Hook _showMapHook; public TreasureMaps(DalamudPluginInterface pi, Configuration config) { this.Interface = pi ?? throw new ArgumentNullException(nameof(pi), "DalamudPluginInterface cannot be null"); this.Config = config ?? throw new ArgumentNullException(nameof(config), "Configuration cannot be null"); - var delegatePtr = this.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 48 8B D9 49 8B F8 41 0F B7 08"); - if (delegatePtr == IntPtr.Zero) { - PluginLog.Log("Unable to detect treasure maps because could not find ACS handler delegate"); - return; - } - - this._acsHook = new Hook(delegatePtr, new HandleActorControlSelfDelegate(this.OnACS)); + var acsPtr = this.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 48 8B D9 49 8B F8 41 0F B7 08"); + this._acsHook = new Hook(acsPtr, new HandleActorControlSelfDelegate(this.OnACS)); this._acsHook.Enable(); + + var showMapPtr = this.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 40 84 FF 0F 85 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ??"); + this._showMapHook = new Hook(showMapPtr, new ShowTreasureMapDelegate(this.OnShowMap)); + this._showMapHook.Enable(); + } + + public void Dispose() { + this._acsHook.Dispose(); + this._showMapHook.Dispose(); } public void OnHover(object sender, ulong id) { @@ -77,6 +85,36 @@ namespace Globetrotter { this.OpenMapLocation(); } + private IntPtr OnShowMap(IntPtr manager, ushort rowId, ushort subRowId, byte a4) { + try { + if (!this.OnShowMapInner(rowId, subRowId)) { + return IntPtr.Zero; + } + } catch (Exception ex) { + PluginLog.LogError(ex, "Exception on show map"); + } + + return this._showMapHook.Original(manager, rowId, subRowId, a4); + } + + private bool OnShowMapInner(ushort rowId, ushort subRowId) { + if (this._lastMap == null) { + try { + var eventItemId = this.MapToRow.First(entry => entry.Value == rowId); + this._lastMap = new TreasureMapPacket(eventItemId.Key, subRowId, false); + } catch (InvalidOperationException) { + // no-op + } + } + + if (!this.Config.ShowOnOpen && (!this.Config.ShowOnDecipher || this._lastMap?.JustOpened != true)) { + return true; + } + + this.OpenMapLocation(); + return false; + } + private char OnACS(long a1, long a2, IntPtr dataPtr) { try { this.OnACSInner(dataPtr); @@ -94,10 +132,6 @@ namespace Globetrotter { } this._lastMap = packet; - - if (this.Config.ShowOnOpen && packet.JustOpened) { - this.OpenMapLocation(); - } } public void OpenMapLocation() { @@ -132,9 +166,13 @@ namespace Globetrotter { ); this.Interface.Framework.Gui.OpenMapWithMapLink(mapLink); + + if (this._lastMap != null) { + this._lastMap.JustOpened = false; + } } - private static TreasureMapPacket ParsePacket(IntPtr dataPtr) { + private static TreasureMapPacket? ParsePacket(IntPtr dataPtr) { uint category = Marshal.ReadByte(dataPtr); if (category != TreasureMapsCode) { return null; @@ -169,16 +207,12 @@ namespace Globetrotter { val *= c; return (41f / c * ((val + 1024f) / 2048f)) + 1; } - - public void Dispose() { - this._acsHook?.Dispose(); - } } internal class TreasureMapPacket { public uint EventItemId { get; } public uint SubRowId { get; } - public bool JustOpened { get; } + public bool JustOpened { get; set; } public TreasureMapPacket(uint eventItemId, uint subRowId, bool justOpened) { this.EventItemId = eventItemId;