Skip to content

Commit

Permalink
Added support for a Native UI alternative to the tick bar.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurochi51 committed Jan 27, 2024
1 parent 7b64b42 commit ad88031
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 41 deletions.
4 changes: 4 additions & 0 deletions TickTracker/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public class Configuration : IPluginConfiguration
public bool AlwaysShowWithHostileTarget { get; set; } = false;
public bool AlwaysShowInCombat { get; set; } = false;
public bool HPVisible { get; set; } = true;
public bool HPNativeUiVisible { get; set; } = false;
public bool MPVisible { get; set; } = true;
public bool MPNativeUiVisible { get; set; }
public bool GPVisible { get; set; } = true;
public bool HideOnFullResource { get; set; } = false;
public bool DisableCollisionInCombat { get; set; } = false;
Expand All @@ -26,13 +28,15 @@ public class Configuration : IPluginConfiguration
public Vector4 HPBarBackgroundColor { get; set; } = new(0f, 0f, 0f, 1f);
public Vector4 HPBarFillColor { get; set; } = new(0.276f, 0.8f, 0.24f, 1f);
public Vector4 HPBarBorderColor { get; set; } = new(0.246f, 0.262f, 0.270f, 1f);
public Vector4 HPNativeUiColor { get; set; } = new(0f, 0.570f, 0.855f, 1f);
public Vector4 HPIconColor { get; set; } = new(1f, 1f, 1f, 1f);

public Vector2 MPBarPosition { get; set; } = new(900, 500);
public Vector2 MPBarSize { get; set; } = new(180, 50);
public Vector4 MPBarBackgroundColor { get; set; } = new(0f, 0f, 0f, 1f);
public Vector4 MPBarFillColor { get; set; } = new(0.753f, 0.271f, 0.482f, 1f);
public Vector4 MPBarBorderColor { get; set; } = new(0.246f, 0.262f, 0.270f, 1f);
public Vector4 MPNativeUiColor { get; set; } = new(0f, 0.570f, 0.855f, 1f);
public Vector4 MPIconColor { get; set; } = new(1f, 1f, 1f, 1f);

public Vector2 GPBarPosition { get; set; } = new(750, 400);
Expand Down
166 changes: 166 additions & 0 deletions TickTracker/Helpers/NativeUi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;

using Dalamud.Interface;
using FFXIVClientStructs.FFXIV.Component.GUI;
using FFXIVClientStructs.FFXIV.Client.System.Memory;

namespace TickTracker.Helpers;

