Thread Tools Display Modes
08-09-16, 08:41 AM   #1
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Nameplate Unit Classification

So I'm trying to add Unit Classifications to my nameplates. This is just a very very alpha version, simply to test the functionality of detecting the UnitClassification.

In the function that creates the frame for frame.healthBar, I have a few additional lines of code to create artwork on the sides and around the border of the nameplates, aswell as change the statusbar texture of them.
All of this functions fine, however, when I add a function to determine the UnitClassification, it always returns "normal" no matter what.

Here is the code I am using:
Lua Code:
  1. local u = namePlateUnitToken
  2.   local class = UnitClassification(u)
  3.   if class == "worldboss" then
  4.     print("Boss")
  5.   elseif class == "elite" then
  6.     print("Elite")
  7.   elseif class == "rare" or class == "rareelite" then
  8.     print("Rare")
  9.   elseif class == "normal" then
  10.     print("Normal")
  11.   elseif class == "minus" then
  12.         print("Minion")
  13.   elseif class == "" then
  14.     print("Undefined")
  15.   else
  16.     print("Detection Failed")
  17.   end

Since I am not using UnitClassification(unit) I can be sure it's not trying to use myself as the 'unit' in question (though that would be unlikely anyways)
Also, I have fallbacks incase UnitClassification(u) returns a nil/error or just doesn't give any return.

I can be certain, from the way the if function is fired, that UnitClassification(u) IS returning the value "normal", and since the variable u is set to namePlateUnitToken, I fail to see why this function does not behave properly.

My issue is, it ALWAYS returns 'normal', whether the unit is normal, boss, elite or rare.


The function this code is inside is hooked into the "DefaultCompactNamePlateFrameSetupInternal" function, so it is fired anytime a nameplate is created, removed or changed.
Code:
hooksecurefunc("DefaultCompactNamePlateFrameSetupInternal", SetupNamePlate)
  Reply With Quote
08-09-16, 09:19 AM   #2
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Where is "namePlateUnitToken" coming from? The DefaultCompactNamePlateFrameSetupInternal function doesn't receive any such value, so neither will anything you secure-hook it with.

However, the whole point (well, one of the points) of the nameplate changes in 7.0 is that you don't need to hook default UI functions to modify nameplates. There are events that fire specifically to inform you about this stuff. Use them. The NAME_PLATE_UNIT_ADDED and NAME_PLATE_UNIT_REMOVED events do include a unit token, and the default UI code does name it "namePlateUnitToken", so maybe you're already using events, but since you didn't actually show your code, I can only make assumptions based on the snippet you provided.

Nobody here is psychic. We can't read your mind through the ether, or see your screen in a scrying bowl, or telepathically commune with your hard drive. Please just show your code -- all of your code -- when you ask for help with code.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-09-16, 10:12 AM   #3
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
Where is "namePlateUnitToken" coming from? The DefaultCompactNamePlateFrameSetupInternal function doesn't receive any such value, so neither will anything you secure-hook it with.

However, the whole point (well, one of the points) of the nameplate changes in 7.0 is that you don't need to hook default UI functions to modify nameplates. There are events that fire specifically to inform you about this stuff. Use them. The NAME_PLATE_UNIT_ADDED and NAME_PLATE_UNIT_REMOVED events do include a unit token, and the default UI code does name it "namePlateUnitToken", so maybe you're already using events, but since you didn't actually show your code, I can only make assumptions based on the snippet you provided.

