diff --git a/Configuration.cs b/Configuration.cs index c5c4107..06844eb 100644 --- a/Configuration.cs +++ b/Configuration.cs @@ -24,6 +24,8 @@ public class Configuration : IPluginConfiguration { public int TextMulRed = 100; public int TextMulGreen = 100; public int TextMulBlue = 100; + public bool ClearResultsOnInactive = true; + public float ClearDelaySeconds = 10; } public enum MeterMode { diff --git a/Plugin.cs b/Plugin.cs index a99b163..2392281 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -3,11 +3,14 @@ using System.Globalization; using System.Text; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Game.Gui.ContextMenu; +using Dalamud.Game.Text; 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.Graphics; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -37,14 +40,21 @@ public class Plugin : IDalamudPlugin { [PluginService] internal ICommandManager CommandManager { get; init; } + [PluginService] + internal IContextMenu ContextMenu { get; init; } + internal Configuration Config { get; } private Client Client { get; } internal PluginUi Ui { get; } private Commands Commands { get; } - private Stopwatch Watch { get; } = Stopwatch.StartNew(); + private Stopwatch AlternateWatch { get; } = Stopwatch.StartNew(); + private Stopwatch DelayWatch { get; } = new(); private bool _ranLastTick; private bool _showDps = true; + private bool _reset; + private bool _wasActive; + private bool _manuallyReset = true; private readonly byte[] _manaUsers = [ 6, // cnj @@ -70,9 +80,11 @@ public class Plugin : IDalamudPlugin { this.Commands = new Commands(this); this.AddonLifecycle!.RegisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList); + this.ContextMenu!.OnMenuOpened += this.MenuOpened; } public void Dispose() { + this.ContextMenu.OnMenuOpened -= this.MenuOpened; this.AddonLifecycle.UnregisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList); this.Commands.Dispose(); this.Ui.Dispose(); @@ -83,24 +95,114 @@ public class Plugin : IDalamudPlugin { this.Interface.SavePluginConfig(this.Config); } + private unsafe void MenuOpened(IMenuOpenedArgs args) { + var add = args.AddonName == "_PartyList"; + if (!add) { + var ctx = AgentContext.Instance(); + if (args.AgentPtr != (nint) ctx) { + return; + } + + var targetId = ctx->TargetObjectId.ObjectId; + if (targetId == 0xE000_0000) { + return; + } + + if (this.ClientState.LocalPlayer is { } player && player.GameObjectId == targetId) { + add = true; + } else { + var group = GroupManager.Instance()->GetGroup(); + if (group != null && group->GetPartyMemberByEntityId(targetId) != null) { + add = true; + } + } + } + + if (!add) { + return; + } + + args.AddMenuItem(new MenuItem { + Name = "Clear parse results", + Prefix = SeIconChar.BoxedLetterP, + PrefixColor = 37, + IsEnabled = this.Client.Data?.IsActive == false, + OnClicked = _ => { + this._reset = true; + this._manuallyReset = true; + }, + }); + } + private unsafe void UpdateList(AddonEvent type, AddonArgs args) { var ranLast = this._ranLastTick; this._ranLastTick = false; var list = (AddonPartyList*) AtkStage.Instance()->RaptureAtkUnitManager->GetAddonByName("_PartyList"); - if (!this.UpdateListInner(list) && ranLast) { + if (list == null) { + return; + } + + var shouldUpdate = false; + + var wasActive = this._wasActive; + var becameInactive = false; + if (this.Client.Data is { } data) { + if (wasActive && !data.IsActive) { + becameInactive = true; + } + + this._wasActive = data.IsActive; + } + + if (this.Client.Data is { IsActive: true }) { + shouldUpdate = true; + this._manuallyReset = false; + } else if (becameInactive) { + if (this.Config.ClearResultsOnInactive) { + this.DelayWatch.Restart(); + } + } + + if (this.Config.ClearResultsOnInactive && this.DelayWatch.IsRunning && this.DelayWatch.Elapsed >= TimeSpan.FromSeconds(this.Config.ClearDelaySeconds)) { + this.DelayWatch.Reset(); + this._reset = true; + } + + if (!this._reset && !this._manuallyReset) { + // only do these checks if we shouldn't reset and if we're waiting for active data + + // keep running if we're in the delay period + if (this.DelayWatch.IsRunning) { + shouldUpdate = true; + } + + // keep running if the user wants to manually clear results + if (!this.Config.ClearResultsOnInactive) { + shouldUpdate = true; + } + } + + if (this._reset) { + this.DelayWatch.Reset(); + this._reset = false; + this._manuallyReset = true; + this.ResetMembers(list); + } + + if (shouldUpdate && this.UpdateListInner(list) && ranLast) { this.ResetMembers(list); } } + /// + /// Update the party list. + /// + /// true if the list should be reset immediately private unsafe bool UpdateListInner(AddonPartyList* list) { if (this.Client.Data is not { } data) { return false; } - if (!data.IsActive) { - return false; - } - if (this.ClientState.LocalPlayer is not { } player) { return false; } @@ -109,7 +211,7 @@ public class Plugin : IDalamudPlugin { var playerName = player.Name.TextValue; if (list->HoveredIndex >= 0 || list->TargetedIndex >= 0) { - return false; + return true; } var names = new List<(string, int)>(); @@ -122,7 +224,7 @@ public class Plugin : IDalamudPlugin { // controller soft target not handled by hoveredindex above if (chara->GetSoftTargetId() == member.EntityId) { - return false; + return true; } var name = MemoryHelper.ReadStringNullTerminated((nint) member.Name); @@ -131,8 +233,8 @@ public class Plugin : IDalamudPlugin { this._ranLastTick = true; - if (this.Watch.Elapsed.TotalMilliseconds > this.Config.AlternateSeconds * 1_000) { - this.Watch.Restart(); + if (this.AlternateWatch.Elapsed.TotalMilliseconds > this.Config.AlternateSeconds * 1_000) { + this.AlternateWatch.Restart(); this._showDps ^= true; } @@ -150,7 +252,7 @@ public class Plugin : IDalamudPlugin { this.UpdateMember(list->PartyMembers[i], member.Object, data.Encounter, combatant); } - return true; + return false; } private unsafe void ResetMembers(AddonPartyList* list) { diff --git a/PluginUi.cs b/PluginUi.cs index fe6409f..0956f39 100644 --- a/PluginUi.cs +++ b/PluginUi.cs @@ -75,7 +75,7 @@ public class PluginUi : IDisposable { this.Plugin.Config.TextColour = ConvertRgba(textColour); } - if (ImGui.TreeNodeEx("Advanced colour options")) { + if (ImGui.TreeNodeEx("Advanced colour options##text")) { using var treePop = new OnDispose(ImGui.TreePop); anyChanged |= ImGui.SliderInt("Add red", ref this.Plugin.Config.TextAddRed, 0, 255); @@ -86,6 +86,11 @@ public class PluginUi : IDisposable { anyChanged |= ImGui.SliderInt("Multiply blue", ref this.Plugin.Config.TextMulBlue, 0, 100); } + anyChanged |= ImGui.Checkbox("Clear results after encounter ends", ref this.Plugin.Config.ClearResultsOnInactive); + using (ImGuiHelper.DisabledUnless(this.Plugin.Config.ClearResultsOnInactive)) { + anyChanged |= ImGui.SliderFloat("Seconds to delay before clearing", ref this.Plugin.Config.ClearDelaySeconds, 0, 300); + } + if (anyChanged) { this.Plugin.SaveConfig(); }