// Heavily inspired by SimpleTweaks and ReadyCheckHelper
public static unsafe class NativeUi
{
private static readonly Dictionary<string, uint> NodeIds = new(StringComparer.Ordinal);
private static readonly Dictionary<uint, string> NodeNames = new();
private static uint NodeIdBase = 0x5469636B;

public static uint Get(string name, int index = 0)
{
if (TryGet(name, index, out var id)) return id;
lock (NodeIds)
{
lock (NodeNames)
{
id = NodeIdBase;
NodeIdBase += 16;
NodeIds.Add($"{name}#{index}", id);
NodeNames.Add(id, $"{name}#{index}");
return id;
}
}
}
public static bool TryGet(string name, int index, out uint id) => NodeIds.TryGetValue($"{name}#{index}", out id);
public static AtkResNode* GetNodeByID(AtkUldManager* uldManager, uint nodeId, NodeType? type = null) => GetNodeByID<AtkResNode>(uldManager, nodeId, type);
public static T* GetNodeByID<T>(AtkUldManager* uldManager, uint nodeId, NodeType? type = null) where T : unmanaged
{
for (var i = 0; i < uldManager->NodeListCount; i++)
{
var n = uldManager->NodeList[i];
if (n->NodeID != nodeId || (type != null && n->Type != type.Value)) continue;
return (T*)n;
}
return null;
}

public static void LinkNodeAfterTargetNode(AtkResNode* node, AtkComponentNode* parent, AtkResNode* targetNode)
{
var prev = targetNode->PrevSiblingNode;
node->ParentNode = targetNode->ParentNode;

targetNode->PrevSiblingNode = node;
prev->NextSiblingNode = node;

node->PrevSiblingNode = prev;
node->NextSiblingNode = targetNode;

parent->Component->UldManager.UpdateDrawNodeList();
}

public static void UnlinkNode<T>(T* atkNode, AtkComponentBase* componentBase) where T : unmanaged
{
var node = (AtkResNode*)atkNode;
if (node == null) return;

if (node->ParentNode->ChildNode == node)
{
node->ParentNode->ChildNode = node->NextSiblingNode;
}

if (node->NextSiblingNode is not null && node->NextSiblingNode->PrevSiblingNode == node)
{
node->NextSiblingNode->PrevSiblingNode = node->PrevSiblingNode;
}

if (node->PrevSiblingNode is not null && node->PrevSiblingNode->NextSiblingNode == node)
{
node->PrevSiblingNode->NextSiblingNode = node->NextSiblingNode;
}
componentBase->UldManager.UpdateDrawNodeList();
}

public static void FreeImageComponents(ref AtkImageNode* imageNode)
{
IMemorySpace.Free(imageNode->PartsList->Parts->UldAsset, (ulong)(sizeof(AtkUldAsset) * imageNode->PartsList->PartCount));
IMemorySpace.Free(imageNode->PartsList->Parts, (ulong)(sizeof(AtkUldPart) * imageNode->PartsList->PartCount));
IMemorySpace.Free(imageNode->PartsList, (ulong)sizeof(AtkUldPartsList));
imageNode->AtkResNode.Destroy(false);
IMemorySpace.Free(imageNode, (ulong)sizeof(AtkImageNode));
}

private static AtkUldPartsList* CreateUldParts(uint UldPartsListID, UldWrapper uld, int partList)
{
if (!uld.Valid || uld.Uld is null)
{
return null;
}
var uldFile = uld.Uld;
var partCount = uldFile.Parts[partList].PartCount;
var atkPartsList = (AtkUldPartsList*)IMemorySpace.GetUISpace()->Malloc((ulong)sizeof(AtkUldPartsList), 8);
if (atkPartsList is null)
{
return null;
}
atkPartsList->Id = UldPartsListID;
atkPartsList->PartCount = partCount;
var atkUldParts = (AtkUldPart*)IMemorySpace.GetUISpace()->Malloc((ulong)(sizeof(AtkUldPart) * partCount), 8);
if (atkUldParts is null)
{
IMemorySpace.Free(atkPartsList, (ulong)sizeof(AtkUldPartsList));
return null;
}
for (var i = 0; i < partCount; i++)
{
atkUldParts[i].U = uldFile.Parts[partList].Parts[i].U;
atkUldParts[i].V = uldFile.Parts[partList].Parts[i].V;
atkUldParts[i].Width = uldFile.Parts[partList].Parts[i].W;
atkUldParts[i].Height = uldFile.Parts[partList].Parts[i].H;
}
atkPartsList->Parts = atkUldParts;
var atkUldAsset = (AtkUldAsset*)IMemorySpace.GetUISpace()->Malloc((ulong)(sizeof(AtkUldAsset) * partCount), 8);
if (atkUldAsset is null)
{
IMemorySpace.Free(atkUldParts, (ulong)(sizeof(AtkUldPart) * partCount));
IMemorySpace.Free(atkPartsList, (ulong)sizeof(AtkUldPartsList));
return null;
}
for (var i = 0; i < partCount; ++i)
{
atkUldAsset->Id = NodeIdBase;
NodeIdBase += 16;
atkUldAsset->AtkTexture.Ctor();
atkUldParts[i].UldAsset = &atkUldAsset[i];
}

return atkPartsList;
}

public static AtkImageNode* CreateImageNode(uint UldPartsListID, UldWrapper uld, int partList, uint ImageNodeID, string texturePath, ushort partIndex, AtkComponentNode* parent, AtkResNode* targetNode, bool visibility)
{
var imageNode = IMemorySpace.GetUISpace()->Create<AtkImageNode>();
if (imageNode is null)
{
return null;
}
var atkPartList = CreateUldParts(UldPartsListID, uld, partList);
if (atkPartList is null)
{
IMemorySpace.Free(imageNode, (ulong)sizeof(AtkImageNode));
return null;
}
imageNode->AtkResNode.Type = NodeType.Image;
imageNode->AtkResNode.NodeID = ImageNodeID;
imageNode->PartsList = atkPartList;
imageNode->PartId = partIndex;
imageNode->LoadTexture(texturePath);
imageNode->AtkResNode.NodeFlags = NodeFlags.AnchorTop | NodeFlags.AnchorLeft | NodeFlags.Enabled | NodeFlags.Visible | NodeFlags.EmitsEvents;
imageNode->AtkResNode.DrawFlags |= 1;
imageNode->WrapMode = 1;
imageNode->Flags = 0;
imageNode->AtkResNode.SetWidth(160);
imageNode->AtkResNode.SetHeight(20);
imageNode->AtkResNode.SetScale(1, 1);
imageNode->AtkResNode.ToggleVisibility(visibility);
LinkNodeAfterTargetNode(&imageNode->AtkResNode, parent, targetNode);
return imageNode;
}
}
Loading

0 comments on commit ad88031

Please sign in to comment.