Nobody here is psychic. We can't read your mind through the ether, or see your screen in a scrying bowl, or telepathically commune with your hard drive. Please just show your code -- all of your code -- when you ask for help with code.
Forgive me
Lua Code:
  1. -- rNamePlates: core
  2. -- Galaxy/zork, 2016
  3.  
  4. -----------------------------
  5. -- Variables
  6. -----------------------------
  7.  
  8. local A, L = ...
  9.  
  10. -----------------------------
  11. -- SetCVar
  12. -----------------------------
  13.  
  14. SetCVar("namePlateMinScale", 1)
  15. SetCVar("namePlateMaxScale", 1)
  16.  
  17. -----------------------------
  18. -- Options
  19. -----------------------------
  20.  
  21. local mediapath = "Interface\\AddOns\\Roth_UI\\embeds\\rNamePlates\\media\\"
  22.  
  23. local groups = {
  24.   "Friendly",
  25.   "Enemy",
  26. }
  27.  
  28. local options = {
  29.   useClassColors = true,
  30.   displayNameWhenSelected = true,
  31.   displayNameByPlayerNameRules = true,
  32.   playLoseAggroHighlight = false,
  33.   displayAggroHighlight = true,
  34.   displaySelectionHighlight = true,
  35.   considerSelectionInCombatAsHostile = true,
  36.   colorNameWithExtendedColors = true,
  37.   colorHealthWithExtendedColors = true,
  38.   selectedBorderColor = CreateColor(0, 0, 0, 1),
  39.   tankBorderColor = false,
  40.   defaultBorderColor = CreateColor(0, 0, 0, 1),
  41.   showClassificationIndicator = true,
  42. }
  43.  
  44. for i, group  in next, groups do
  45.   for key, value in next, options do
  46.     _G["DefaultCompactNamePlate"..group.."FrameOptions"][key] = value
  47.   end
  48. end
  49.  
  50.  
  51. -----------------------------
  52. -- Functions
  53. -----------------------------
  54.  
  55. --SetupNamePlate
  56. local function SetupNamePlate(frame, setupOptions, frameOptions, ...)
  57.   --Health Bar
  58.   frame.healthBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  59.   frame.healthBar:SetSize(256,32)
  60.   frame.healthBar:SetScale(0.35)
  61.  
  62.   --Left Edge artwork
  63.   local le = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  64.   le:SetTexture(mediapath.."edge_left")
  65.   le:SetSize(64,64)
  66.   le:SetPoint("RIGHT",frame.healthBar,"LEFT",0,0)
  67.  
  68.   --Right Edge artwork
  69.   local re = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  70.   re:SetTexture(mediapath.."edge_right")
  71.   re:SetSize(64,64)
  72.   re:SetPoint("LEFT",frame.healthBar,"RIGHT",0,0)
  73.  
  74.   --Healthbar Background
  75.   local bg = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  76.   bg:SetTexture(mediapath.."statusbar_bg")
  77.   bg:SetAllPoints()
  78.  
  79.   --Name shadow
  80.   local shadow = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  81.   shadow:SetTexture("Interface\\Common\\NameShadow")
  82.   shadow:SetPoint("BOTTOM",frame.healthBar,"TOP",0,-20)
  83.   shadow:SetSize(256,32)
  84.   shadow:SetTexCoord(1,1,1,0,0,1,0,0)
  85.   shadow:SetAlpha(0.5)
  86.  
  87.   --Highlight Frame
  88.   local hlf = CreateFrame("Frame",nil,frame.healthBar)
  89.   hlf:SetAllPoints()
  90.   frame.healthBar.hlf = hlf
  91.  
  92.   --Highlight
  93.   local hl = hlf:CreateTexture(nil,"BACKGROUND",nil,-8)
  94.   hl:SetTexture(mediapath.."statusbar_highlight")
  95.   hl:SetAllPoints()
  96.  
  97.   -- Elite/Boss Check
  98.   local u = namePlateUnitToken
  99.   local class = UnitClassification(u)
  100.   if class == "worldboss" then
  101.     print("Boss")
  102.   elseif class == "elite" then
  103.     print("Elite")
  104.   elseif class == "rare" or class == "rareelite" then
  105.     print("Rare")
  106.   elseif class == "normal" or "minus" then
  107.     print("Normal")
  108.   elseif class == "" then
  109.     print("Undefined")
  110.   else
  111.     print("Detection Failed")
  112.   end
  113.  
  114.   --Cast Bar
  115.   frame.castBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  116.   if GetCVar("NamePlateVerticalScale") == "1" then
  117.     frame.castBar:SetHeight(11)
  118.     frame.castBar.Icon:SetTexCoord(0.1,0.9,0.1,0.9)
  119.     frame.castBar.Icon:SetSize(17,17)
  120.     frame.castBar.Icon:ClearAllPoints()
  121.     frame.castBar.Icon:SetPoint("BOTTOMRIGHT",frame.castBar,"BOTTOMLEFT",-2,0)
  122.   end
  123. end
  124. hooksecurefunc("DefaultCompactNamePlateFrameSetupInternal", SetupNamePlate)
  125.  
  126. local function IsTank()
  127.   local assignedRole = UnitGroupRolesAssigned("player")
  128.   if assignedRole == "TANK" then return true end
  129.   local role = GetSpecializationRole(GetSpecialization())
  130.   if role == "TANK" then return true end
  131.   return false
  132. end
  133.  
  134.  
  135. --UpdateHealthBorder
  136. local function UpdateHealthBorder(frame)
  137.   if frame.displayedUnit:match("(nameplate)%d?$") ~= "nameplate" then return end
  138.   if not IsTank() then return end
  139.   local status = UnitThreatSituation("player", frame.displayedUnit)
  140.   if status and status >= 3 then
  141.     frame.healthBar.border:SetVertexColor(0, 1, 0, 0.8)
  142.   end
  143. end
  144. hooksecurefunc("CompactUnitFrame_UpdateHealthBorder", UpdateHealthBorder)


The hooksecurefunc stuff was done by zork, he updated the addon so that it would have access to the new nameplates, but handed it over to me to do all of the visual work on it, such as re-skinning, adding borders, changing text, etc.
I assumed since that was the func that he used to grab the creation of the nameplates, that it included the namePlateUnitToken variable in it.
I will do my best to re-write this using events instead of hooks when servers come back up. :s
  Reply With Quote
08-09-16, 02:11 PM   #4
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Well, the problem remains:

Code:
98.  local u = namePlateUnitToken
There is no such variable as "namePlateUnitToken" defined in this scope.

If you can add a "print(namePlateUnitToken)" there and get anything other than "nil", then something is leaking a variable with that name into the global namespace. Make sure it's not your addon doing the leaking.

Beyond that, you're making the same mistake here that you're making in our other thread about the oUF layout. You're executing code one time when a frame is created, and expecting it to magically run again and update stuff based on things that change later. That's not how it works.

In this case, you probably need to run that classification-related code in response to the NAME_PLATE_UNIT_ADDED event.

