using System.Diagnostics; using System.Globalization; using System.Numerics; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.IoC; using Dalamud.Memory; using Dalamud.Plugin; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Info; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; namespace PartyDamage; public class Plugin : IDalamudPlugin { [PluginService] internal static IPluginLog Log { get; private set; } [PluginService] private IAddonLifecycle AddonLifecycle { get; init; } [PluginService] private IClientState ClientState { get; init; } [PluginService] private IFramework Framework { get; init; } [PluginService] private IPartyList PartyList { get; init; } [PluginService] private IDalamudPluginInterface Interface { get; init; } private Client Client { get; } public Plugin() { this.Client = new Client(); this.AddonLifecycle!.RegisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList); // this.Interface!.UiBuilder.Draw += this.Draw; } public void Dispose() { // this.Interface.UiBuilder.Draw -= this.Draw; this.AddonLifecycle.UnregisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList); this.Client.Dispose(); } private unsafe void Draw() { if (this.Client.Data is not { } data) { return; } var names = new List(); foreach (var member in AgentHUD.Instance()->PartyMembers) { if (member.Name == null) { continue; } var name = MemoryHelper.ReadStringNullTerminated((nint) member.Name); names.Add(name); } var list = (AddonPartyList*) AtkStage.Instance()->RaptureAtkUnitManager->GetAddonByName("_PartyList"); foreach (var combatant in data.Combatants.Values) { var idx = combatant.Name == "YOU" ? 0 : names.IndexOf(combatant.Name); if (idx == -1) { continue; } var dpsText = combatant.EncDps switch { float.NaN => "?", float.PositiveInfinity => "0", float.NegativeInfinity => "0", < 1_000 => combatant.EncDps.ToString("N1"), < 1_000_000 => $"{combatant.EncDps / 1_000:N1}K", < 1_000_000_000 => $"{combatant.EncDps / 1_000_000:N1}M", _ => combatant.EncDps.ToString("N1"), }; var gauge = list->PartyMembers[idx].MPGaugeBar->OwnerNode; var pos = new Vector2( gauge->ScreenX + gauge->Width, gauge->ScreenY + gauge->Height / 2f + 8 ); var size = ImGui.CalcTextSize(dpsText); ImGui.GetBackgroundDrawList().AddRectFilled( pos - Vector2.One * 2, pos + size + Vector2.One * 2, 0xFF000000 ); ImGui.GetBackgroundDrawList().AddText( pos, 0xFFFFFFFF, dpsText ); } } private Stopwatch Watch { get; } = Stopwatch.StartNew(); private unsafe void UpdateList(AddonEvent type, AddonArgs args) { if (this.Client.Data is not { } data) { return; } if (this.ClientState.LocalPlayer is not { } player) { return; } var chara = (Character*) player.Address; var playerName = player.Name.TextValue; var list = (AddonPartyList*) AtkStage.Instance()->RaptureAtkUnitManager->GetAddonByName("_PartyList"); if (list->HoveredIndex >= 0 || list->TargetedIndex >= 0) { return; } var names = new List(); var members = AgentHUD.Instance()->PartyMembers; for (var i = 0; i < members.Length && i < list->PartyMembers.Length; i++) { var member = members[i]; if (member.Name == null) { continue; } // controller soft target not handled by hoveredindex above if (chara->GetSoftTargetId() == member.EntityId) { return; } var name = MemoryHelper.ReadStringNullTerminated((nint) member.Name); names.Add(name); var lookupName = name == playerName ? "YOU" : name; if (!data.Combatants.TryGetValue(lookupName, out var combatant)) { continue; } this.UpdateMember(list->PartyMembers[i], member.Object, data.Encounter, combatant); } // var numPlayers = list->PartyMembers.Length; // foreach (var combatant in data.Combatants.Values) { // if (combatant.Name.EndsWith(" (YOU)")) { // var name = combatant.Name[..^6]; // var chocoboName = UIState.Instance()->Buddy.CompanionInfo.NameString; // if (chocoboName == name) { // this.UpdateMember(list->Chocobo, data.Encounter, combatant); // continue; // } // } // var idx = combatant.Name == "YOU" // ? 0 // : names.IndexOf(combatant.Name); // if (idx == -1 || idx >= numPlayers) { // continue; // } // this.UpdateMember(list->PartyMembers[idx], data.Encounter, combatant); // } } private unsafe void UpdateMember( AddonPartyList.PartyListMemberStruct member, BattleChara* chara, Encounter encounter, Combatant combatant ) { member.TargetGlow->ToggleVisibility(true); member.TargetGlow->SetAlpha(128); member.TargetGlow->SetScaleX( encounter.EncDps == 0 ? 0 : combatant.EncDps / encounter.EncDps ); var left = (AtkTextNode*) member.MPGaugeBar->GetTextNodeById(2); var right = (AtkTextNode*) member.MPGaugeBar->GetTextNodeById(3); if (this.Watch.Elapsed.Seconds % 6 < 3) { left->TextColor = new ByteColor { RGBA = 0xFFFFFFFF, }; right->TextColor = left->TextColor; var manaString = chara->Mana.ToString(CultureInfo.InvariantCulture); left->SetText(manaString[..^2]); right->SetText(manaString[^2..]); return; } left->TextColor = new ByteColor { RGBA = 0xedffecff, }; right->TextColor = left->TextColor; if (combatant.EncDps == 0 || float.IsInfinity(combatant.EncDps) || float.IsNaN(combatant.EncDps)) { left->SetText("0."); right->SetText("00"); } else if (combatant.EncDps < 1_000) { var dps = Math.Round(combatant.EncDps * 100).ToString(CultureInfo.InvariantCulture); left->SetText($"{dps[..^2]}."); right->SetText(dps[^2..]); } else if (combatant.EncDps < 1_000_000) { var dps = Math.Round(combatant.EncDps / 100).ToString(CultureInfo.InvariantCulture); left->SetText($"{dps[..^1]}."); right->SetText($"{dps[^1..]}K"); } // var dpsText = combatant.EncDps switch { // float.NaN => "?", // float.PositiveInfinity => "0", // float.NegativeInfinity => "0", // < 1_000 => combatant.EncDps.ToString("N1"), // < 1_000_000 => $"{combatant.EncDps / 1_000:N1}K", // < 1_000_000_000 => $"{combatant.EncDps / 1_000_000:N1}M", // _ => combatant.EncDps.ToString("N1"), // }; // if (!member.CastingProgressBar->IsVisible()) { // member.CastingActionName->ToggleVisibility(true); // member.CastingActionName->SetText(dpsText); // float x, y; // member.CastingActionName->GetPositionFloat(&x, &y); // member.CastingActionName->SetPositionFloat(200, 32); // member.CastingActionName->AlignmentType = AlignmentType.Left; // } else { // member.CastingActionName->AlignmentType = AlignmentType.Right; // member.CastingActionName->SetPositionFloat(0, 10); // } } }