diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index a67a0abb6..2e8d128c3 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -12,6 +12,8 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + using Lumina.Excel.GeneratedSheets; using Action = System.Action; @@ -89,6 +91,16 @@ internal sealed class ClientState : IInternalDisposableService, IClientState /// public ushort TerritoryType { get; private set; } + /// + public unsafe uint MapId + { + get + { + var agentMap = AgentMap.Instance(); + return agentMap != null ? AgentMap.Instance()->CurrentMapId : 0; + } + } + /// public PlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as PlayerCharacter; @@ -237,6 +249,9 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat /// public ushort TerritoryType => this.clientStateService.TerritoryType; + + /// + public uint MapId => this.clientStateService.MapId; /// public PlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer; diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 652a6c888..50e31fad0 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -48,6 +48,11 @@ public interface IClientState /// Gets the current Territory the player resides in. /// public ushort TerritoryType { get; } + + /// + /// Gets the current Map the player resides in. + /// + public uint MapId { get; } /// /// Gets the local player character, if one is present. diff --git a/Dalamud/Utility/MapUtil.cs b/Dalamud/Utility/MapUtil.cs index b4bbe1038..2ed3dfad1 100644 --- a/Dalamud/Utility/MapUtil.cs +++ b/Dalamud/Utility/MapUtil.cs @@ -1,5 +1,11 @@ using System.Numerics; +using Dalamud.Data; +using Dalamud.Game.ClientState.Objects.Types; + +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +using Lumina; using Lumina.Excel.GeneratedSheets; namespace Dalamud.Utility; @@ -128,4 +134,32 @@ public static class MapUtil { return WorldToMap(worldCoordinates, map.OffsetX, map.OffsetY, map.SizeFactor); } + + /// + /// Extension method to get the current position of a GameObject in Map Coordinates (visible to players in the + /// minimap or chat). A Z (height) value will always be returned, even on maps that do not natively show one. + /// + /// Thrown if ClientState is unavailable. + /// The GameObject to get the position for. + /// Whether to "correct" a Z offset to sane values for maps that don't have one. + /// A Vector3 that represents the X (east/west), Y (north/south), and Z (height) position of this object. + public static unsafe Vector3 GetMapCoordinates(this GameObject go, bool correctZOffset = false) + { + var agentMap = AgentMap.Instance(); + + if (agentMap == null || agentMap->CurrentMapId == 0) + throw new InvalidOperationException("Could not determine active map - data may not be loaded yet?"); + + var territoryTransient = Service.Get() + .GetExcelSheet()! + .GetRow(agentMap->CurrentTerritoryId); + + return WorldToMap( + go.Position, + agentMap->CurrentOffsetX, + agentMap->CurrentOffsetY, + territoryTransient?.OffsetZ ?? 0, + (uint)agentMap->CurrentMapSizeFactor, + correctZOffset); + } }