Code:
local f = CreateFrame("Frame")
f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
f:SetScript("OnEvent", function(f, event, ...)
	if event == "NAME_PLATE_UNIT_ADDED" then
		local unit = ...
		local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
		if not nameplate then return end

		-- Do stuff here that needs to happen every
		-- time a nameplate's unit changes.
	end
end)
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-09-16, 04:30 PM   #5
sezz
A Chromatic Dragonspawn
AddOn Author - Click to view addons
Join Date: Apr 2008
Posts: 158
You could also hook CompactUnitFrame_UpdateClassificationIndicator and set showClassificationIndicator to false on nameplates to hide the default icon.

Originally Posted by Phanx View Post
Beyond that, you're making the same mistake here that you're making in our other thread about the oUF layout. You're executing code one time when a frame is created, and expecting it to magically run again and update stuff based on things that change later.
DefaultCompactNamePlateFrameSetupInternal is executed on NAME_PLATE_UNIT_ADDED.
  Reply With Quote
08-09-16, 05:48 PM   #6
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Yea, the code does fire everytime a nameplate is added, so I got that bar right. I don't know where I'm getting a variable leak from but I will double check my other embeds.

Thanks for that hook sazz, THAT I know how to do. XD
  Reply With Quote
08-09-16, 07:35 PM   #7
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
In this case, you probably need to run that classification-related code in response to the NAME_PLATE_UNIT_ADDED event.

Code:
local f = CreateFrame("Frame")
f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
f:SetScript("OnEvent", function(f, event, ...)
	if event == "NAME_PLATE_UNIT_ADDED" then
		local unit = ...
		local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
		if not nameplate then return end

		-- Do stuff here that needs to happen every
		-- time a nameplate's unit changes.
	end
end)
I love you.
I'm learning much from you thanks.

I found out why my function was always returning 'normal', apparently UnitClassification(unit) always returns 'normal' on bosses now? So you have to check for UnitLevel(unit) == -1 instead.
I was failing to properly test the detection against elites and rares aswell, woops.

So my way of doing it actually did work.
But keeping it seperate in it's own function is loads better.
  Reply With Quote
08-09-16, 08:53 PM   #8
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Sooo.. I tried to change name text and failed epically! Woo!

Lua Code:
  1. local f = CreateFrame("Frame")
  2. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  3. f:SetScript("OnEvent", function(f, event, ...)
  4.     if event == "NAME_PLATE_UNIT_ADDED" then
  5.         local unit = ...
  6.         local name = UnitName(unit)
  7.         local level = UnitLevel(unit)
  8.         local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
  9.         if not nameplate then return end
  10.        
  11.         local u = namePlateUnitToken
  12.         local class = UnitClassification(u)
  13.        
  14.         local function ChangeName(frame, ...)
  15.             local string
  16.             string = name.."("..level..")"
  17.             frame.name:SetText(string)
  18.         end
  19.         hooksecurefunc("CompactUnitFrame_UpdateName", ChangeName)
  20.     end
  21. end)

I would LIKE it to simply replace the name on the nameplate with the units name plus their level in () (for now, at least)

It does this, but whenever a new frame is added, all the existing frames take the same name as the new one.
Onbviously this is due to the NAME_PLATE_UNIT_ADDED event but I'm unsure how else to go about it?
I tried NAME_PLATE_UNIT_CREATED and had the same effect.


