From 1e7bd8a7bfad5121febf36b6d3631353ae504482 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:50:12 -0700 Subject: [PATCH] [`TargetCastBarCountdown`] Modernize Tweak (#645) * [`TargetCastBarCountdown`] Modernize Tweak * [`TargetCastBarCountdown`] Fix not saving on change --- Tweaks/UiAdjustment/TargetCastbarCountdown.cs | 205 +++++++----------- 1 file changed, 74 insertions(+), 131 deletions(-) diff --git a/Tweaks/UiAdjustment/TargetCastbarCountdown.cs b/Tweaks/UiAdjustment/TargetCastbarCountdown.cs index 45fb8e57..443553e2 100644 --- a/Tweaks/UiAdjustment/TargetCastbarCountdown.cs +++ b/Tweaks/UiAdjustment/TargetCastbarCountdown.cs @@ -1,44 +1,42 @@ #nullable enable using System; using System.Numerics; +using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; +using SimpleTweaksPlugin.Events; using SimpleTweaksPlugin.TweakSystem; using SimpleTweaksPlugin.Utility; namespace SimpleTweaksPlugin.Tweaks.UiAdjustment; -public unsafe class TargetCastbarCountdown : UiAdjustments.SubTweak -{ - public override string Name => "Target Castbar Countdown"; - public override string Description => "Displays time remaining on targets ability cast."; - protected override string Author => "MidoriKami"; +[TweakName("Target Castbar Countdown")] +[TweakDescription("Displays time remaining on targets ability cast.")] +[TweakAuthor("MidoriKami")] +[TweakAutoConfig] +[TweakReleaseVersion("1.8.3.0")] +[Changelog("1.8.3.1", "Add TopRight option for displaying countdown")] +[Changelog("1.8.9.0", "Add option to disable on primary target")] +public unsafe class TargetCastbarCountdown : UiAdjustments.SubTweak { + private uint CastBarTextNodeId => CustomNodes.Get(this, "Countdown"); - private static AtkUnitBase* AddonTargetInfoCastBar => Common.GetUnitBase("_TargetInfoCastBar"); - private static AtkUnitBase* AddonTargetInfo => Common.GetUnitBase("_TargetInfo"); - private static AtkUnitBase* AddonFocusTargetInfo => Common.GetUnitBase("_FocusTargetInfo"); - - private const uint CastbarTextNodeId = 3000U; - private readonly ByteColor textColor = new() { R = 255, G = 255, B = 255, A = 255 }; private readonly ByteColor edgeColor = new() { R = 142, G = 106, B = 12, A = 255 }; private readonly ByteColor backgroundColor = new() { R = 0, G = 0, B = 0, A = 0 }; - + private Config TweakConfig { get; set; } = null!; - private class Config : TweakConfig - { + private class Config : TweakConfig { public bool PrimaryTargetEnabled = true; public bool FocusTargetEnabled = false; public NodePosition FocusTargetPosition = NodePosition.Left; public NodePosition CastbarPosition = NodePosition.BottomLeft; } - private enum NodePosition - { + private enum NodePosition { Right, Left, TopLeft, @@ -47,47 +45,38 @@ private enum NodePosition BottomRight } - protected override void ConfigChanged() - { - SaveConfig(TweakConfig); - FreeAllNodes(); - } - - protected override DrawConfigDelegate DrawConfigTree => (ref bool hasChanged) => - { - if (ImGui.Checkbox("Enable Primary Target", ref TweakConfig.PrimaryTargetEnabled)) hasChanged = true; + private void DrawConfig() { + var hasChanged = ImGui.Checkbox("Enable Primary Target", ref TweakConfig.PrimaryTargetEnabled); - if (ImGui.Checkbox("Enable Focus Target", ref TweakConfig.FocusTargetEnabled)) hasChanged = true; + hasChanged |= ImGui.Checkbox("Enable Focus Target", ref TweakConfig.FocusTargetEnabled); ImGui.TextUnformatted("Select which direction relative to Cast Bar to show countdown"); - if (TweakConfig is { PrimaryTargetEnabled: false, FocusTargetEnabled: false }) - { + if (TweakConfig is { PrimaryTargetEnabled: false, FocusTargetEnabled: false }) { ImGuiHelpers.ScaledIndent(20.0f); - ImGui.TextUnformatted("No Castbars Selected"); + ImGui.TextUnformatted("No CastBars Selected"); ImGuiHelpers.ScaledIndent(-20.0f); } - if (TweakConfig.FocusTargetEnabled) - { - if (DrawCombo(ref TweakConfig.FocusTargetPosition, "Focus Target")) hasChanged = true; + if (TweakConfig.FocusTargetEnabled) { + hasChanged |= DrawCombo(ref TweakConfig.FocusTargetPosition, "Focus Target"); } - if (TweakConfig.PrimaryTargetEnabled) - { - if (DrawCombo(ref TweakConfig.CastbarPosition, "Primary Target")) hasChanged = true; + if (TweakConfig.PrimaryTargetEnabled) { + hasChanged |= DrawCombo(ref TweakConfig.CastbarPosition, "Primary Target"); } - }; - private bool DrawCombo(ref NodePosition setting, string label) - { + if (hasChanged) { + SaveConfig(TweakConfig); + FreeAllNodes(); + } + } + + private bool DrawCombo(ref NodePosition setting, string label) { var regionSize = ImGui.GetContentRegionAvail(); ImGui.SetNextItemWidth(regionSize.X * 1.0f / 3.0f); - if (ImGui.BeginCombo(label, setting.ToString())) - { - foreach (var direction in Enum.GetValues()) - { - if (ImGui.Selectable(direction.ToString(), setting == direction)) - { + if (ImGui.BeginCombo(label, setting.ToString())) { + foreach (var direction in Enum.GetValues()) { + if (ImGui.Selectable(direction.ToString(), setting == direction)) { setting = direction; return true; } @@ -98,108 +87,60 @@ private bool DrawCombo(ref NodePosition setting, string label) return false; } - - public override void Setup() - { - AddChangelogNewTweak("1.8.3.0"); - AddChangelog("1.8.3.1", "Add TopRight option for displaying countdown"); - AddChangelog("1.8.9.0", "Add option to disable on primary target"); - base.Setup(); - } - protected override void Enable() - { - TweakConfig = LoadConfig() ?? new Config(); - - Common.FrameworkUpdate += OnFrameworkUpdate; - Service.ClientState.EnterPvP += OnEnterPvP; - Service.ClientState.LeavePvP += OnLeavePvP; - base.Enable(); - } - - protected override void Disable() - { - SaveConfig(TweakConfig); - - Common.FrameworkUpdate -= OnFrameworkUpdate; - Service.ClientState.EnterPvP -= OnEnterPvP; - Service.ClientState.LeavePvP -= OnLeavePvP; - - FreeAllNodes(); - - base.Disable(); - } - - private void OnEnterPvP() - { - Common.FrameworkUpdate -= OnFrameworkUpdate; - + protected override void Disable() { FreeAllNodes(); } - - private void OnLeavePvP() - { - Common.FrameworkUpdate += OnFrameworkUpdate; - } - private void OnFrameworkUpdate() - { - // If we get here while in any kind of PvP area, unregister this callback and free nodes. - if (Service.ClientState.IsPvP) - { - Common.FrameworkUpdate -= OnFrameworkUpdate; - FreeAllNodes(); - return; - } + [AddonPostRequestedUpdate("_TargetInfoCastBar", "_TargetInfo", "_FocusTargetInfo")] + private void OnAddonRequestedUpdate(AddonArgs args) { + if (Service.ClientState.IsPvP) return; - // Castbar is split from target info - if (AddonTargetInfoCastBar is not null && AddonTargetInfoCastBar->IsVisible && TweakConfig.PrimaryTargetEnabled) UpdateAddon(AddonTargetInfoCastBar, 7, 2, Service.Targets.Target); + var addon = (AtkUnitBase*) args.Addon; - // Castbar is combined with target info - if (AddonTargetInfo is not null && AddonTargetInfo->IsVisible && TweakConfig.PrimaryTargetEnabled) UpdateAddon(AddonTargetInfo, 15, 10, Service.Targets.Target); + switch (args.AddonName) { + case "_TargetInfoCastBar" when addon->IsVisible: + UpdateAddon(addon, 7, 2, Service.Targets.Target); + break; - // Focus target castbar - if (AddonFocusTargetInfo is not null && AddonFocusTargetInfo->IsVisible && TweakConfig.FocusTargetEnabled) UpdateAddon(AddonFocusTargetInfo, 8, 3, Service.Targets.FocusTarget, true); + case "_TargetInfo" when addon->IsVisible: + UpdateAddon(addon, 15, 10, Service.Targets.Target); + break; + + case "_FocusTargetInfo" when addon->IsVisible: + UpdateAddon(addon, 8, 3, Service.Targets.FocusTarget, true); + break; + } } - private void UpdateAddon(AtkUnitBase* addon, uint visibilityNodeId, uint positioningNodeId, GameObject? target, bool focusTarget = false) - { + private void UpdateAddon(AtkUnitBase* addon, uint visibilityNodeId, uint positioningNodeId, GameObject? target, bool focusTarget = false) { var interruptNode = Common.GetNodeByID(&addon->UldManager, visibilityNodeId); - var castbarNode = Common.GetNodeByID(&addon->UldManager, positioningNodeId); - if (interruptNode is not null && castbarNode is not null) - { - TryMakeNodes(addon, castbarNode, focusTarget); + var castBarNode = Common.GetNodeByID(&addon->UldManager, positioningNodeId); + if (interruptNode is not null && castBarNode is not null) { + TryMakeNodes(addon, castBarNode, focusTarget); UpdateIcons(interruptNode->AtkResNode.IsVisible, addon, target); } } - private void TryMakeNodes(AtkUnitBase* parent, AtkResNode* positionNode, bool focusTarget) - { - if (!UiHelper.IsAddonReady(parent)) return; - - var textNode = Common.GetNodeByID(&parent->UldManager, CastbarTextNodeId); - if (textNode is null) MakeTextNode(parent, CastbarTextNodeId, positionNode, focusTarget); + private void TryMakeNodes(AtkUnitBase* parent, AtkResNode* positionNode, bool focusTarget) { + var textNode = Common.GetNodeByID(&parent->UldManager, CastBarTextNodeId); + if (textNode is null) MakeTextNode(parent, CastBarTextNodeId, positionNode, focusTarget); } - private void UpdateIcons(bool castBarVisible, AtkUnitBase* parent, GameObject? target) - { - var textNode = Common.GetNodeByID(&parent->UldManager, CastbarTextNodeId); - + private void UpdateIcons(bool castBarVisible, AtkUnitBase* parent, GameObject? target) { + var textNode = Common.GetNodeByID(&parent->UldManager, CastBarTextNodeId); if (textNode is null) return; - if (target as BattleChara is { IsCasting: true } targetInfo && castBarVisible && targetInfo.TotalCastTime > targetInfo.CurrentCastTime) - { + if (target as BattleChara is { IsCasting: true } targetInfo && castBarVisible && targetInfo.TotalCastTime > targetInfo.CurrentCastTime) { textNode->AtkResNode.ToggleVisibility(true); textNode->SetText($"{targetInfo.TotalCastTime - targetInfo.CurrentCastTime:00.00}"); } - else - { + else { textNode->AtkResNode.ToggleVisibility(false); } } - private void MakeTextNode(AtkUnitBase* parent, uint nodeId, AtkResNode* positioningNode, bool focusTarget) - { + private void MakeTextNode(AtkUnitBase* parent, uint nodeId, AtkResNode* positioningNode, bool focusTarget) { var textNode = UiHelper.MakeTextNode(nodeId); textNode->AtkResNode.NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.AnchorTop | NodeFlags.AnchorLeft; @@ -213,13 +154,12 @@ private void MakeTextNode(AtkUnitBase* parent, uint nodeId, AtkResNode* position textNode->LineSpacing = 20; textNode->AlignmentFontType = 37; textNode->FontSize = 20; - textNode->TextFlags = (byte) (TextFlags.Edge); + textNode->TextFlags = (byte) TextFlags.Edge; textNode->AtkResNode.SetWidth(80); textNode->AtkResNode.SetHeight(22); - var nodePosition = (focusTarget ? TweakConfig.FocusTargetPosition : TweakConfig.CastbarPosition) switch - { + var nodePosition = (focusTarget ? TweakConfig.FocusTargetPosition : TweakConfig.CastbarPosition) switch { NodePosition.Left => new Vector2(positioningNode->X - 80, positioningNode->Y), NodePosition.Right => new Vector2(positioningNode->X + positioningNode->Width, positioningNode->Y), NodePosition.TopLeft => new Vector2(positioningNode->X, positioningNode->Y - 14), @@ -234,18 +174,21 @@ private void MakeTextNode(AtkUnitBase* parent, uint nodeId, AtkResNode* position UiHelper.LinkNodeAtEnd((AtkResNode*) textNode, parent); } - private void FreeAllNodes() - { - TryFreeTextNode(AddonTargetInfoCastBar, CastbarTextNodeId); - TryFreeTextNode(AddonTargetInfo, CastbarTextNodeId); - TryFreeTextNode(AddonFocusTargetInfo, CastbarTextNodeId); + private void FreeAllNodes() { + var addonTargetInfoCastBar = Common.GetUnitBase("_TargetInfoCastBar"); + var addonTargetInfo = Common.GetUnitBase("_TargetInfo"); + var addonFocusTargetInfo = Common.GetUnitBase("_FocusTargetInfo"); + + TryFreeTextNode(addonTargetInfoCastBar, CastBarTextNodeId); + TryFreeTextNode(addonTargetInfo, CastBarTextNodeId); + TryFreeTextNode(addonFocusTargetInfo, CastBarTextNodeId); } private void TryFreeTextNode(AtkUnitBase* addon, uint nodeId) { if (addon == null) return; + var textNode = Common.GetNodeByID(&addon->UldManager, nodeId); - if (textNode is not null) - { + if (textNode is not null) { UiHelper.UnlinkAndFreeTextNode(textNode, addon); } }