diff --git a/Mappy/Controllers/IntegrationsController.cs b/Mappy/Controllers/IntegrationsController.cs index 5651d72..ebf8403 100644 --- a/Mappy/Controllers/IntegrationsController.cs +++ b/Mappy/Controllers/IntegrationsController.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.CompilerServices; using Dalamud.Game.ClientState.Conditions; using Dalamud.Hooking; using Dalamud.Plugin.Services; @@ -27,8 +28,10 @@ public unsafe class IntegrationsController : IDisposable private bool _wasBetweenAreas; private int _lastQuestCount = -1; private int _lastTempMarkerCount = -1; - /// Snapshot of (QuestId, Sequence) for each active quest; when this changes we refresh so markers update (e.g. multi-step objective). + /// Snapshot of (QuestId, Sequence, variables) for each active quest; when this changes we refresh so markers update (e.g. multi-step objective). private string _lastQuestSequenceSnapshot = string.Empty; + /// Snapshot of temp marker positions; when this changes we refresh so quest area circles update (e.g. marker moved from 1/3 to 2/3 location). + private string _lastTempMarkerSnapshot = string.Empty; private bool _refreshedDuringLoad; /// When true, request a silent refresh on the next framework update (e.g. after plugin load). private bool _requestRefreshOnLoad = true; @@ -113,6 +116,8 @@ public unsafe class IntegrationsController : IDisposable _wasBetweenAreas = Service.Condition.IsBetweenAreas(); _lastQuestCount = GetActiveQuestCount(); try { _lastTempMarkerCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { } + _lastQuestSequenceSnapshot = GetQuestSequenceSnapshot(); + _lastTempMarkerSnapshot = GetTempMarkerSnapshot(); return; } @@ -154,6 +159,7 @@ public unsafe class IntegrationsController : IDisposable var tempCount = -1; try { tempCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { } var sequenceSnapshot = GetQuestSequenceSnapshot(); + var tempMarkerSnapshot = GetTempMarkerSnapshot(); if (!skipQuestTempRefresh) { if (_lastQuestCount >= 0 && questCount < _lastQuestCount) RequestSilentRefresh(); // quest turned in @@ -165,9 +171,12 @@ public unsafe class IntegrationsController : IDisposable RequestSilentRefresh(); // objectives added (e.g. new quest) if (_lastQuestSequenceSnapshot.Length > 0 && sequenceSnapshot != _lastQuestSequenceSnapshot) RequestSilentRefresh(); // quest step advanced (multi-step objective) + if (_lastTempMarkerSnapshot.Length > 0 && tempMarkerSnapshot.Length > 0 && tempMarkerSnapshot != _lastTempMarkerSnapshot) + RequestSilentRefresh(); // marker positions changed (e.g. 1/3 -> 2/3, circle moved) _lastQuestCount = questCount; _lastTempMarkerCount = tempCount; _lastQuestSequenceSnapshot = sequenceSnapshot; + _lastTempMarkerSnapshot = tempMarkerSnapshot; } else { // During suppression: only update temp baseline so we don't false-trigger when suppression // ends (e.g. Duty List click repopulates markers). Keep _lastQuestCount and _lastQuestSequenceSnapshot @@ -188,16 +197,22 @@ public unsafe class IntegrationsController : IDisposable } } - /// Build a string of (QuestId, Sequence) for each active quest so we can detect step advances. + /// Build a string of (QuestId, Sequence, variables) for each active quest so we can detect step advances and multi-step objective progress (1/3, 2/3, etc.). private static unsafe string GetQuestSequenceSnapshot() { try { var parts = new List(); - foreach (var q in QuestManager.Instance()->NormalQuests) + var span = QuestManager.Instance()->NormalQuests; + for (var i = 0; i < span.Length; i++) { + ref var q = ref span[i]; if (q.QuestId is 0) continue; - parts.Add($"{q.QuestId}:{q.Sequence}"); + // Include variables (objective progress) - changes when 1/3 -> 2/3 even if Sequence does not + var ptr = (byte*)Unsafe.AsPointer(ref q); + var varStr = string.Empty; + for (var j = 0; j < 6; j++) varStr += $"{ptr[0x0C + j]:X2}"; + parts.Add($"{q.QuestId}:{q.Sequence}:{varStr}"); } return string.Join("|", parts); } @@ -207,6 +222,31 @@ public unsafe class IntegrationsController : IDisposable } } + /// Build a string of temp marker positions; when marker moves (e.g. 1/3 to 2/3 location) we detect and refresh. + private static unsafe string GetTempMarkerSnapshot() + { + try + { + var agent = AgentMap.Instance(); + var count = agent->TempMapMarkerCount; + if (count == 0) return string.Empty; + var parts = new List(); + var seen = new HashSet<(int, int)>(); + for (var i = 0; i < count; i++) + { + ref var m = ref agent->TempMapMarkers[i]; + var key = (m.MapMarker.X, m.MapMarker.Y); + if (seen.Add(key)) + parts.Add($"{m.MapMarker.X},{m.MapMarker.Y}"); + } + return string.Join("|", parts.OrderBy(x => x)); + } + catch + { + return string.Empty; + } + } + /// Call when user opens map via Duty List (quest/gathering/flag/teleport). Cancels any in-progress silent refresh so we never Hide() the map. Suppresses new quest/temp-marker-triggered refresh for ~1s. Must be called BEFORE openMapHook.Original so OnFrameworkUpdate cannot call Hide() first. private void SuppressSilentRefreshForUserMapOpen() { diff --git a/Mappy/Mappy.csproj b/Mappy/Mappy.csproj index fc78b03..55f8a22 100644 --- a/Mappy/Mappy.csproj +++ b/Mappy/Mappy.csproj @@ -4,7 +4,7 @@ HSMappy HSMappy Knack117 - 1.0.0.18 + 1.0.0.19 A more versatile in-game map. Replaces the in-game map with an ImGui implementation with several additional features. Fork with minimap improvements, quest radius on minimap, and more. http://brassnet.ddns.net:33983/KnackAtNite/HSMappy diff --git a/Mappy/pluginmaster.json b/Mappy/pluginmaster.json index 5c6dae0..bbf5e4e 100644 --- a/Mappy/pluginmaster.json +++ b/Mappy/pluginmaster.json @@ -1 +1 @@ -[{"Author":"Knack117","Name":"HSMappy","Punchline":"A more versatile in-game map.","Description":"Replaces the in-game map with an ImGui implementation with several additional features. Fork with minimap improvements, quest radius on minimap, white gradient player cone, and more.","Changelog":"1.0.0.18: Minimap automations (Desynth, Extract, Repair, Equip, Coffers); player movement cancels automation; >> button with tooltip. 1.0.0.17: Minimap stays visible during dialogue (NPC/quest); rest of hide behavior unchanged. 1.0.0.16: Draw minimap underneath other UI (HSUI etc); Draw Under Other UI config option. 1.0.0.15: Top/Bottom Info Bars: renamed from Map Info/Coordinate; configurable order for both; Repair % (most damaged item); font, size, color, background for both bars; right-align last bottom bar element. 1.0.0.14: Movement Trail (Carbonite-style) - red dots show where you've been on map and minimap; configurable distance, fade time, max points; Clear Trail in context menu. 1.0.0.13: User-placed map notes with Title/Description; custom white-page icon; notes on minimap; Remove Note via context menu; Note List layout fix. 1.0.0.12: Other players on minimap use distinct icon (60403) from party markers. 1.0.0.11: Player/NPC tracking on minimap with Show Players and NPCs toggle. 1.0.0.10: Release build. Suppress silent refresh at start of OnOpenMapHook; remove debug logging. 1.0.0.9: Duty List quest click: don't Hide() when viewing quest map (SelectedMapId != CurrentMapId). 1.0.0.8: Cancel silent refresh when opening map from Duty List so it doesn't immediately close. 1.0.0.7: Duty List quest click opens Area Map even when Hide With Game GUI would block it. 1.0.0.6: Minimap stays open after client restart (restore on login). 1.0.0.5: Fix crash when map texture path is invalid (ArgumentOutOfRangeException in Lumina GetFileHash). 1.0.0.4: Temp marker circle refreshes when quest objective is progressed. 1.0.0.3: Fix marker cache refresh after quest turn-in; invalidate temp cache so old markers don't persist. 1.0.0.2: Red direction arrow on minimap pointing to player flag. 1.0.0.1: Duty List quest click keeps Area Map open; player flags show on minimap. 1.0.0.0: Initial HSMappy release. Minimap: quest radius circle (orange, transparent), tooltip; cone drawn under markers; white gradient cone; /hsmappy commands.","InternalName":"HSMappy","AssemblyVersion":"1.0.0.18","RepoUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy","ApplicableVersion":"any","Tags":["map","mapping","overlay","utility"],"CategoryTags":["jobs"],"DalamudApiLevel":14,"DownloadLinkInstall":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.18/latest.zip","IsHide":false,"IsTestingExclusive":false,"DownloadLinkTesting":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.18/latest.zip","DownloadLinkUpdate":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.18/latest.zip","LastUpdate":"1772372275"}] +[{"Author":"Knack117","Name":"HSMappy","Punchline":"A more versatile in-game map.","Description":"Replaces the in-game map with an ImGui implementation with several additional features. Fork with minimap improvements, quest radius on minimap, white gradient player cone, and more.","Changelog":"1.0.0.19: Minimap quest area circles refresh when multi-step objective progresses (1/3 -> 2/3, etc.). 1.0.0.18: Minimap automations (Desynth, Extract, Repair, Equip, Coffers); player movement cancels automation; >> button with tooltip. 1.0.0.17: Minimap stays visible during dialogue (NPC/quest); rest of hide behavior unchanged. 1.0.0.16: Draw minimap underneath other UI (HSUI etc); Draw Under Other UI config option. 1.0.0.15: Top/Bottom Info Bars: renamed from Map Info/Coordinate; configurable order for both; Repair % (most damaged item); font, size, color, background for both bars; right-align last bottom bar element. 1.0.0.14: Movement Trail (Carbonite-style) - red dots show where you've been on map and minimap; configurable distance, fade time, max points; Clear Trail in context menu. 1.0.0.13: User-placed map notes with Title/Description; custom white-page icon; notes on minimap; Remove Note via context menu; Note List layout fix. 1.0.0.12: Other players on minimap use distinct icon (60403) from party markers. 1.0.0.11: Player/NPC tracking on minimap with Show Players and NPCs toggle. 1.0.0.10: Release build. Suppress silent refresh at start of OnOpenMapHook; remove debug logging. 1.0.0.9: Duty List quest click: don't Hide() when viewing quest map (SelectedMapId != CurrentMapId). 1.0.0.8: Cancel silent refresh when opening map from Duty List so it doesn't immediately close. 1.0.0.7: Duty List quest click opens Area Map even when Hide With Game GUI would block it. 1.0.0.6: Minimap stays open after client restart (restore on login). 1.0.0.5: Fix crash when map texture path is invalid (ArgumentOutOfRangeException in Lumina GetFileHash). 1.0.0.4: Temp marker circle refreshes when quest objective is progressed. 1.0.0.3: Fix marker cache refresh after quest turn-in; invalidate temp cache so old markers don't persist. 1.0.0.2: Red direction arrow on minimap pointing to player flag. 1.0.0.1: Duty List quest click keeps Area Map open; player flags show on minimap. 1.0.0.0: Initial HSMappy release. Minimap: quest radius circle (orange, transparent), tooltip; cone drawn under markers; white gradient cone; /hsmappy commands.","InternalName":"HSMappy","AssemblyVersion":"1.0.0.19","RepoUrl":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy","ApplicableVersion":"any","Tags":["map","mapping","overlay","utility"],"CategoryTags":["jobs"],"DalamudApiLevel":14,"DownloadLinkInstall":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.19/latest.zip","IsHide":false,"IsTestingExclusive":false,"DownloadLinkTesting":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.19/latest.zip","DownloadLinkUpdate":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.19/latest.zip","LastUpdate":"1772407702"}]