Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 611e61967b | |||
| 8763cf4c70 | |||
| 851b450a17 | |||
| 340030f826 |
@@ -2,6 +2,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
@@ -26,8 +28,10 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
private bool _wasBetweenAreas;
|
private bool _wasBetweenAreas;
|
||||||
private int _lastQuestCount = -1;
|
private int _lastQuestCount = -1;
|
||||||
private int _lastTempMarkerCount = -1;
|
private int _lastTempMarkerCount = -1;
|
||||||
/// <summary>Snapshot of (QuestId, Sequence) for each active quest; when this changes we refresh so markers update (e.g. multi-step objective).</summary>
|
/// <summary>Snapshot of (QuestId, Sequence, variables) for each active quest; when this changes we refresh so markers update (e.g. multi-step objective).</summary>
|
||||||
private string _lastQuestSequenceSnapshot = string.Empty;
|
private string _lastQuestSequenceSnapshot = string.Empty;
|
||||||
|
/// <summary>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).</summary>
|
||||||
|
private string _lastTempMarkerSnapshot = string.Empty;
|
||||||
private bool _refreshedDuringLoad;
|
private bool _refreshedDuringLoad;
|
||||||
/// <summary>When true, request a silent refresh on the next framework update (e.g. after plugin load).</summary>
|
/// <summary>When true, request a silent refresh on the next framework update (e.g. after plugin load).</summary>
|
||||||
private bool _requestRefreshOnLoad = true;
|
private bool _requestRefreshOnLoad = true;
|
||||||
@@ -112,6 +116,8 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
_wasBetweenAreas = Service.Condition.IsBetweenAreas();
|
_wasBetweenAreas = Service.Condition.IsBetweenAreas();
|
||||||
_lastQuestCount = GetActiveQuestCount();
|
_lastQuestCount = GetActiveQuestCount();
|
||||||
try { _lastTempMarkerCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { }
|
try { _lastTempMarkerCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { }
|
||||||
|
_lastQuestSequenceSnapshot = GetQuestSequenceSnapshot();
|
||||||
|
_lastTempMarkerSnapshot = GetTempMarkerSnapshot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +159,7 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
var tempCount = -1;
|
var tempCount = -1;
|
||||||
try { tempCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { }
|
try { tempCount = (int)AgentMap.Instance()->TempMapMarkerCount; } catch { }
|
||||||
var sequenceSnapshot = GetQuestSequenceSnapshot();
|
var sequenceSnapshot = GetQuestSequenceSnapshot();
|
||||||
|
var tempMarkerSnapshot = GetTempMarkerSnapshot();
|
||||||
if (!skipQuestTempRefresh) {
|
if (!skipQuestTempRefresh) {
|
||||||
if (_lastQuestCount >= 0 && questCount < _lastQuestCount)
|
if (_lastQuestCount >= 0 && questCount < _lastQuestCount)
|
||||||
RequestSilentRefresh(); // quest turned in
|
RequestSilentRefresh(); // quest turned in
|
||||||
@@ -164,9 +171,12 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
RequestSilentRefresh(); // objectives added (e.g. new quest)
|
RequestSilentRefresh(); // objectives added (e.g. new quest)
|
||||||
if (_lastQuestSequenceSnapshot.Length > 0 && sequenceSnapshot != _lastQuestSequenceSnapshot)
|
if (_lastQuestSequenceSnapshot.Length > 0 && sequenceSnapshot != _lastQuestSequenceSnapshot)
|
||||||
RequestSilentRefresh(); // quest step advanced (multi-step objective)
|
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;
|
_lastQuestCount = questCount;
|
||||||
_lastTempMarkerCount = tempCount;
|
_lastTempMarkerCount = tempCount;
|
||||||
_lastQuestSequenceSnapshot = sequenceSnapshot;
|
_lastQuestSequenceSnapshot = sequenceSnapshot;
|
||||||
|
_lastTempMarkerSnapshot = tempMarkerSnapshot;
|
||||||
} else {
|
} else {
|
||||||
// During suppression: only update temp baseline so we don't false-trigger when suppression
|
// 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
|
// ends (e.g. Duty List click repopulates markers). Keep _lastQuestCount and _lastQuestSequenceSnapshot
|
||||||
@@ -187,16 +197,22 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Build a string of (QuestId, Sequence) for each active quest so we can detect step advances.</summary>
|
/// <summary>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.).</summary>
|
||||||
private static unsafe string GetQuestSequenceSnapshot()
|
private static unsafe string GetQuestSequenceSnapshot()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var parts = new List<string>();
|
var parts = new List<string>();
|
||||||
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;
|
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);
|
return string.Join("|", parts);
|
||||||
}
|
}
|
||||||
@@ -206,6 +222,31 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Build a string of temp marker positions; when marker moves (e.g. 1/3 to 2/3 location) we detect and refresh.</summary>
|
||||||
|
private static unsafe string GetTempMarkerSnapshot()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var agent = AgentMap.Instance();
|
||||||
|
var count = agent->TempMapMarkerCount;
|
||||||
|
if (count == 0) return string.Empty;
|
||||||
|
var parts = new List<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>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.</summary>
|
/// <summary>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.</summary>
|
||||||
private void SuppressSilentRefreshForUserMapOpen()
|
private void SuppressSilentRefreshForUserMapOpen()
|
||||||
{
|
{
|
||||||
@@ -538,7 +579,10 @@ public unsafe class IntegrationsController : IDisposable
|
|||||||
if (System.SystemConfig.HideInCombat && Service.Condition.IsInCombat()) return false;
|
if (System.SystemConfig.HideInCombat && Service.Condition.IsInCombat()) return false;
|
||||||
if (System.SystemConfig.HideBetweenAreas && Service.Condition.IsBetweenAreas()) return false;
|
if (System.SystemConfig.HideBetweenAreas && Service.Condition.IsBetweenAreas()) return false;
|
||||||
if (!System.SystemConfig.MinimapHideWithGameGui) return true;
|
if (!System.SystemConfig.MinimapHideWithGameGui) return true;
|
||||||
// Same as main map
|
// Don't hide during dialogue (Occupied = NPC dialogue, OccupiedInQuestEvent = quest dialogue)
|
||||||
|
if (Service.Condition[ConditionFlag.Occupied] || Service.Condition[ConditionFlag.OccupiedInQuestEvent])
|
||||||
|
return true;
|
||||||
|
// Same as main map for non-dialogue cases
|
||||||
if (System.SystemConfig.HideWithGameGui && !IsNamePlateAddonVisible()) return false;
|
if (System.SystemConfig.HideWithGameGui && !IsNamePlateAddonVisible()) return false;
|
||||||
if (System.SystemConfig.HideWithGameGui && Control.Instance()->TargetSystem.TargetModeIndex is 1) return false;
|
if (System.SystemConfig.HideWithGameGui && Control.Instance()->TargetSystem.TargetModeIndex is 1) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ public class SystemConfig : CharacterConfiguration
|
|||||||
public bool MinimapShowPlayersAndNpcs = true;
|
public bool MinimapShowPlayersAndNpcs = true;
|
||||||
/// <summary>Icon ID for other players on the minimap (default 60403, distinct from party 60421). Override if you prefer a different look.</summary>
|
/// <summary>Icon ID for other players on the minimap (default 60403, distinct from party 60421). Override if you prefer a different look.</summary>
|
||||||
public uint MinimapOtherPlayerIconId = 60403;
|
public uint MinimapOtherPlayerIconId = 60403;
|
||||||
|
/// <summary>Draw minimap underneath other plugin UI (e.g. HSUI). Uses ImGui BringWindowToDisplayBack.</summary>
|
||||||
|
public bool MinimapDrawUnderOtherUI = true;
|
||||||
/// <summary>Show current map info (region, map, area, sub-area) at the top of the minimap.</summary>
|
/// <summary>Show current map info (region, map, area, sub-area) at the top of the minimap.</summary>
|
||||||
public bool MinimapShowMapInfoBar = true;
|
public bool MinimapShowMapInfoBar = true;
|
||||||
/// <summary>Order of map info: 0=Region, 1=Map, 2=Area, 3=SubArea. e.g. {0,1,2,3} = Region, Map, Area, SubArea.</summary>
|
/// <summary>Order of map info: 0=Region, 1=Map, 2=Area, 3=SubArea. e.g. {0,1,2,3} = Region, Map, Area, SubArea.</summary>
|
||||||
@@ -152,6 +154,8 @@ public class SystemConfig : CharacterConfiguration
|
|||||||
public int MinimapCoordBarFontType = 0;
|
public int MinimapCoordBarFontType = 0;
|
||||||
/// <summary>Background color (RGBA) for coordinate bar. Alpha = opacity.</summary>
|
/// <summary>Background color (RGBA) for coordinate bar. Alpha = opacity.</summary>
|
||||||
public Vector4 MinimapCoordBarBackground = new(0f, 0f, 0f, 0.2f);
|
public Vector4 MinimapCoordBarBackground = new(0f, 0f, 0f, 0.2f);
|
||||||
|
/// <summary>Show the ">>" action menu button in the top-right corner of the minimap (Desynth, Extract, Repair, Equip, Open Coffers).</summary>
|
||||||
|
public bool MinimapShowActionMenuButton = true;
|
||||||
|
|
||||||
// Movement Trail (Carbonite-style: show where you've been)
|
// Movement Trail (Carbonite-style: show where you've been)
|
||||||
/// <summary>Draw a red trail of dots on the map showing where you've been.</summary>
|
/// <summary>Draw a red trail of dots on the map showing where you've been.</summary>
|
||||||
|
|||||||
@@ -0,0 +1,384 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using AtkValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
||||||
|
|
||||||
|
namespace Mappy.Helpers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Automation actions ported from AutoDuty for the minimap pop-out menu:
|
||||||
|
/// Desynth all, Extract Materia, Repair (self), Equip Gear, Open Coffers.
|
||||||
|
/// </summary>
|
||||||
|
internal static class MinimapActions
|
||||||
|
{
|
||||||
|
private static IFramework.OnUpdateDelegate? _updateHandler;
|
||||||
|
private static long _lastThrottleMs;
|
||||||
|
private static string _activeAction = string.Empty;
|
||||||
|
private static Vector3? _anchorPosition;
|
||||||
|
|
||||||
|
private const int ThrottleMs = 250;
|
||||||
|
private const float MovementEpsilon = 0.5f;
|
||||||
|
|
||||||
|
private static bool CheckPlayerMoved()
|
||||||
|
{
|
||||||
|
if (_anchorPosition is not { } anchor) return false;
|
||||||
|
var current = Service.ObjectTable?.LocalPlayer?.Position;
|
||||||
|
if (!current.HasValue) return false;
|
||||||
|
return Vector3.Distance(anchor, current.Value) > MovementEpsilon;
|
||||||
|
}
|
||||||
|
private static bool Throttle(string key)
|
||||||
|
{
|
||||||
|
var now = Environment.TickCount64;
|
||||||
|
if (now - _lastThrottleMs < ThrottleMs) return false;
|
||||||
|
_lastThrottleMs = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe bool TryGetAddon(string name, out AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
var handle = Service.GameGui.GetAddonByName(name);
|
||||||
|
addon = handle.Address != nint.Zero ? (AtkUnitBase*)handle.Address : null;
|
||||||
|
return addon != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe bool IsAddonReady(AtkUnitBase* addon) =>
|
||||||
|
addon != null && addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded;
|
||||||
|
|
||||||
|
private static unsafe void FireCallback(AtkUnitBase* addon, params object[] args)
|
||||||
|
{
|
||||||
|
if (addon == null || args.Length == 0) return;
|
||||||
|
var atkValues = CreateAtkValueArray(args);
|
||||||
|
if (atkValues == null) return;
|
||||||
|
try { addon->FireCallback((uint)args.Length, atkValues); }
|
||||||
|
finally { FreeAtkValueArray(atkValues, args.Length); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe AtkValue* CreateAtkValueArray(object[] values)
|
||||||
|
{
|
||||||
|
var ptr = (AtkValue*)Marshal.AllocHGlobal(values.Length * sizeof(AtkValue));
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
switch (values[i])
|
||||||
|
{
|
||||||
|
case int n: ptr[i].Type = AtkValueType.Int; ptr[i].Int = n; break;
|
||||||
|
case uint u: ptr[i].Type = AtkValueType.UInt; ptr[i].UInt = u; break;
|
||||||
|
case bool b: ptr[i].Type = AtkValueType.Bool; ptr[i].Byte = (byte)(b ? 1 : 0); break;
|
||||||
|
default: ptr[i].Type = AtkValueType.Int; ptr[i].Int = 0; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void FreeAtkValueArray(AtkValue* ptr, int count)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(new IntPtr(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InvokeDesynth() => StartRunner(RunDesynth);
|
||||||
|
public static void InvokeExtract() => StartRunner(RunExtract);
|
||||||
|
public static void InvokeRepair() => StartRunner(RunRepair);
|
||||||
|
public static void InvokeEquip() => StartRunner(RunEquip);
|
||||||
|
public static void InvokeCoffers() => StartRunner(RunCoffers);
|
||||||
|
|
||||||
|
private static void StartRunner(Action<IFramework> runner)
|
||||||
|
{
|
||||||
|
if (_updateHandler != null) return;
|
||||||
|
_anchorPosition = Service.ObjectTable?.LocalPlayer?.Position;
|
||||||
|
_updateHandler = framework =>
|
||||||
|
{
|
||||||
|
try { runner(framework); }
|
||||||
|
catch (Exception ex) { Service.Log.Warning(ex, "MinimapActions error"); StopRunner(); }
|
||||||
|
};
|
||||||
|
Service.Framework.Update += _updateHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StopRunner()
|
||||||
|
{
|
||||||
|
if (_updateHandler == null) return;
|
||||||
|
Service.Framework.Update -= _updateHandler;
|
||||||
|
_updateHandler = null;
|
||||||
|
_activeAction = string.Empty;
|
||||||
|
_anchorPosition = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Desynth (ported from AutoDuty DesynthHelper) ---
|
||||||
|
private static AgentSalvage.SalvageItemCategory _desynthCategory;
|
||||||
|
private static bool _desynthInitialized;
|
||||||
|
|
||||||
|
private static unsafe void RunDesynth(IFramework framework)
|
||||||
|
{
|
||||||
|
if (CheckPlayerMoved()) { StopRunner(); return; }
|
||||||
|
if (!Throttle("Desynth")) return;
|
||||||
|
if (Service.ClientState is not { IsLoggedIn: true } || Service.Condition[ConditionFlag.InCombat]) { StopRunner(); return; }
|
||||||
|
_activeAction = "Desynth";
|
||||||
|
|
||||||
|
if (Conditions.Instance()->Mounted) { ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23); return; }
|
||||||
|
if (InventoryManager.Instance()->GetEmptySlotsInBag() < 1) { StopRunner(); return; }
|
||||||
|
if (GenericHelpersIsOccupied()) return;
|
||||||
|
|
||||||
|
if (TryGetAddon("SalvageResult", out var salvageResult) && IsAddonReady(salvageResult))
|
||||||
|
{
|
||||||
|
salvageResult->Close(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TryGetAddon("SalvageDialog", out var salvageDialog) && IsAddonReady(salvageDialog))
|
||||||
|
{
|
||||||
|
FireCallback(salvageDialog, true, 15, false); // NQ only = false
|
||||||
|
FireCallback(salvageDialog, true, 0, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!TryGetAddon("SalvageItemSelector", out var selectorBase))
|
||||||
|
{
|
||||||
|
AgentSalvage.Instance()->AgentInterface.Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var selector = (AddonSalvageItemSelector*)selectorBase;
|
||||||
|
if (!IsAddonReady(selectorBase) || !selector->IsReady) return;
|
||||||
|
|
||||||
|
AgentSalvage.Instance()->ItemListRefresh(true);
|
||||||
|
if (!_desynthInitialized) { var cats = Enum.GetValues<AgentSalvage.SalvageItemCategory>(); _desynthCategory = cats.Length > 0 ? cats[0] : 0; _desynthInitialized = true; }
|
||||||
|
if (AgentSalvage.Instance()->SelectedCategory != _desynthCategory)
|
||||||
|
{
|
||||||
|
AgentSalvage.Instance()->SelectedCategory = _desynthCategory;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (selector->ItemCount > 0)
|
||||||
|
{
|
||||||
|
var agent = AgentSalvage.Instance();
|
||||||
|
for (var i = 0; i < agent->ItemCount; i++)
|
||||||
|
{
|
||||||
|
var item = agent->ItemList[i];
|
||||||
|
var invItem = InventoryManager.Instance()->GetInventorySlot(item.InventoryType, (int)item.InventorySlot);
|
||||||
|
if (invItem->ItemId == 10146) continue;
|
||||||
|
var itemSheet = Service.DataManager.GetExcelSheet<Item>()?.GetRow(invItem->ItemId);
|
||||||
|
if (itemSheet == null) continue;
|
||||||
|
FireCallback((AtkUnitBase*)selector, true, 12, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!NextDesynthCategory()) { selector->Close(true); StopRunner(); _desynthInitialized = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool NextDesynthCategory()
|
||||||
|
{
|
||||||
|
var cats = Enum.GetValues<AgentSalvage.SalvageItemCategory>();
|
||||||
|
var idx = Array.IndexOf(cats, _desynthCategory) + 1;
|
||||||
|
for (; idx < cats.Length; idx++) { _desynthCategory = cats[idx]; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Extract (ported from AutoDuty ExtractHelper) ---
|
||||||
|
private static int _extractCategory;
|
||||||
|
private static bool _extractSwitched;
|
||||||
|
|
||||||
|
private static unsafe void RunExtract(IFramework framework)
|
||||||
|
{
|
||||||
|
if (CheckPlayerMoved()) { StopRunner(); return; }
|
||||||
|
if (!Throttle("Extract")) return;
|
||||||
|
if (Service.ClientState is not { IsLoggedIn: true }) { StopRunner(); return; }
|
||||||
|
_activeAction = "Extract";
|
||||||
|
|
||||||
|
if (Conditions.Instance()->Mounted) { ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23); return; }
|
||||||
|
if (InventoryManager.Instance()->GetEmptySlotsInBag() < 1) { StopRunner(); return; }
|
||||||
|
if (GenericHelpersIsOccupied()) return;
|
||||||
|
|
||||||
|
if (!QuestManager.IsQuestComplete(66174)) { Service.Log.Info("Materia Extraction requires quest: Forging the Spirit"); StopRunner(); return; }
|
||||||
|
|
||||||
|
if (TryGetAddon("MaterializeDialog", out var matDialog) && IsAddonReady(matDialog))
|
||||||
|
{
|
||||||
|
FireCallback(matDialog, true, 2, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!TryGetAddon("Materialize", out var materialize))
|
||||||
|
{
|
||||||
|
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 14);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IsAddonReady(materialize)) return;
|
||||||
|
|
||||||
|
if (_extractCategory <= 6)
|
||||||
|
{
|
||||||
|
var listNode = materialize->GetNodeById(12);
|
||||||
|
if (listNode == null) return;
|
||||||
|
var list = listNode->GetAsAtkComponentList();
|
||||||
|
if (list == null || list->UldManager.NodeListCount < 3) return;
|
||||||
|
var textNode = list->UldManager.NodeList[2]->GetComponent()->GetTextNodeById(5);
|
||||||
|
if (textNode == null) return;
|
||||||
|
var spiritbond = textNode->NodeText.ToString();
|
||||||
|
if (!_extractSwitched)
|
||||||
|
{
|
||||||
|
FireCallback(materialize, false, 1, _extractCategory);
|
||||||
|
_extractSwitched = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (spiritbond?.Replace(" ", "") == "100%")
|
||||||
|
{
|
||||||
|
FireCallback(materialize, true, 2, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_extractCategory++;
|
||||||
|
_extractSwitched = false;
|
||||||
|
}
|
||||||
|
else { materialize->Close(true); StopRunner(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Repair (self-repair only, ported from AutoDuty RepairHelper) ---
|
||||||
|
private static bool _repairSeenAddon;
|
||||||
|
|
||||||
|
private static unsafe void RunRepair(IFramework framework)
|
||||||
|
{
|
||||||
|
if (CheckPlayerMoved()) { StopRunner(); return; }
|
||||||
|
if (!Throttle("Repair")) return;
|
||||||
|
if (Service.ClientState is not { IsLoggedIn: true }) { StopRunner(); return; }
|
||||||
|
_activeAction = "Repair";
|
||||||
|
|
||||||
|
if (Conditions.Instance()->Mounted) { ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23); return; }
|
||||||
|
|
||||||
|
if (Service.Condition[Dalamud.Game.ClientState.Conditions.ConditionFlag.Occupied39]) { StopRunner(); return; }
|
||||||
|
if (!TryGetAddon("Repair", out var repair) && !TryGetAddon("SelectYesno", out var yesno))
|
||||||
|
{
|
||||||
|
ActionManager.Instance()->UseAction(ActionType.GeneralAction, 6);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_repairSeenAddon && (!TryGetAddon("SelectYesno", out yesno) || !IsAddonReady(yesno)))
|
||||||
|
{
|
||||||
|
if (TryGetAddon("Repair", out repair) && IsAddonReady(repair))
|
||||||
|
{
|
||||||
|
// Repair All: fire callback (same pattern as AddonMaster.Repair.RepairAll)
|
||||||
|
FireCallback(repair, true, 0);
|
||||||
|
_repairSeenAddon = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (TryGetAddon("SelectYesno", out yesno) && IsAddonReady(yesno))
|
||||||
|
{
|
||||||
|
yesno->FireCallbackInt(0);
|
||||||
|
_repairSeenAddon = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_repairSeenAddon && (!TryGetAddon("SelectYesno", out _) || !IsAddonReady(yesno)))
|
||||||
|
StopRunner();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Equip (vanilla RecommendEquipModule, ported from AutoDuty AutoEquipHelper) ---
|
||||||
|
private static int _equipState;
|
||||||
|
|
||||||
|
private static unsafe void RunEquip(IFramework framework)
|
||||||
|
{
|
||||||
|
if (CheckPlayerMoved()) { StopRunner(); return; }
|
||||||
|
if (!Throttle("Equip")) return;
|
||||||
|
if (Service.ClientState is not { IsLoggedIn: true } || Service.ObjectTable?.LocalPlayer == null) { StopRunner(); return; }
|
||||||
|
_activeAction = "Equip";
|
||||||
|
|
||||||
|
if (RecommendEquipModule.Instance()->IsUpdating) return;
|
||||||
|
if (_equipState == 0)
|
||||||
|
{
|
||||||
|
var job = Service.ObjectTable.LocalPlayer.ClassJob;
|
||||||
|
var jobId = (byte)job.RowId;
|
||||||
|
RecommendEquipModule.Instance()->SetupForClassJob(jobId);
|
||||||
|
_equipState = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RecommendEquipModule.Instance()->EquipRecommendedGear();
|
||||||
|
StopRunner();
|
||||||
|
_equipState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Coffers (ported from AutoDuty CofferHelper) ---
|
||||||
|
private static readonly Dictionary<uint, int> _cofferDone = new();
|
||||||
|
private static int _cofferInitialGearset = -1;
|
||||||
|
|
||||||
|
private static unsafe void RunCoffers(IFramework framework)
|
||||||
|
{
|
||||||
|
if (CheckPlayerMoved()) { StopRunner(); return; }
|
||||||
|
if (!Throttle("Coffer")) return;
|
||||||
|
if (Service.ClientState is not { IsLoggedIn: true }) { StopRunner(); return; }
|
||||||
|
_activeAction = "Coffer";
|
||||||
|
|
||||||
|
if (Conditions.Instance()->Mounted) { ActionManager.Instance()->UseAction(ActionType.GeneralAction, 23); return; }
|
||||||
|
if (InventoryManager.Instance()->GetEmptySlotsInBag() < 1) { StopRunner(); return; }
|
||||||
|
if (GenericHelpersIsOccupied() || Service.ObjectTable?.LocalPlayer?.IsCasting == true) return;
|
||||||
|
|
||||||
|
if (_cofferInitialGearset < 0) _cofferInitialGearset = RaptureGearsetModule.Instance()->CurrentGearsetIndex;
|
||||||
|
|
||||||
|
var items = GetCofferItems();
|
||||||
|
var module = RaptureGearsetModule.Instance();
|
||||||
|
if (items.Count > 0)
|
||||||
|
{
|
||||||
|
var (itemId, invType, slot, qty) = items[0];
|
||||||
|
if (!_cofferDone.TryGetValue(itemId, out var prevQty) || prevQty != qty)
|
||||||
|
{
|
||||||
|
UseItem(invType, (ushort)slot);
|
||||||
|
if (Service.ObjectTable?.LocalPlayer?.IsCasting == true)
|
||||||
|
_cofferDone[itemId] = qty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_cofferInitialGearset >= 0 && module->CurrentGearsetIndex != _cofferInitialGearset)
|
||||||
|
{
|
||||||
|
module->EquipGearset(_cofferInitialGearset);
|
||||||
|
}
|
||||||
|
else { StopRunner(); _cofferDone.Clear(); _cofferInitialGearset = -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<(uint ItemId, InventoryType InvType, int Slot, int Qty)> GetCofferItems()
|
||||||
|
{
|
||||||
|
var result = new List<(uint, InventoryType, int, int)>();
|
||||||
|
var sheet = Service.DataManager.GetExcelSheet<Item>();
|
||||||
|
if (sheet == null) return result;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.Inventory1);
|
||||||
|
if (container == null) return result;
|
||||||
|
for (var i = 0; i < container->Size; i++)
|
||||||
|
{
|
||||||
|
var slot = container->Items[i];
|
||||||
|
if (slot.ItemId == 0) continue;
|
||||||
|
var itemRow = sheet.GetRow(slot.ItemId);
|
||||||
|
if (itemRow.RowId == 0 || !ValidCoffer(itemRow)) continue;
|
||||||
|
if (_cofferDone.TryGetValue(slot.ItemId, out var prev) && prev == slot.Quantity) continue;
|
||||||
|
result.Add((slot.ItemId, InventoryType.Inventory1, i, slot.Quantity));
|
||||||
|
}
|
||||||
|
container = InventoryManager.Instance()->GetInventoryContainer(InventoryType.Inventory2);
|
||||||
|
if (container == null) return result;
|
||||||
|
for (var i = 0; i < container->Size; i++)
|
||||||
|
{
|
||||||
|
var slot = container->Items[i];
|
||||||
|
if (slot.ItemId == 0) continue;
|
||||||
|
var itemRow = sheet.GetRow(slot.ItemId);
|
||||||
|
if (itemRow.RowId == 0 || !ValidCoffer(itemRow)) continue;
|
||||||
|
if (_cofferDone.TryGetValue(slot.ItemId, out var prev) && prev == slot.Quantity) continue;
|
||||||
|
result.Add((slot.ItemId, InventoryType.Inventory2, i, slot.Quantity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ValidCoffer(Item item) =>
|
||||||
|
item.ItemAction.RowId is 1085 or 388 or 367 && item.ItemUICategory.RowId == 61;
|
||||||
|
|
||||||
|
private static unsafe void UseItem(InventoryType invType, ushort slot)
|
||||||
|
{
|
||||||
|
var container = InventoryManager.Instance()->GetInventoryContainer(invType);
|
||||||
|
if (container == null) return;
|
||||||
|
var item = container->Items[slot];
|
||||||
|
if (item.ItemId == 0) return;
|
||||||
|
ActionManager.Instance()->UseAction(ActionType.Item, item.ItemId, 65535);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool GenericHelpersIsOccupied()
|
||||||
|
{
|
||||||
|
if (Service.ObjectTable?.LocalPlayer == null) return true;
|
||||||
|
var player = Service.ObjectTable.LocalPlayer;
|
||||||
|
return player.IsCasting;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<Name>HSMappy</Name>
|
<Name>HSMappy</Name>
|
||||||
<InternalName>HSMappy</InternalName>
|
<InternalName>HSMappy</InternalName>
|
||||||
<Author>Knack117</Author>
|
<Author>Knack117</Author>
|
||||||
<Version>1.0.0.15</Version>
|
<Version>1.0.0.19</Version>
|
||||||
<Punchline>A more versatile in-game map.</Punchline>
|
<Punchline>A more versatile in-game map.</Punchline>
|
||||||
<Description>Replaces the in-game map with an ImGui implementation with several additional features. Fork with minimap improvements, quest radius on minimap, and more.</Description>
|
<Description>Replaces the in-game map with an ImGui implementation with several additional features. Fork with minimap improvements, quest radius on minimap, and more.</Description>
|
||||||
<RepoUrl>http://brassnet.ddns.net:33983/KnackAtNite/HSMappy</RepoUrl>
|
<RepoUrl>http://brassnet.ddns.net:33983/KnackAtNite/HSMappy</RepoUrl>
|
||||||
|
|||||||
@@ -312,6 +312,12 @@ public class MinimapOptionsTab : ITabItem
|
|||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
ImGui.SetTooltip("When enabled, the minimap hides during NPC dialogue, object interaction, and when the game hides nameplates (same as the main map). When disabled, the minimap stays visible in those situations.");
|
ImGui.SetTooltip("When enabled, the minimap hides during NPC dialogue, object interaction, and when the game hides nameplates (same as the main map). When disabled, the minimap stays visible in those situations.");
|
||||||
configChanged |= ImGui.Checkbox("Lock Position", ref System.SystemConfig.MinimapLockPosition);
|
configChanged |= ImGui.Checkbox("Lock Position", ref System.SystemConfig.MinimapLockPosition);
|
||||||
|
configChanged |= ImGui.Checkbox("Show action menu button (>>)", ref System.SystemConfig.MinimapShowActionMenuButton);
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
|
ImGui.SetTooltip("Show the >> button at top-right of minimap to quickly access: Desynth all items, Extract Materia, Repair, Equip Gear, Open Coffers.");
|
||||||
|
configChanged |= ImGui.Checkbox("Draw Under Other UI", ref System.SystemConfig.MinimapDrawUnderOtherUI);
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
|
ImGui.SetTooltip("When enabled, the minimap draws underneath other plugin UI (e.g. HSUI). Disable to draw the minimap on top.");
|
||||||
configChanged |= ImGui.Checkbox("Show Top Info Bar", ref System.SystemConfig.MinimapShowMapInfoBar);
|
configChanged |= ImGui.Checkbox("Show Top Info Bar", ref System.SystemConfig.MinimapShowMapInfoBar);
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
ImGui.SetTooltip("Show current map info (region, map, area, sub-area) at the top of the minimap. Respects the main map label settings (Show Region/Map/Area/Sub-Area Text).");
|
ImGui.SetTooltip("Show current map info (region, map, area, sub-area) at the top of the minimap. Respects the main map label settings (Show Region/Map/Area/Sub-Area Text).");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using KamiLib.Window;
|
|||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using Mappy.Controllers;
|
using Mappy.Controllers;
|
||||||
using Mappy.Data;
|
using Mappy.Data;
|
||||||
|
using Mappy.Helpers;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using Map = Lumina.Excel.Sheets.Map;
|
using Map = Lumina.Excel.Sheets.Map;
|
||||||
@@ -46,6 +47,19 @@ public class MinimapWindow : Window
|
|||||||
|
|
||||||
protected override unsafe void DrawContents()
|
protected override unsafe void DrawContents()
|
||||||
{
|
{
|
||||||
|
if (System.SystemConfig.MinimapDrawUnderOtherUI)
|
||||||
|
{
|
||||||
|
var win = ImGuiP.GetCurrentWindow();
|
||||||
|
var pos = ImGui.GetWindowPos();
|
||||||
|
var size = ImGui.GetWindowSize();
|
||||||
|
var mouse = ImGui.GetMousePos();
|
||||||
|
var cursorOverMinimap = mouse.X >= pos.X && mouse.X <= pos.X + size.X && mouse.Y >= pos.Y && mouse.Y <= pos.Y + size.Y;
|
||||||
|
if (cursorOverMinimap)
|
||||||
|
ImGuiP.BringWindowToDisplayFront(win);
|
||||||
|
else
|
||||||
|
ImGuiP.BringWindowToDisplayBack(win);
|
||||||
|
}
|
||||||
|
|
||||||
var agent = AgentMap.Instance();
|
var agent = AgentMap.Instance();
|
||||||
// Try loading from Lumina first so minimap can show without ever opening the area map
|
// Try loading from Lumina first so minimap can show without ever opening the area map
|
||||||
if (!System.MapRenderer.HasMinimapCacheFor(agent->CurrentMapId) && agent->SelectedMapId != agent->CurrentMapId)
|
if (!System.MapRenderer.HasMinimapCacheFor(agent->CurrentMapId) && agent->SelectedMapId != agent->CurrentMapId)
|
||||||
@@ -126,6 +140,11 @@ public class MinimapWindow : Window
|
|||||||
DrawCoordinateBar(totalWidth, bottomBarHeight, scale);
|
DrawCoordinateBar(totalWidth, bottomBarHeight, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Action menu ">>" button at top-right of minimap
|
||||||
|
if (System.SystemConfig.MinimapShowActionMenuButton) {
|
||||||
|
DrawActionMenuButton(totalWidth, topBarHeight, minimapSide);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore default padding for the next window is done in plugin Draw callback (PopStyleVar after all windows).
|
// Restore default padding for the next window is done in plugin Draw callback (PopStyleVar after all windows).
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,6 +336,61 @@ public class MinimapWindow : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawActionMenuButton(float totalWidth, float topBarHeight, float _)
|
||||||
|
{
|
||||||
|
const string buttonLabel = ">>";
|
||||||
|
var pad = 6f * ImGuiHelpers.GlobalScale;
|
||||||
|
var textSize = ImGui.CalcTextSize(buttonLabel);
|
||||||
|
var buttonSize = textSize + new Vector2(pad * 2, pad);
|
||||||
|
var windowPos = ImGui.GetWindowPos();
|
||||||
|
var screenPos = new Vector2(
|
||||||
|
windowPos.X + totalWidth - buttonSize.X - pad,
|
||||||
|
windowPos.Y + topBarHeight + pad);
|
||||||
|
var buttonMin = screenPos;
|
||||||
|
var buttonMax = screenPos + buttonSize;
|
||||||
|
|
||||||
|
var drawList = ImGui.GetForegroundDrawList();
|
||||||
|
var isHovered = ImGui.IsMouseHoveringRect(buttonMin, buttonMax);
|
||||||
|
var isClicked = isHovered && ImGui.IsMouseClicked(ImGuiMouseButton.Left);
|
||||||
|
|
||||||
|
if (isClicked)
|
||||||
|
ImGui.OpenPopup("minimap_action_popup");
|
||||||
|
|
||||||
|
var bgColor = isHovered ? new Vector4(0.25f, 0.25f, 0.3f, 0.98f) : new Vector4(0.12f, 0.12f, 0.18f, 0.98f);
|
||||||
|
var borderColor = new Vector4(1f, 1f, 1f, 0.9f);
|
||||||
|
var textColor = new Vector4(1f, 1f, 1f, 1f);
|
||||||
|
|
||||||
|
drawList.AddRectFilled(buttonMin, buttonMax, ImGui.GetColorU32(bgColor), 3f);
|
||||||
|
drawList.AddRect(buttonMin, buttonMax, ImGui.GetColorU32(borderColor), 3f, ImDrawFlags.None, 1.5f);
|
||||||
|
var textPos = screenPos + new Vector2((buttonSize.X - textSize.X) * 0.5f, (buttonSize.Y - textSize.Y) * 0.5f);
|
||||||
|
drawList.AddText(textPos, ImGui.GetColorU32(textColor), buttonLabel);
|
||||||
|
|
||||||
|
if (isHovered)
|
||||||
|
{
|
||||||
|
ImGui.SetNextWindowBgAlpha(1f);
|
||||||
|
using var tooltip = ImRaii.Tooltip();
|
||||||
|
ImGui.Text("Automations");
|
||||||
|
}
|
||||||
|
|
||||||
|
var popupX = windowPos.X + totalWidth + pad;
|
||||||
|
var popupY = windowPos.Y + topBarHeight;
|
||||||
|
ImGui.SetNextWindowPos(new Vector2(popupX, popupY), ImGuiCond.Appearing);
|
||||||
|
if (ImGui.BeginPopup("minimap_action_popup", ImGuiWindowFlags.NoMove))
|
||||||
|
{
|
||||||
|
if (ImGui.MenuItem("Desynth all items in inventory"))
|
||||||
|
MinimapActions.InvokeDesynth();
|
||||||
|
if (ImGui.MenuItem("Extract Materia"))
|
||||||
|
MinimapActions.InvokeExtract();
|
||||||
|
if (ImGui.MenuItem("Repair"))
|
||||||
|
MinimapActions.InvokeRepair();
|
||||||
|
if (ImGui.MenuItem("Equip Gear"))
|
||||||
|
MinimapActions.InvokeEquip();
|
||||||
|
if (ImGui.MenuItem("Open Coffers"))
|
||||||
|
MinimapActions.InvokeCoffers();
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateStyle()
|
private void UpdateStyle()
|
||||||
{
|
{
|
||||||
if (System.SystemConfig.MinimapLockPosition)
|
if (System.SystemConfig.MinimapLockPosition)
|
||||||
|
|||||||
@@ -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.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.15","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.15/latest.zip","IsHide":false,"IsTestingExclusive":false,"DownloadLinkTesting":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.15/latest.zip","DownloadLinkUpdate":"http://brassnet.ddns.net:33983/KnackAtNite/HSMappy/releases/download/v1.0.0.15/latest.zip","LastUpdate":"1772365750"}]
|
[{"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"}]
|
||||||
|
|||||||
Reference in New Issue
Block a user