Full addon code:
Lua Code:
  1. -- rNamePlates: core
  2. -- Galaxy/zork, 2016
  3.  
  4. -----------------------------
  5. -- Variables
  6. -----------------------------
  7.  
  8. local A, L = ...
  9.  
  10. -----------------------------
  11. -- SetCVar
  12. -----------------------------
  13.  
  14. SetCVar("namePlateMinScale", 1)
  15. SetCVar("namePlateMaxScale", 1)
  16.  
  17. -----------------------------
  18. -- Options
  19. -----------------------------
  20.  
  21. local mediapath = "Interface\\AddOns\\Roth_UI\\embeds\\rNamePlates\\media\\"
  22.  
  23. local groups = {
  24.   "Friendly",
  25.   "Enemy",
  26. }
  27.  
  28. local options = {
  29.   useClassColors = true,
  30.   displayNameWhenSelected = true,
  31.   displayNameByPlayerNameRules = true,
  32.   playLoseAggroHighlight = false,
  33.   displayAggroHighlight = true,
  34.   displaySelectionHighlight = true,
  35.   considerSelectionInCombatAsHostile = true,
  36.   colorNameWithExtendedColors = true,
  37.   colorHealthWithExtendedColors = true,
  38.   selectedBorderColor = CreateColor(0, 0, 0, 1),
  39.   tankBorderColor = false,
  40.   defaultBorderColor = CreateColor(0, 0, 0, 1),
  41.   showClassificationIndicator = true,
  42. }
  43.  
  44. for i, group  in next, groups do
  45.   for key, value in next, options do
  46.     _G["DefaultCompactNamePlate"..group.."FrameOptions"][key] = value
  47.   end
  48. end
  49.  
  50.  
  51. -----------------------------
  52. -- Functions
  53. -----------------------------
  54.  
  55. --SetupNamePlate
  56. local function SetupNamePlate(frame, setupOptions, frameOptions, ...)
  57.   --Health Bar
  58.   frame.healthBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  59.   frame.healthBar:SetSize(256,32)
  60.   frame.healthBar:SetScale(0.35)
  61.   frame.classificationIndicator:SetSize(0,0)
  62.  
  63.  
  64.   --Left Edge artwork
  65.   local le = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  66.   le:SetTexture(mediapath.."edge_left")
  67.   le:SetSize(64,64)
  68.   le:SetPoint("RIGHT",frame.healthBar,"LEFT",0,0)
  69.  
  70.  
  71.   --Right Edge artwork
  72.   local re = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  73.   re:SetTexture(mediapath.."edge_right")
  74.   re:SetSize(64,64)
  75.   re:SetPoint("LEFT",frame.healthBar,"RIGHT",0,0)
  76.  
  77.  
  78.  
  79.   --Healthbar Background
  80.   local bg = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  81.   bg:SetTexture(mediapath.."statusbar_bg")
  82.   bg:SetAllPoints()
  83.  
  84.   --Name shadow
  85.   local shadow = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  86.   shadow:SetTexture("Interface\\Common\\NameShadow")
  87.   shadow:SetPoint("BOTTOM",frame.healthBar,"TOP",0,-20)
  88.   shadow:SetSize(256,32)
  89.   shadow:SetTexCoord(1,1,1,0,0,1,0,0)
  90.   shadow:SetAlpha(0.5)
  91.  
  92.   --Highlight Frame
  93.   local hlf = CreateFrame("Frame",nil,frame.healthBar)
  94.   hlf:SetAllPoints()
  95.   frame.healthBar.hlf = hlf
  96.  
  97.   --Highlight
  98.   local hl = hlf:CreateTexture(nil,"BACKGROUND",nil,-8)
  99.   hl:SetTexture(mediapath.."statusbar_highlight")
  100.   hl:SetPoint("TOP",0,0)
  101.   hl:SetPoint("LEFT",0,0)
  102.   hl:SetPoint("RIGHT",0,0)
  103.   hl:SetPoint("BOTTOM",0,0)
  104.  
  105.   --Cast Bar
  106.   frame.castBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  107.   if GetCVar("NamePlateVerticalScale") == "1" then
  108.     frame.castBar:SetHeight(11)
  109.     frame.castBar.Icon:SetTexCoord(0.1,0.9,0.1,0.9)
  110.     frame.castBar.Icon:SetSize(17,17)
  111.     frame.castBar.Icon:ClearAllPoints()
  112.     frame.castBar.Icon:SetPoint("BOTTOMRIGHT",frame.castBar,"BOTTOMLEFT",-2,0)
  113.   end
  114. end
  115. hooksecurefunc("DefaultCompactNamePlateFrameSetupInternal", SetupNamePlate)
  116.  
  117. --Elite/Boss tags
  118. local f = CreateFrame("Frame")
  119. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  120. f:SetScript("OnEvent", function(f, event, ...)
  121.     if event == "NAME_PLATE_UNIT_ADDED" then
  122.         local unit = ...
  123.         local name = UnitName(unit)
  124.         local level = UnitLevel(unit)
  125.         local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
  126.         if not nameplate then return end
  127.        
  128.         local u = namePlateUnitToken
  129.         local class = UnitClassification(u)
  130.        
  131.         local function ChangeName(frame, ...)
  132.             local string
  133.             string = name.."("..level..")"
  134.             frame.name:SetText(string)
  135.         end
  136.         hooksecurefunc("CompactUnitFrame_UpdateName", ChangeName)
  137.     end
  138. end)
  139.  
  140.  
  141.  
  142. local function IsTank()
  143.   local assignedRole = UnitGroupRolesAssigned("player")
  144.   if assignedRole == "TANK" then return true end
  145.   local role = GetSpecializationRole(GetSpecialization())
  146.   if role == "TANK" then return true end
  147.   return false
  148. end
  149.  
  150.  
  151. --UpdateHealthBorder
  152. local function UpdateHealthBorder(frame)
  153.   if frame.displayedUnit:match("(nameplate)%d?$") ~= "nameplate" then return end
  154.   if not IsTank() then return end
  155.   local status = UnitThreatSituation("player", frame.displayedUnit)
  156.   if status and status >= 3 then
  157.     frame.healthBar.border:SetVertexColor(0, 1, 0, 0.8)
  158.   end
  159. end
  160. hooksecurefunc("CompactUnitFrame_UpdateHealthBorder", UpdateHealthBorder)
  Reply With Quote
08-10-16, 12:54 PM   #9
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by sezz View Post
DefaultCompactNamePlateFrameSetupInternal is executed on NAME_PLATE_UNIT_ADDED.
In that case, any function you run as a hook for this should not re-create existing objects. Otherwise you'll end up with each nameplate frame having 10000 copies of the same texture object on top of each other.

You should also not install hooks in such a function, of you'll end up with 10000 copies of the same function running every time the original hooked function is called, which will eventually have a very negative effect on your framerate.

I still stand by my statement that one-time setup should be run in an event handler for NAME_PLATE_CREATED.

