Create framework for YesHealMe (AU NoTankYou).

This commit is contained in:
Bernardo Lopes 2023-03-20 10:46:03 -03:00
parent 197abfeb0b
commit 4dbf27089a
5 changed files with 187 additions and 0 deletions

View file

@ -135,6 +135,16 @@ internal class FoolsManager : IDisposable, IServiceType
RealAuthor = "Chirp",
Type = typeof(GoodVibesPlugin),
},
new()
{
Name = "YesHealMe",
InternalName = "YesHealMePlugin",
Description = "The only warning you need.",
Author = "MidoriKami",
RealAuthor = "Berna",
Type = typeof(YesHealMePlugin),
},
};
}

View file

@ -0,0 +1,41 @@
using Dalamud;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.SubKinds;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
namespace NoTankYou.Utilities;
public static unsafe class HudHelper
{
private static AgentHUD* AgentHud => AgentModule.Instance()->GetAgentHUD();
public static PlayerCharacter? GetPlayerCharacter(int index)
{
// Sorta temporary, waiting for ClientStructs to merge a fixed size array for this element
var partyMemberList = AgentHud->PartyMemberList;
var targetOffset = index * sizeof(HudPartyMember);
var targetAddress = partyMemberList + targetOffset;
var hudData = (HudPartyMember*) targetAddress;
var targetPlayer = hudData->ObjectId;
return GetPlayer(targetPlayer);
}
public static PlayerCharacter? GetAllianceMember(int index)
{
var targetPlayer = AgentHud->RaidMemberIds[index];
return GetPlayer(targetPlayer);
}
private static PlayerCharacter? GetPlayer(uint objectId)
{
var result = Service<ObjectTable>.Get().SearchById(objectId);
if (result?.GetType() == typeof(PlayerCharacter))
return result as PlayerCharacter;
return null;
}
}

View file

@ -0,0 +1,67 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud;
using Dalamud.Game;
using Dalamud.Game.Gui;
using FFXIVClientStructs.FFXIV.Client.UI;
using NoTankYou.DataModels;
using NoTankYou.Utilities;
namespace NoTankYou.System;
public unsafe class PartyListAddon : IEnumerable<PartyListAddonData>, IDisposable
{
public record PartyFramePositionInfo(Vector2 Position, Vector2 Size, Vector2 Scale);
public IEnumerator<PartyListAddonData> GetEnumerator() => addonData.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private static AddonPartyList* PartyList => (AddonPartyList*) Service<GameGui>.Get()?.GetAddonByName("_PartyList");
public static bool DataAvailable => PartyList != null && PartyList->AtkUnitBase.RootNode != null;
private readonly List<PartyListAddonData> addonData = new();
public PartyListAddon()
{
Service<Framework>.Get().Update += OnFrameworkUpdate;
}
public void Dispose()
{
Service<Framework>.Get().Update -= OnFrameworkUpdate;
}
private void OnFrameworkUpdate(Framework framework)
{
addonData.Clear();
if (!DataAvailable) return;
if (PartyList->MemberCount <= 0) return;
foreach (var index in Enumerable.Range(0, PartyList->MemberCount))
{
var playerCharacter = HudHelper.GetPlayerCharacter(index);
var userInterface = PartyList->PartyMember[index];
addonData.Add(new PartyListAddonData
{
PlayerCharacter = playerCharacter,
UserInterface = userInterface,
});
}
}
public static PartyFramePositionInfo GetPositionInfo()
{
// Resource Node (id 9) contains a weird offset for the actual list elements
var rootNode = PartyList->AtkUnitBase.RootNode;
var addonBasePosition = new Vector2(rootNode->X, rootNode->Y);
var scale = new Vector2(rootNode->ScaleX, rootNode->ScaleY);
var partyListNode = PartyList->AtkUnitBase.GetNodeById(9);
var partyListPositionOffset = new Vector2(partyListNode->X, partyListNode->Y) * scale;
var partyListSize = new Vector2(partyListNode->Width, partyListNode->Height);
return new PartyFramePositionInfo(addonBasePosition + partyListPositionOffset, partyListSize * scale, scale);
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Dalamud.Game.ClientState.Objects.SubKinds;
using FFXIVClientStructs.FFXIV.Client.UI;
namespace NoTankYou.DataModels;
public readonly unsafe struct PartyListAddonData
{
private static readonly Dictionary<uint, Stopwatch> TimeSinceLastTargetable = new();
public AddonPartyList.PartyListMemberStruct UserInterface { get; init; }
public PlayerCharacter? PlayerCharacter { get; init; }
private bool Targetable => UserInterface.PartyMemberComponent->OwnerNode->AtkResNode.Color.A != 0x99;
public bool IsTargetable()
{
if (PlayerCharacter is null) return false;
TimeSinceLastTargetable.TryAdd(PlayerCharacter.ObjectId, Stopwatch.StartNew());
var stopwatch = TimeSinceLastTargetable[PlayerCharacter.ObjectId];
if (Targetable)
{
// Returns true if the party member has been targetable for 2second or more
return stopwatch.Elapsed >= TimeSpan.FromSeconds(2);
}
else
{
// Returns false, and continually resets the stopwatch
stopwatch.Restart();
return false;
}
}
}

View file

@ -0,0 +1,32 @@
using System.Linq;
using Dalamud.Logging;
using NoTankYou.System;
namespace Dalamud.Fools.Plugins;
public class YesHealMePlugin: IFoolsPlugin
{
private PartyListAddon partyListAddon;
public YesHealMePlugin()
{
partyListAddon = new PartyListAddon();
}
public void DrawUi()
{
foreach (var partyMember in this.partyListAddon.Select(pla => pla.PlayerCharacter).Where(pc => pc is not null))
{
if (partyMember.CurrentHp < partyMember.MaxHp)
{
// Do things here
}
}
}
public void Dispose()
{
this.partyListAddon.Dispose();
}
}