Thread Tools Display Modes
10-29-22, 09:41 AM   #1
Pubert
A Defias Bandit
Join Date: Oct 2022
Posts: 3
Question Making interactive Personal Resource Display

Hiya complete beginner with addons as well as posting on forums so bear with me on this one!
I'd made a weakaura to display more information about my character than plater allows and match the nameplates I have installed visually and messed around trying to add Togglemenu functionality to it so I could do away with unit frames entirely and conserve UI space.

I realize it's bad practice to add secure actions to a weakaura so I wrote a lil' addon for that process instead
Code:
local function MoveStuff()
    local healthButton = CreateFrame("BUTTON","healthButton", YOUR_FRAME, "SecureActionButtonTemplate");
    healthButton:SetPoint("CENTER", C_NamePlate.GetNamePlateForUnit("player"), "CENTER", 0, 0)
    healthButton:SetSize(130, 40)
    healthButton:RegisterForClicks('AnyUp')
    healthButton:SetAttribute('unit', 'player')
    healthButton:SetAttribute('*type1', 'target')
    healthButton:SetAttribute('*type2', 'togglemenu')
end

local f = CreateFrame('Frame')
f:RegisterEvent('UPDATE_MOUSEOVER_UNIT')
f:SetScript('OnEvent', MoveStuff)
It works fine but has a quirk, I tried setting the Event it registers to PLAYER_ENTERING_WORLD but that seemed to be too early for the code to register the location of NamePlateForUnit("player"), therefore slapping it on the center of my screen instead. So I then tried putting it on SPELLS_CHANGED and I got it to work, I could right click my personal resource display and get the toggle menu, but it kept making two frames, one placed squarely at the center of my screen and the other on the desired nameplate so eventually I landed on UPDATE_MOUSEOVER_UNIT, which works! No frame at the center of my screen.

But if my character dies or seemingly at random points, the frame at the center reappears which I assume is down to the addon persisting to make these frames and the location of the nameplate not being available at all times, is there something I can do to fix this? A better event to check? I'm lost.
  Reply With Quote
10-29-22, 04:51 PM   #2
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
Originally Posted by Pubert View Post
I realize it's bad practice to add secure actions to a weakaura so I wrote a lil' addon for that process instead
To be clear, there's a big difference between "bad practice" and "will not work" or "sometimes works with disastrous side effects". Dealing with secure/protected actions most often falls under the later categories.

Making this idea work isn't completely impossible, though we'll need to take advantage of a quirk with how NamePlates are set up.
Lua Code:
  1. local HookedUnitFrames={};
  2.  
  3. local function HookUnitFrame(unitframe)
  4.     if InCombatLockdown() then return false; end--  Signal we can't create the frame now
  5.  
  6.     local clickframe=CreateFrame("Button",nil,unitframe,"SecureUnitButtonTemplate");
  7.     clickframe:SetAllPoints(unitframe);
  8.     clickframe:RegisterForClicks("AnyUp");
  9.     clickframe:SetAttribute("useparent-unit",true);--   Defer to UnitFrame's unit attribute
  10.     clickframe:SetAttribute("*type1","target");
  11.     clickframe:SetAttribute("*type2","togglemenu");
  12.     return true;--  We created the frame
  13. end
  14.  
  15. hooksecurefunc(NamePlateDriverFrame,"AcquireUnitFrame",function(base)
  16. --  Exit early if forbidden or we've already encountered the frame
  17.     if base:IsForbidden() or HookedUnitFrames[base.UnitFrame]~=nil then return; end
  18.     HookedUnitFrames[base.UnitFrame]=HookUnitFrame(base.UnitFrame);--   Attempt hook and store success flag (fail registers for retry)
  19. end);
  20.  
  21. EventRegistry:RegisterFrameEventAndCallback("PLAYER_REGEN_ENABLED",function()
  22.     for unitframe,hooked in pairs(HookedUnitFrames) do
  23. --      Retry hook if failed and update flag
  24.         if not hooked then HookedUnitFrames[unitframe]=HookUnitFrame(unitframe); end
  25.     end
  26. end);

First, we hook NamePlateDriverFrame:AcquireUnitFrame() to attach our "clickframe" to. What the original function does is grab a CompactUnitFrame from a frame pool and attach it to the NamePlate. Note while the code names it NamePlateDriverMixin, this table is already loaded into NamePlateDriverFrame by the time our code runs. Hooking the function from NamePlateDriverMixin will essentially do nothing useful.

What we do is access that CompactUnitFrame and attach our SecureUnitButton to it. This lets us access a feature of the secure templates to refer to their parent for one or more attributes. SecureUnitButton was swapped in for its special handling of spell target cursors. Notably, it cancels spell targeting if the action is to bring up a menu.