Anything that runs for NAME_PLATE_UNIT_ADDED or NAME_PLATE_UNIT_REMOVED should not create any new objects or do one-time setup. Use those events (or hooks on functions that run in response to those events) only to show, hide, reposition, or otherwise change things that depend on the properties of the nameplate's current unit (like its classification).
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-10-16, 01:57 PM   #10
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,326
What I would do, instead of hooking DefaultCompactNamePlateFrameSetupInternal(), is to hook the NamePlateDriver's ApplyFrameOptions() method.
Code:
hooksecurefunc(NamePlateDriverFrame,"ApplyFrameOptions",function(nameplate,unit)
-- Code here
end);
In this, you can access the UnitFrame through nameplate.UnitFrame.
__________________
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)
  Reply With Quote
08-10-16, 09:03 PM   #11
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
In that case, any function you run as a hook for this should not re-create existing objects. Otherwise you'll end up with each nameplate frame having 10000 copies of the same texture object on top of each other.

You should also not install hooks in such a function, of you'll end up with 10000 copies of the same function running every time the original hooked function is called, which will eventually have a very negative effect on your framerate.

I still stand by my statement that one-time setup should be run in an event handler for NAME_PLATE_CREATED.

Anything that runs for NAME_PLATE_UNIT_ADDED or NAME_PLATE_UNIT_REMOVED should not create any new objects or do one-time setup. Use those events (or hooks on functions that run in response to those events) only to show, hide, reposition, or otherwise change things that depend on the properties of the nameplate's current unit (like its classification).
Done. Here's the code, I may of still done something wrong, but it does appear to be working.
Lua Code:
  1. --SetupNamePlate
  2. local h = CreateFrame("Frame")
  3. h:RegisterEvent("NAME_PLATE_CREATED")
  4. h:SetScript("OnEvent", function(h, event, ...)
  5.     if event == "NAME_PLATE_CREATED" then
  6.         hooksecurefunc("DefaultCompactNamePlateFrameSetupInternal", function(frame, setupOptions, frameOptions, ...)
  7.             --Health Bar
  8.             frame.healthBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  9.             frame.healthBar:SetSize(256,32)
  10.             frame.healthBar:SetScale(0.35)
  11.             frame.classificationIndicator:SetSize(0,0)
  12.  
  13.             --Left Edge artwork
  14.             local le = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  15.             le:SetTexture(mediapath.."edge_left")
  16.             le:SetSize(64,64)
  17.             le:SetPoint("RIGHT",frame.healthBar,"LEFT",0,0)
  18.  
  19.             --Right Edge artwork
  20.             local re = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  21.             re:SetTexture(mediapath.."edge_right")
  22.             re:SetSize(64,64)
  23.             re:SetPoint("LEFT",frame.healthBar,"RIGHT",0,0)
  24.            
  25.             --Healthbar Background
  26.             local bg = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  27.             bg:SetTexture(mediapath.."statusbar_bg")
  28.             bg:SetAllPoints()
  29.  
  30.             --Name shadow
  31.             local shadow = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  32.             shadow:SetTexture("Interface\\Common\\NameShadow")
  33.             shadow:SetPoint("BOTTOM",frame.healthBar,"TOP",0,-20)
  34.             shadow:SetSize(256,32)
  35.             shadow:SetTexCoord(1,1,1,0,0,1,0,0)
  36.             shadow:SetAlpha(0.5)
  37.  
  38.             --Highlight Frame
  39.             local hlf = CreateFrame("Frame",nil,frame.healthBar)
  40.             hlf:SetAllPoints()
  41.             frame.healthBar.hlf = hlf
  42.  
  43.             --Highlight
  44.             local hl = hlf:CreateTexture(nil,"BACKGROUND",nil,-8)
  45.             hl:SetTexture(mediapath.."statusbar_highlight")
  46.             hl:SetPoint("TOP",0,0)
  47.             hl:SetPoint("LEFT",0,0)
  48.             hl:SetPoint("RIGHT",0,0)
  49.             hl:SetPoint("BOTTOM",0,0)
  50.  
  51.             --Cast Bar
  52.             frame.castBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  53.             if GetCVar("NamePlateVerticalScale") == "1" then
  54.                 frame.castBar:SetHeight(11)
  55.                 frame.castBar.Icon:SetTexCoord(0.1,0.9,0.1,0.9)
  56.                 frame.castBar.Icon:SetSize(17,17)
  57.                 frame.castBar.Icon:ClearAllPoints()
  58.                 frame.castBar.Icon:SetPoint("BOTTOMRIGHT",frame.castBar,"BOTTOMLEFT",-2,0)
  59.             end
  60.         end)
  61.     end
  62. end)
PS. Look at that blocking! it finally copy/pasted correctly!

On a side note, inside a NAME_PLATE_UNIT_ADDED handler i am attempting to change the name shown on the nameplate with
Code:
frame.name:SetText()
however it causes the names on ALL shown nameplates to change, basically making all nameplates display the same name whenever a new units plate is added.
I was thinking of various ideas to try and counter this, in BASH it would be easy enough to handle it with an if/then/else function, would that still be viable in Lua? And if so, how do I compare UnitName(unit) to frame.name, since frame.name is a table? Is it even possible? Would i have to use variables to compare the two? My idea was something like...
Code:
local name = UnitName(unit)
local level = UnitLevel(unit)
local string = name.."("..level..")"

hooksecurefunc(
if frame.name == name then
    frame.name:SetText(string)
end
but obviously frame.name returns the name of the frame, not the unit, but I can't think of any other way to only update the frame belonging to a specific unit.

Last edited by Joker119 : 08-10-16 at 09:29 PM.
  Reply With Quote
08-10-16, 10:26 PM   #12
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
I was able to solve the name problem myself:

Lua Code:
  1. local EliteTag = "+"
  2. local RareTag = "*"
  3. local BossTag = "*"
  4.  
  5. --Name
  6. local f = CreateFrame("Frame")
  7. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  8. f:SetScript("OnEvent", function(f, event, ...)
  9.     if event == "NAME_PLATE_UNIT_ADDED" then
  10.         local unit = ...
  11.         local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
  12.         if not nameplate then return end
  13.        
  14.         hooksecurefunc("CompactUnitFrame_UpdateName", function (frame)
  15.         --Set the tag based on UnitClassification, can return "worldboss", "rare", "rareelite", "elite", "normal", "minus"
  16.         local tag
  17.             if UnitClassification(frame.unit) == "worldboss" or UnitLevel(frame.unit) == -1 then
  18.                 tag = BossTag
  19.             elseif UnitClassification(frame.unit) == "rare" or UnitClassification(frame.unit) =="rareelite" then
  20.                 tag = RareTag
  21.             elseif UnitClassification(frame.unit) == "elite" then
  22.                 tag = EliteTag
  23.             else
  24.                 tag = ""
  25.             end
  26.             --Set the nameplate name to include tag(if any), name and level
  27.             frame.name:SetText(tag..UnitName(frame.unit).." ("..UnitLevel(frame.unit)..")"..tag)
  28.         end)
  29.     end
  30. end)

Last edited by Joker119 : 08-10-16 at 10:28 PM.
  Reply With Quote
08-10-16, 11:33 PM   #13
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
New problem

Running into a new issue..
http://imgur.com/a/TeFTW

After more and more nameplates are created, it does this.

Lua Code:
  1. -- rNamePlates: core
  2. -- Galaxy/zork, 2016
  3.  
  4. -----------------------------
  5. -- Variables
  6. -----------------------------
  7.  
  8. local A, L = ...
  9. local mediapath = "Interface\\AddOns\\Roth_UI\\embeds\\rNamePlates\\media\\"
  10. local EliteTag = "+"
  11. local RareTag = "^"
  12. local BossTag = "*"
  13.  
  14. -----------------------------
  15. -- SetCVar
  16. -----------------------------
  17.  
  18. SetCVar("namePlateMinScale", 1)
  19. SetCVar("namePlateMaxScale", 1)
  20.  
  21. -----------------------------
  22. -- Options
  23. -----------------------------
  24.  
  25. local groups = {
  26.   "Friendly",
  27.   "Enemy",
  28. }
  29.  
  30. local options = {
  31.   useClassColors = true,
  32.   displayNameWhenSelected = true,
  33.   displayNameByPlayerNameRules = true,
  34.   playLoseAggroHighlight = false,
  35.   displayAggroHighlight = true,
  36.   displaySelectionHighlight = true,
  37.   considerSelectionInCombatAsHostile = true,
  38.   colorNameWithExtendedColors = true,
  39.   colorHealthWithExtendedColors = true,
  40.   selectedBorderColor = CreateColor(0, 0, 0, 1),
  41.   tankBorderColor = false,
  42.   defaultBorderColor = CreateColor(0, 0, 0, 1),
  43.   showClassificationIndicator = true,
  44. }
  45.  
  46. for i, group  in next, groups do
  47.   for key, value in next, options do
  48.     _G["DefaultCompactNamePlate"..group.."FrameOptions"][key] = value
  49.   end
  50. end
  51.  
  52.  
  53. -----------------------------
  54. -- Functions
  55. -----------------------------
  56.  
  57. --SetupNamePlate
  58. local h = CreateFrame("Frame")
  59. h:RegisterEvent("NAME_PLATE_CREATED")
  60. h:SetScript("OnEvent", function(h, event, ...)
  61.     if event == "NAME_PLATE_CREATED" then
  62.         hooksecurefunc("DefaultCompactNamePlateFrameSetupInternal", function(frame, setupOptions, frameOptions, ...)
  63.             --Health Bar
  64.             frame.healthBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  65.             frame.healthBar:SetSize(256,32)
  66.             frame.healthBar:SetScale(0.35)
  67.  
  68.             --Left Edge artwork
  69.             local le = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  70.             le:SetTexture(mediapath.."edge_left")
  71.             le:SetSize(64,64)
  72.             le:SetPoint("RIGHT",frame.healthBar,"LEFT",0,0)
  73.  
  74.             --Right Edge artwork
  75.             local re = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  76.             re:SetTexture(mediapath.."edge_right")
  77.             re:SetSize(64,64)
  78.             re:SetPoint("LEFT",frame.healthBar,"RIGHT",0,0)
  79.            
  80.             --Healthbar Background
  81.             local bg = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  82.             bg:SetTexture(mediapath.."statusbar_bg")
  83.             bg:SetAllPoints()
  84.  
  85.             --Name shadow
  86.             local shadow = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  87.             shadow:SetTexture("Interface\\Common\\NameShadow")
  88.             shadow:SetPoint("BOTTOM",frame.healthBar,"TOP",0,-20)
  89.             shadow:SetSize(256,32)
  90.             shadow:SetTexCoord(1,1,1,0,0,1,0,0)
  91.             shadow:SetAlpha(0.5)
  92.  
  93.             --Highlight Frame
  94.             local hlf = CreateFrame("Frame",nil,frame.healthBar)
  95.             hlf:SetAllPoints(frame.healthBar)
  96.             frame.healthBar.hlf = hlf
  97.  
  98.             --Highlight
  99.             local hl = hlf:CreateTexture(nil,"BACKGROUND",nil,-8)
  100.             hl:SetTexture(mediapath.."statusbar_highlight")
  101.             hl:SetPoint("TOP",50,0)
  102.             hl:SetPoint("LEFT",-30,0)
  103.             hl:SetPoint("RIGHT",30,0)
  104.             hl:SetPoint("BOTTOM",40,0)
  105.  
  106.             --Cast Bar
  107.             frame.castBar:SetStatusBarTexture(mediapath.."statusbar_fill")
  108.             if GetCVar("NamePlateVerticalScale") == "1" then
  109.                 frame.castBar:SetHeight(11)
  110.                 frame.castBar.Icon:SetTexCoord(0.1,0.9,0.1,0.9)
  111.                 frame.castBar.Icon:SetSize(17,17)
  112.                 frame.castBar.Icon:ClearAllPoints()
  113.                 frame.castBar.Icon:SetPoint("BOTTOMRIGHT",frame.castBar,"BOTTOMLEFT",-2,0)
  114.             end
  115.         end)
  116.     end
  117. end)
  118.  
  119.  
  120. --Name
  121. local f = CreateFrame("Frame")
  122. f:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  123. f:SetScript("OnEvent", function(f, event, ...)
  124.     if event == "NAME_PLATE_UNIT_ADDED" then
  125.         local unit = ...
  126.         local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
  127.         if not nameplate then return end
  128.        
  129.         hooksecurefunc("CompactUnitFrame_UpdateName", function (frame)
  130.         --Set the tag based on UnitClassification, can return "worldboss", "rare", "rareelite", "elite", "normal", "minus"
  131.         local tag
  132.         local level = UnitLevel(frame.unit)
  133.             if UnitClassification(frame.unit) == "worldboss" or UnitLevel(frame.unit) == -1 then
  134.                 tag = BossTag
  135.                 level = "??"
  136.             elseif UnitClassification(frame.unit) == "rare" or UnitClassification(frame.unit) =="rareelite" then
  137.                 tag = RareTag
  138.             elseif UnitClassification(frame.unit) == "elite" then
  139.                 tag = EliteTag
  140.             else
  141.                 tag = ""
  142.             end
  143.             --Set the nameplate name to include tag(if any), name and level
  144.             frame.name:SetText(UnitName(frame.unit).." ("..level..")"..tag)
  145.         end)
  146.     end
  147. end)
  148.  
  149.  
  150.  
  151. local function IsTank()
  152.   local assignedRole = UnitGroupRolesAssigned("player")
  153.   if assignedRole == "TANK" then return true end
  154.   local role = GetSpecializationRole(GetSpecialization())
  155.   if role == "TANK" then return true end
  156.   return false
  157. end
  158.  
  159.  
  160. --UpdateHealthBorder
  161. local function UpdateHealthBorder(frame)
  162.   if frame.displayedUnit:match("(nameplate)%d?$") ~= "nameplate" then return end
  163.   if not IsTank() then return end
  164.   local status = UnitThreatSituation("player", frame.displayedUnit)
  165.   if status and status >= 3 then
  166.     frame.healthBar.border:SetVertexColor(0, 1, 0, 0.8)
  167.   end
  168. end
  169. hooksecurefunc("CompactUnitFrame_UpdateHealthBorder", UpdateHealthBorder)

I put the function that adds the textures inside a NAME_PLATE_CREATED event handler as you suggested, but it's still doing this. (i took it back out and confirmed it does the same thing either way)

I'm unsure how I would go about changing the frame textures and such WITHOUT hooking into this function, I tried the one Phantom recommended but can't seem to get it to work,
Code:
--SetupNamePlate
local h = CreateFrame("Frame")
h:RegisterEvent("NAME_PLATE_CREATED")
h:SetScript("OnEvent", function(h, event, ...)
    if event == "NAME_PLATE_CREATED" then
        hooksecurefunc(NamePlateDriverFrame,"ApplyFrameOptions", function(nameplate,unit)
            --Health Bar
            nameplate.healthBar:SetStatusBarTexture(mediapath.."statusbar_fill")
returns with an error "attempt to index value "healthBar" (a nil value)"
I also tried nameplate.UnitFrame.healthBar, nameplate.UnitFrame, nameplate.frame.healthBar, frame.unitplate.healthBar and frame.unitplate.UnitFrame.healthBar
  Reply With Quote
08-11-16, 03:14 PM   #14
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
*cough*

Originally Posted by Phanx View Post
You should also not install hooks in such a function, of you'll end up with 10000 copies of the same function running every time the original hooked function is called, which will eventually have a very negative effect on your framerate.
You're still doing this. Don't install hooks (eg. with hooksecurefunc) in a function that runs more than once. Your latest code is re-hooking DefaultCompactNamePlateFrameSetupInternal every time NAME_PLATE_CREATED fires. Move the hooksecurefunc call up into the main chunk, or into any other context that only gets executed once per session.

Functions can be hooked more than once, so every time you call hooksecurefunc, it adds the provided function to the end of the chain. It doesn't replace the function you provided the last time you called it.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-11-16, 04:49 PM   #15
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
*cough*



You're still doing this. Don't install hooks (eg. with hooksecurefunc) in a function that runs more than once. Your latest code is re-hooking DefaultCompactNamePlateFrameSetupInternal every time NAME_PLATE_CREATED fires. Move the hooksecurefunc call up into the main chunk, or into any other context that only gets executed once per session.

Functions can be hooked more than once, so every time you call hooksecurefunc, it adds the provided function to the end of the chain. It doesn't replace the function you provided the last time you called it.
Fixed the issue by doing this for all of the textures/frames ADDED to the nameplates when the event fires, the others just replace existing textures and thus don't create 10000000 overlapping textures:
Lua Code:
  1. --Left Edge artwork
  2.             if (not frame.healthBar.le) then
  3.                 frame.healthBar.le = frame.healthBar:CreateTexture(nil,"BACKGROUND",nil,-8)
  4.                 frame.healthBar.le:SetTexture(mediapath.."edge_left")
  5.                 frame.healthBar.le:SetSize(64,64)
  6.                 frame.healthBar.le:SetPoint("RIGHT",frame.healthBar,"LEFT",0,0)
  7.             end

I moved the hook to the main chunk and put the rest in a event handler and use a variable to tie the frame.healthBar provided by the hook to the functions inside the event handler for NAME_PLATE_CREATED

I was unsure how to work editing frame.healthBar without hooking that function, so that was my compromise.

Am I getting at least alittle better now?
  Reply With Quote
08-11-16, 07:30 PM   #16
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Is your GitHub repo up to date with your latest code?
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-11-16, 10:30 PM   #17
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
Is your GitHub repo up to date with your latest code?
It should be (1.7....7? Or 8) if not I will fix it tonight when I get home. At work currently.
I may have moved the hook into the main chunk after my last git commit and forgot to resync the repo
I was I'm a rush to finish putting together my motorcycle engine in time for work today. Woops. XD
  Reply With Quote
08-12-16, 10:29 AM   #18
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
GitHub is up to date, apparently I wasn't able to get the hook to pass the variable for some reason so I gave up on it, since the only thing the hook will do every time it's run is replace the statusbar texture and check if the others exist or not, it's not causing textures to be created a million times, so i left it alone.


If you know a way to get the hook to register a variable and pass it on in the main chunk, i'd much rather use the hook in the main chunk just to grab the frame.healthBar and pass it along, or if there's another hook I can use instead (I tried using NamePlateDriverFrame,"ApplyFrameOptions" but it always returns frame.healthBar is a nil value)
  Reply With Quote
08-12-16, 07:38 PM   #19
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
I don't really understand what variable you're trying to pass around.

If you're adding new objects and need to refer to them later, just store them on the frame. If you're adding multiple new objects, it may be easiest (and less prone to interference) to store them all in a table just for your addon:

Code:
nameplateFrame.RothUI = {}

local customBar = CreateFrame("StatusBar", nil, nameplateFrame)
nameplateFrame.RothUI.customBar = customBar

local customTexture = nameplateFrame:CreateTexture(nil, "OVERLAY")
nameplateFrame.RothUI.customTexture = customTexture
Then later, just refer to "nameplateFrame.RothUI.customBar" to modify the custom bar.

If you're working with the nameplate's default health bar or other objects/regions, you don't need a variable at all; just refer to it normally under "nameplateFrame.UnitFrame.healthBar".
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.
  Reply With Quote
08-14-16, 01:52 PM   #20
Joker119
A Flamescale Wyrmkin
 
Joker119's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2014
Posts: 113
Originally Posted by Phanx View Post
I don't really understand what variable you're trying to pass around.

If you're adding new objects and need to refer to them later, just store them on the frame. If you're adding multiple new objects, it may be easiest (and less prone to interference) to store them all in a table just for your addon:

Code:
nameplateFrame.RothUI = {}

local customBar = CreateFrame("StatusBar", nil, nameplateFrame)
nameplateFrame.RothUI.customBar = customBar

local customTexture = nameplateFrame:CreateTexture(nil, "OVERLAY")
nameplateFrame.RothUI.customTexture = customTexture
Then later, just refer to "nameplateFrame.RothUI.customBar" to modify the custom bar.

If you're working with the nameplate's default health bar or other objects/regions, you don't need a variable at all; just refer to it normally under "nameplateFrame.UnitFrame.healthBar".
nameplateFrame.UnitFrame.healthBar results in:
Code:
8x ...ceRoth_UI\embeds\rNamePlates\rNamePlates.lua:62: attempt to index global 'nameplateFrame' (a nil value)
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Nameplate Unit Classification


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