Last active
May 27, 2024 02:58
-
-
Save msciotti/4e84ee4ef4d3097b3464aa0778a3064d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Dalamud.Game.Command; | |
using Dalamud.IoC; | |
using Dalamud.Plugin; | |
using Dalamud.Plugin.Services; | |
using ImGuiNET; | |
using Dalamud.Game.ClientState.Objects; | |
using Dalamud.Game.ClientState.Objects.SubKinds; | |
using Lumina; | |
using Lumina.Excel.GeneratedSheets; | |
using System; | |
using FFXIVClientStructs.FFXIV.Component.GUI; | |
using System.Numerics; | |
using Lumina.Extensions; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace MountPlugin; | |
public sealed class MountPlugin : IDalamudPlugin | |
{ | |
public string Name => "Mount Plugin"; | |
private DalamudPluginInterface PluginInterface { get; init; } | |
private ICommandManager CommandManager { get; init; } | |
private IClientState ClientState { get; init; } | |
private ITargetManager TargetManager { get; init; } | |
private IObjectTable ObjectTable { get; init; } | |
private IGameGui GameGui { get; init; } | |
private GameData GameData { get; init; } | |
private bool showWindow; | |
// Dictionary to cache mount icons | |
private Dictionary<uint, IntPtr> mountIconCache = new Dictionary<uint, IntPtr>(); | |
private const int MaxCacheSize = 100; | |
private bool previousTargetBarVisibleState = false; | |
public MountPlugin( | |
[RequiredVersion("1.0")] DalamudPluginInterface pluginInterface, | |
[RequiredVersion("1.0")] IClientState clientState, | |
[RequiredVersion("1.0")] ICommandManager commandManager, | |
[RequiredVersion("1.0")] ITargetManager targetManager, | |
[RequiredVersion("1.0")] IObjectTable objectTable, | |
[RequiredVersion("1.0")] IGameGui gameGui) { | |
PluginInterface = pluginInterface; | |
CommandManager = commandManager; | |
ClientState = clientState; | |
TargetManager = targetManager; | |
ObjectTable = objectTable; | |
GameGui = gameGui; | |
var sqPackPath = "D:\\Games\\Steam\\steamapps\\common\\FINAL FANTASY XIV ONLINE\\game\\sqpack"; | |
if (string.IsNullOrEmpty(sqPackPath)) | |
{ | |
throw new InvalidOperationException("Unable to find sqpack path for FFXIV"); | |
} | |
GameData = new GameData(sqPackPath); | |
CommandManager.AddHandler("/mountinfo", new CommandInfo(OnCommand) | |
{ | |
HelpMessage = "Show mount information of nearby players" | |
}); | |
PluginInterface.UiBuilder.Draw += DrawUI; | |
PluginInterface.UiBuilder.OpenConfigUi += DrawUI; | |
} | |
public void Dispose() | |
{ | |
CommandManager.RemoveHandler("/mountinfo"); | |
PluginInterface.UiBuilder.Draw -= DrawUI; | |
GameData?.Dispose(); | |
// Free cached textures | |
foreach (var icon in mountIconCache.Values) | |
{ | |
ImGui.MemFree(icon); | |
} | |
mountIconCache.Clear(); | |
} | |
private void OnCommand(string command, string arguments) | |
{ | |
showWindow = !showWindow; | |
} | |
private unsafe void DrawUI() | |
{ | |
if (!showWindow) return; | |
var target = TargetManager.Target; | |
if (target != null && target is PlayerCharacter playerCharacter) | |
{ | |
var isFocused = GetTargetHealthBarFocused(playerCharacter); | |
// Only show the mount icon when the target bar is visible | |
// Only render state changes | |
if (!isFocused) | |
{ | |
previousTargetBarVisibleState = false; | |
return; | |
} | |
if (previousTargetBarVisibleState && isFocused) | |
{ | |
return; | |
} | |
previousTargetBarVisibleState = true; | |
var mountID = GetMountID(playerCharacter); | |
if (mountID > 0) | |
{ | |
var mountName = GetMountNameById(mountID); | |
var mountIconTexture = GetMountIconTexture(mountID); | |
if (mountIconTexture == IntPtr.Zero) | |
{ | |
ImGui.Text("Mount icon not found"); | |
return; | |
} | |
var nameplatePosition = GetTargetHealthBarPosition(playerCharacter); | |
if (nameplatePosition.HasValue) | |
{ | |
try | |
{ | |
var iconSize = new Vector2(32, 32); // Adjust icon size as needed | |
var iconPosition = nameplatePosition.Value + new Vector2(0, 20); // Adjust position as needed | |
ImGui.SetNextWindowPos(iconPosition, ImGuiCond.Always); | |
if (ImGui.Begin("MountIcon", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoMove)) | |
{ | |
ImGui.Image(mountIconTexture, iconSize); | |
//// Show tooltip with mount name on hover | |
//if (ImGui.IsItemHovered()) | |
//{ | |
// ImGui.SetTooltip(mountName); | |
//} | |
ImGui.End(); | |
} | |
else | |
{ | |
Console.WriteLine("Failed to create window"); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine($"THE THING BROKE DUDE: {ex}"); | |
} | |
} | |
} | |
} | |
} | |
private IntPtr GetMountIconTexture(uint mountObjectID) | |
{ | |
if (!mountIconCache.TryGetValue(mountObjectID, out var texture)) | |
{ | |
var mountRow = GameData.GetExcelSheet<Mount>()?.GetRow(mountObjectID); | |
if (mountRow != null) | |
{ | |
var icon = GameData.GetIcon(mountRow.Icon); | |
if (icon != null) | |
{ | |
texture = PluginInterface.UiBuilder.LoadImageRaw(icon.Data, icon.Header.Width, icon.Header.Height, 4).ImGuiHandle; | |
mountIconCache[mountObjectID] = texture; | |
if(mountIconCache.Count > MaxCacheSize) | |
{ | |
// Free the oldest texture | |
var oldestKey = mountIconCache.Keys.First(); | |
var oldestTexture = mountIconCache[oldestKey]; | |
ImGui.MemFree(oldestTexture); | |
mountIconCache.Remove(oldestKey); | |
} | |
} | |
} | |
} | |
return texture; | |
} | |
private unsafe uint GetMountID(PlayerCharacter playerCharacter) | |
{ | |
var characterPtr = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)playerCharacter.Address; | |
if (characterPtr == null) return 0; | |
var mountContainer = characterPtr->Mount; | |
var mountObjectID = mountContainer.MountId; | |
if (mountObjectID == 0) return 0; | |
return mountObjectID; | |
} | |
private unsafe string GetMountNameById(uint mountObjectID) | |
{ | |
if (GameData != null) | |
{ | |
var mountRow = GameData.GetExcelSheet<Mount>().GetRow(mountObjectID); | |
if (mountRow != null) | |
{ | |
return mountRow.Singular; | |
} | |
} | |
return "Unknown Mount"; | |
} | |
private unsafe Vector2? GetTargetHealthBarPosition(PlayerCharacter playerCharacter) | |
{ | |
var targetInfoHud = (AtkUnitBase*)GameGui.GetAddonByName("_TargetInfo"); | |
if (targetInfoHud == null) return null; | |
var healthBarNode = (AtkResNode*)targetInfoHud->RootNode->ChildNode; | |
if (healthBarNode == null) return null; | |
// Get screen coordinates from the node | |
var x = healthBarNode->ScreenX; | |
var y = healthBarNode->ScreenY; | |
return new Vector2(x, y); | |
} | |
private unsafe bool GetTargetHealthBarFocused(PlayerCharacter playerCharacter) | |
{ | |
var targetInfoHud = (AtkUnitBase*)GameGui.GetAddonByName("_TargetInfo"); | |
if (targetInfoHud == null) return false; | |
var healthBarNode = (AtkResNode*)targetInfoHud->RootNode->ChildNode; | |
if (healthBarNode == null) return false; | |
return healthBarNode->IsVisible; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment