Quantcast Class icons on nameplates - WoWInterface
Thread Tools Display Modes
08-21-18, 07:55 AM   #1
Mitur
A Murloc Raider
Join Date: Aug 2008
Posts: 4
Class icons on nameplates

Hi guys, first timer here!
Did some search on these forums and discovered a lot of useful information, you are really helpful bunch of people!
Trying to figure out the nameplate magics.
For practice I'm attempting to show class icons over the friendly nameplates.
After the initial loading things work well, but after I move my camera and nameplates get dynamically reassigned, icons tend to appear over wrong classes.
To narrow things down I've tried to restrict the icons to WARRIOR class only.
And it obviously didn't work as planned
https://imgur.com/G3UPLIx

Lua Code:
  1. local events = {
  2.     ["npu_added"] = "NAME_PLATE_UNIT_ADDED",
  3.     ["npu_removed"] = "NAME_PLATE_UNIT_REMOVED",
  4.     -- ["bg"] = "PLAYER_ENTERING_BATTLEGROUND",
  5.     -- ["gru"] = "GROUP_ROSTER_UPDATE",
  6.     -- ["ptc"] = "PLAYER_TARGET_CHANGED",
  7.     ["load"] = "ADDON_LOADED"
  8. }
  9. local icons = {
  10.     ["DEMONHUNTER"] = 236415,
  11.     ["DRUID"] = 625999,
  12.     ["HUNTER"] = 626000,
  13.     ["MAGE"] = 626001,
  14.     ["MONK"] = 626002,
  15.     ["PALADIN"] = 626003,
  16.     ["PRIEST"] = 626004,
  17.     ["ROGUE"] = 626005,
  18.     ["SHAMAN"] = 626006,
  19.     ["WARLOCK"] = 626007,
  20.     ["WARRIOR"] = 626008,
  21.     ["DEATHKNIGHT"] = 135771
  22. }
  23.  
  24. --[[ Potentially useful checks
  25.     UnitIsFriend(unit, otherUnit) comparing relationships between units
  26.     UnitIsPlayer(unit)
  27.     UnitInPArty(unit)
  28. ]]
  29.  
  30. local guidSet = {}
  31. local idSet = {}
  32.  
  33. local sdb = CreateFrame("Frame")
  34. local _G = _G
  35. local GetNamePlateForUnit = _G.C_NamePlate.GetNamePlateForUnit --get nameplate from global UI variable _G
  36.  
  37. local function addIcon(parentFrame, icon)
  38.     local iconFrame, iconTexture
  39.     iconFrame = CreateFrame("Frame", nil, parentFrame)
  40.     iconFrame:SetFrameStrata("BACKGROUND")
  41.     iconFrame:SetWidth(32)
  42.     iconFrame:SetHeight(32)
  43.  
  44.     iconTexture = iconFrame:CreateTexture(nil, "BACKGROUND")
  45.     iconTexture:SetTexture(icons[icon])
  46.     iconTexture:SetAllPoints(iconFrame)
  47.  
  48.     iconFrame.texture = iconTexture
  49.     iconFrame:SetPoint("TOPLEFT", 10, 10)
  50.     iconFrame:Show()
  51. end
  52.  
  53. local function removeIcon(parentFrame)
  54.     parentFrame:Hide()
  55. end
  56.  
  57. local function nameplateHandler(self, event, unit)
  58.     local namePlate = GetNamePlateForUnit(unit)
  59.     local unit_frame = namePlate.UnitFrame
  60.     local unitGUID = UnitGUID(unit)
  61.     local separator = ", "
  62.     local className, classId, raceName, raceId, gender, name, realm = GetPlayerInfoByGUID(unitGUID)
  63.     if event == events["npu_added"] then
  64.         if classId == "WARRIOR" and not idSet:contains(unit) then
  65.             print(name .. separator .. classId .. separator .. unit .. " added!")
  66.             idSet[unit] = name
  67.             addIcon(namePlate, classId)
  68.         end
  69.     elseif event == events["npu_removed"] and idSet:contains(unit) then
  70.         print("Was saved as " .. idSet[id])
  71.         print("Was removed as " .. name)
  72.         idSet:remove(unit)
  73.         removeIcon(namePlate)
  74.     end
  75. end
  76.  
  77. local function showFriendlyNameplates(self, event)
  78.     if event == events["load"] then
  79.         SetCVar("nameplateShowAll", 1)
  80.         SetCVar("nameplateShowFriends", 1)
  81.     end
  82. end
  83.  
  84. -- Utility functions
  85.  
  86. function guidSet:contains(key)
  87.     return self[key] ~= nil
  88. end
  89.  
  90. function idSet:contains(key)
  91.     return self[key] ~= nil
  92. end
  93.  
  94. function idSet:remove(key)
  95.     idSet[key] = nil
  96. end
  97.  
  98. -- Registering for a table of events
  99.  
  100. for k, v in pairs(events) do
  101.     sdb:RegisterEvent(v)
  102. end
  103. sdb:SetScript("OnEvent", nameplateHandler)
  104. -- sdb:SetScript("OnEvent", showFriendlyNameplates)

I feel that the removeIcon function is faulty as I don't know how to get to the frame I've created on top of the nameplate.
Maybe I have to gather all the frames I create in a table?
Or perhaps all the stuff I do is just plain wrong and there is a simpler way to accomplish this task that I don't see as a beginner.
Thank you very much for taking your time to read this and for any hints and advice!

Last edited by Mitur : 08-22-18 at 03:10 AM.
  Reply With Quote
08-21-18, 09:38 AM   #2
Vrul
A Frostmaul Preserver
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 292
I see a few things that I think are wrong but I'm not sure since you reference things not in the posted code. Post ALL of your code.
  Reply With Quote
08-21-18, 09:59 AM   #3
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
You're running :Hide() on the nameplate itself, why?
The nameplate already disappears when unit is removed, that part is redundant, and you're not actually hiding the iconframe. Meaning, once the nameplate gets reassigned to a different unit, it is still anchored to it and will still appear, even if the character is of the wrong class.

[I wrote a thing here that I realized would not work, edited out]

Also, not related to the functionality of your code, but you are creating a new frame every time a nameplate appears. You should only create one per nameplate. Efficiency reasons.

Last edited by Ammako : 08-21-18 at 10:05 AM.
  Reply With Quote
08-21-18, 10:30 AM   #4
Mitur
A Murloc Raider
Join Date: Aug 2008
Posts: 4
Originally Posted by Vrul View Post
I see a few things that I think are wrong but I'm not sure since you reference things not in the posted code. Post ALL of your code.
I only omitted things like table creation (icons table for example), and obvious stuff like event registration etc. Main logic is contained within the code I posted.

Originally Posted by Ammako View Post
You're running :Hide() on the nameplate itself, why?
The nameplate already disappears when unit is removed, that part is redundant, and you're not actually hiding the iconframe. Meaning, once the nameplate gets reassigned to a different unit, it is still anchored to it and will still appear, even if the character is of the wrong class.

[I wrote a thing here that I realized would not work, edited out]

Also, not related to the functionality of your code, but you are creating a new frame every time a nameplate appears. You should only create one per nameplate. Efficiency reasons.
Thanks for explaining the Hide() part, I just don't know how to get the reference to the Frame that I created to hide it properly.
And you're right about the efficiency part for sure.
I should somehow check if the nameplate already contains my Frame. If only I knew how to manage the nameplates properly
  Reply With Quote
08-21-18, 10:47 AM   #5
Vrul
A Frostmaul Preserver
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 292
Originally Posted by Mitur View Post
I only omitted things like table creation (icons table for example), and obvious stuff like event registration etc. Main logic is contained within the code I posted.
Not really.
Code:
    if event == events["npu_added"] and not idSet:contains(unit) then
Without knowing what is going on in idSet:contains(unit) I have no idea if that logic branch is correct. I believe that part to be unnecessary but I have no way to be sure. I also don't know if you have the correct events in your table (or why you abstract them that way instead of just putting the event name there).

Code:
    elseif event == events["npu_removed"] and idSet:contains(id) then
        idSet:remove(id)
Same issues as above and also what does idSet:remove(id) do? What does "id" even reference and where is it set? Is it supposed to be "unit" instead or is there something going on in the code not posted.

In short post ALL of your code if you want help.
  Reply With Quote
08-21-18, 11:15 AM   #6
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
Originally Posted by Mitur View Post
I only omitted things like table creation (icons table for example), and obvious stuff like event registration etc. Main logic is contained within the code I posted.
You should still post your whole code, rule of thumb. You may know what seems important or not, but to the people reading, we don't know what else is there so there's no way for anyone to know.

Originally Posted by Mitur View Post
Thanks for explaining the Hide() part, I just don't know how to get the reference to the Frame that I created to hide it properly.
And you're right about the efficiency part for sure.
I should somehow check if the nameplate already contains my Frame. If only I knew how to manage the nameplates properly
iconFrame is local to your addIcon function, you'll have a hard time accessing it from anywhere else.

You could declare iconFrame inside nameplateHandler, and pass it as an argument to addIcon and removeIcon. Ideally, you'd probably do this differently as a whole, but see if that works to begin with.

The way nameplates work, there is a finite amount of nameplates, and they are recycled as units appear and disappear. Nameplates aren't unique to the unit.If NamePlate1 gets assigned to a Warrior player character, and that character either dies or moves out of sight, NamePlate1 will get re-assigned to a different unit later on. If you modify that nameplate, the modifications will still be in place when it reappears later with a different unit assigned to it. So that has to be kept in mind.

Also, I could be wrong about the multiple frames per nameplate thing, but from my understanding, iconFrame being local to addIcon function, a new instance of it is created every time the function gets called, and references to it are lost once the function exits. Anyone feel free to let me know if that's wrong
  Reply With Quote
08-22-18, 02:42 AM   #7
Mitur
A Murloc Raider
Join Date: Aug 2008
Posts: 4
Thank you guys for pointing out my mistakes, I appreciate it very much!
I've updated my original post with the full code.
@Vrul - sorry for the "id" variable, it got mixed up when I decided to rename the argument to "unit" for better readability and missed some occurrences.
  Reply With Quote
08-22-18, 07:17 AM   #8
Vrul
A Frostmaul Preserver
 
Vrul's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2007
Posts: 292
Code:
local addonName = ...

local iconKey = addonName .. "Icon"

local GetNamePlateForUnit = C_NamePlate.GetNamePlateForUnit

local iconTexture = {
    ["DEATHKNIGHT"] = 135771,
    ["DEMONHUNTER"] = 236415,
    ["DRUID"] = 625999,
    ["HUNTER"] = 626000,
    ["MAGE"] = 626001,
    ["MONK"] = 626002,
    ["PALADIN"] = 626003,
    ["PRIEST"] = 626004,
    ["ROGUE"] = 626005,
    ["SHAMAN"] = 626006,
    ["WARLOCK"] = 626007,
    ["WARRIOR"] = 626008
}

local frame = CreateFrame("Frame")
 
frame:SetScript("OnEvent", function(self, event, unit)
    local namePlate = GetNamePlateForUnit(unit)
    if event == "NAME_PLATE_UNIT_ADDED" and UnitIsFriend("player", unit) then
        local _, class = UnitClass(unit)
        if iconTexture[class] then
            local icon = namePlate[iconKey]
            if not icon then
                icon = namePlate:CreateTexture(nil, "OVERLAY")
                icon:SetPoint('TOPLEFT', 10, 10)
                icon:SetSize(32, 32)
                namePlate[iconKey] = icon
            end
            icon:SetTexture(iconTexture[class])
            icon:Show()
            return
        end
    end
    if namePlate[iconKey] then
        namePlate[iconKey]:Hide()
    end
end)

frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
frame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
  Reply With Quote
08-22-18, 07:30 AM   #9
Mitur
A Murloc Raider
Join Date: Aug 2008
Posts: 4
Originally Posted by Vrul View Post
Lua Code:
  1. -- awesome clean code
Man...
Can't thank you enough for providing me with this crisp, minimalistic example.
Tested it already and it works just fine.
And I've learned a lot, thank you!
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » Class icons on nameplates

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