Coincidentally, CompactUnitFrames are set with their own unit attribute, so we point to that by setting our "useparent-unit" to true. Note "useparent-" is the keyword and the "unit" suffix tells it which attribute to act on. Similarly, you can define "useparent*" to defer all undefined attributes to the parent.

A side effect to this approach is we can't control which NamePlates get our "clickframes". They all will. I also created a fallback for NamePlates that are created while in combat. Since we can't manipulate them during lockdown, we delay it until we're no longer in combat.

Last to note is use of the new EventRegistry, extending CallbackRegistry.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 10-29-22 at 04:55 PM.
  Reply With Quote
10-30-22, 06:27 AM   #3
Pubert
A Defias Bandit
Join Date: Oct 2022
Posts: 3
Can't seem to get your suggestion to work for me, only got it working once while keeping two units in my line of sight, entering combat with one and not the other but I haven't been able to recreate it and it ends up putting default blizzard nameplates visibly behind my plater nameplates, thanks anyway though, will incorporate a combatlockdown check.

Edit: Bashed my head at it some more and came up with something resembling what I'm going for, the only issue remaining is new entities running off with a click frame that belongs to me, but it doesn't get rid of my own which is good. I realize it lacks elegance, decorum and grace but yeah

Lua Code:
  1. personalRessourceDisplayFrame = CreateFrame("Frame", UIParent);
  2. personalRessourceDisplayFrame:Hide();
  3. personalRessourceDisplayFrame.attachedVisibleFrames = {};
  4. personalRessourceDisplayFrame = personalRessourceDisplayFrame;
  5. personalRessourceDisplayFrame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  6. personalRessourceDisplayFrame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
  7.  
  8. local function MoveStuff()
  9.     local frame = C_NamePlate.GetNamePlateForUnit("player");
  10.     if InCombatLockdown() then return false; end
  11.     if (frame) then
  12.         if (Plater and frame.unitFrame.PlaterOnScreen) then
  13.             local healthButton = CreateFrame("BUTTON","healthButton", YOUR_FRAME, "SecureActionButtonTemplate");
  14.             ignoreFramePositionManager = true
  15.             healthButton:SetPoint("CENTER", C_NamePlate.GetNamePlateForUnit("player"), "CENTER", 0, 0)
  16.             healthButton:SetSize(130, 40)
  17.             healthButton:RegisterForClicks('AnyUp')
  18.             healthButton:SetAttribute('unit', 'player')
  19.             healthButton:SetAttribute('*type1', 'target')
  20.             healthButton:SetAttribute('*type2', 'togglemenu')
  21.             healthButton:GetAttribute("parent-unit")
  22.         end
  23.     end
  24. end
  25. personalRessourceDisplayFrame.eventHandler = function(self, event, nameplate)
  26.     StartProfileSystem("prd");
  27.     if (event == "NAME_PLATE_UNIT_ADDED") then
  28.         if (UnitIsUnit(nameplate, "player")) then
  29.             local frame = C_NamePlate.GetNamePlateForUnit("player");
  30.             if (frame) then
  31.                 if (Plater and frame.unitFrame.PlaterOnScreen) then
  32.                     healthButton:Attach(frame, frame.unitFrame.healthBar, frame.unitFrame.powerBar);
  33.                 else
  34.                     heatlhButton:ClearAllPoints();
  35.                     healthButton:Detach();
  36.                     healthButton:Hide();
  37.                 end
  38.             end
  39.            
  40.         elseif (event == "NAME_PLATE_UNIT_REMOVED") then
  41.             if (UnitIsUnit(nameplate, "player")) then
  42.                 healthButton:ClearAllPoints();
  43.                 healthButton:Detach();
  44.                 healthButton:Hide();
  45.             end
  46.         end
  47.     end
  48.     StopProfileSystem("prd");
  49.    
  50.    
  51. end
  52.  
  53. local f = CreateFrame('Frame')
  54. f:RegisterEvent('UPDATE_MOUSEOVER_UNIT')
  55. f:SetScript('OnEvent', MoveStuff)

The reason I set the unit attribute specifically to "Player" is because I am also tracking my target, pet and targettarget on the PRD but writing those in separate addons with nigh identical code save for X and Y values and the Unit attribute on healthButton.

Last edited by Pubert : 10-30-22 at 12:01 PM. Reason: Follow-up
  Reply With Quote
11-09-22, 06:32 AM   #4
Pubert
A Defias Bandit
Join Date: Oct 2022
Posts: 3
Double post, ceased to work. Was janky anyway
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » AddOn Help/Support » Making interactive Personal Resource Display

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off