WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   AddOn Help/Support (https://www.wowinterface.com/forums/forumdisplay.php?f=3)
-   -   Adding %HP to default nameplates; getting an error occasionally (https://www.wowinterface.com/forums/showthread.php?t=56125)

nKweo 03-25-18 03:47 PM

Adding %HP to default nameplates; getting an error occasionally
 
I've been looking into ways to add a simple %HP text to the default enemy nameplates. So far the best thing I've found is Gello's old addon Nameplate Percents.

It actually still works fine most of the times, but it sometimes throws an error (and doesn't display) in PvE instances. I'm still trying to nail down the exact situation; so far it seems that it only happens in boss fights (but not every time).

The error isn't very clear, but it does say something about taint. I suspect the problem is related to this function:
Code:

-- look for new nameplates that don't have a percent overlay and add one
function frame:ScanNameplates(...)
  for i=1,select("#",...) do
    local plate = select(i,...)
                local name = plate:GetName()
                if name and name:match("^NamePlate") then
                        -- the statusBar is the first child of the first child of the nameplate
                        local statusBar = plate:GetChildren():GetChildren()
                        if not statusBar.percentOverlay then
                                statusBar.percentOverlay = statusBar:CreateFontString(nil,"OVERLAY")
                                statusBar.percentOverlay:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
                                local percent = statusBar.percentOverlay
                                percent:SetPoint("LEFT",3,0)
                                statusBar:HookScript("OnShow",ShowPercent)
                                statusBar:HookScript("OnHide",HidePercent)
                                overlays[statusBar.percentOverlay] = 1 -- add new child to next update batch
                        end
    end
  end
end

As I said, it's pretty old code and Blizzard obviously has made big changes to the way nameplates are handled (e.g. C_NamePlate.GetNamePlates). Moreover, they protected some stuff in 7.2(?).

I've been messing around with it a little bit, but my knowledge is insufficient. I'm hoping for some ideas to get this working perfectly. I'm guessing that this shouldn't be too difficult for some of you code wizards. Any ideas are appreciated though!

Ammako 03-25-18 05:37 PM

You would only get those errors if friendly nameplates are enabled, but then again you skipped providing the actual error message, so there's no way to say for certain whether that is the issue or not.

briskman3000 03-25-18 10:48 PM

Blizzard restricted all friendly nameplate customizations inside PvE instances. You can do literally nothing to them anymore in dungeons, Raids, etc.

http://www.wowhead.com/news=260801/a...s-in-patch-7-2

nKweo 03-26-18 02:53 AM

Thanks for the reply guys. The thing is that I'm not using friendly nameplates. Do you think this could nevertheless still lead to an error? I'll try to copy-past the error next time I get it. Like I said, it's been pretty elusive so far (only happening in PvE instances every now and then, so not the best time to troubleshoot).

If the error is caused by the code somehow trying to alter friendly nameplates, how could I make it so that it only looks for enemy plates?

Ammako 03-26-18 06:42 AM

If friendly nameplates aren't used, then there is no way for them to trigger taint.
Plates are only protected when assigned to friendly units, and only in PvE instances. Everything else is free game.
If they aren't enabled then they can't cause errors and break things.

The only thing I could think of would be neutral units that get yellow nameplates while "enemy nameplates" are enabled, but who would be somehow considered friendly by nameplate code... but I don't think that actually exists, and I've never had a problem in nearly a year of insecurely altering nameplates, as long as I kept friendly nameplates disabled (and I've been in a lot of raids/dungeon instances, but admittedly I could have missed some.)

(For the record, as far as I'm aware, Personal Resource Display is not considered a friendly nameplate, so it can't be that.)

But then again, error logs are king. I'd imagine it's not even friendly nameplate-related (or at least I'd be surprised if it were.)

thomasjohnshannon 03-29-18 07:16 PM

You can use "framenamehere:IsForbidden()" to check if the frame is protected. I use this method in my nameplate addon and I haven't had any problems with it.

nKweo 03-30-18 01:05 PM

Brilliant, thanks! Seems to work smooth now. I 'borrowed' heavily from your code. Happy to hear if there are flaws here.
Code:

-- Add health percent text to (unprotected) nameplates (credit: nPlates by Grimsbain)
hooksecurefunc("CompactUnitFrame_UpdateStatusText", function(frame)
        if frame:IsForbidden() or ( UnitIsFriend("player",frame.displayedUnit) and not UnitIsUnit(frame.displayedUnit,"player") ) then return end
        if not frame.healthBar.percent then
                frame.healthBar.percent = frame.healthBar:CreateFontString(nil,"OVERLAY")
                frame.healthBar.percent:SetPoint("LEFT",frame.healthBar)
                frame.healthBar.percent:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
        end
                local percentcalc = ceil((UnitHealth(frame.displayedUnit) / UnitHealthMax(frame.displayedUnit)) * 100)
                frame.healthBar.percent:SetFormattedText("%d%%",percentcalc)
--                frame.healthBar.percent:Show()
end)

EDIT: I'm trying to hide the display from friendly plates at all times (except the personal resource display). Doesn't seem to work like this. Suggestions appreciated!

Ammako 03-30-18 03:02 PM

What happens if you try frame.unit rather than frame.displayedUnit?

semlar 03-30-18 05:48 PM

The issue is that you're scanning for nameplates by looping through the children of WorldFrame, then you're trying to call a function on the frame (plate:GetName()), and if that frame happens to be forbidden it will throw an error.

You could add a check like if not plate:IsForbidden(), and put the rest of your function inside of that, or you can use the actual nameplate events that they added a couple years ago with the launch of Legion (NAME_PLATE_CREATED, NAME_PLATE_UNIT_ADDED, NAME_PLATE_UNIT_REMOVED).

The protected friendly nameplates in instances are implemented in a way that addons don't need to do anything to account for their existence; they don't trigger the same nameplate events and are completely separate from the standard nameplate frames, so unless you're doing something like actively scanning through every child of WorldFrame, you won't accidentally encounter them.

nKweo 03-31-18 01:59 PM

@Ammako: didn't seem to make a difference.
@Semlar: thanks for clarifying. I'm trying a completely different approach now (see previous post), which works better. The problem now though is that the percent display gets drawn on friendly plates even though I'm trying to prevent it (just for consistency).

What's worse is that the percentage overlay gets drawn onto the raid frames as well now... Initially friendly plates stay clean, but as soon as a percentage overlay is created for an enemy plate, overlays get added everywhere... Is there an easy way to circumvent this with this method?

Ammako 03-31-18 03:57 PM

OH wait no, I'm dumb.

See, nameplates get reused, once you draw %HP onto a plate from an enemy it will continue showing once the plate gets reused for another unit, friendly or not.

When unit is friend you want to specifically :Hide() frame.healthBar.percent

Unless this has changed lately, but last I was messing with this stuff, this has been the case.

The thing is, though, doing it via this method taints the friendly nameplates regardless, hiding the text doesn't untaint them (you can't untaint something once it's tainted.)
I wouldn't know of a way to do it without tainting friendly nameplates and tbh I never cared. Default plates are useless within PvE instances so there is very little reason to even have them enabled.


btw you still haven't shown us what the error you were getting from the first code actually was. If it's really just a friendly-nameplates-in-instances thing maybe you can just keep using that code but with extra checks like semlar said. That may be the better way to do it. Or maybe it's not even related to friendly nameplates, because we don't know.

nKweo 03-31-18 05:18 PM

That makes a lot of sense. At least I've managed to keep them from the raid frames. Using this function did the trick (thanks again Grimsbain!):
Code:

local IsNameplate = function(unit)
        if type(unit) ~= "string" then return false end
        if ( string.match(unit,"nameplate") ~= "nameplate" and string.match(unit,"NamePlate") ~= "NamePlate" ) then
                return false
        else
                return true
        end
end

Taint has never actually affected any functionality for me while playing, so I guess it's more of a 'puritan' thing. Pretty happy with this at the moment.

Ammako 03-31-18 06:02 PM

Nameplate taint doesn't really affect anything beyond throwing errors if a nameplate used by a friendly unit has been tainted, even if you aren't currently touching them. Anything you touch becomes tainted, but this is only a problem if said code is expected to run through secure Blizzard functions. Code tainted = the code can no longer be verified as secure = errors if secure code ever processes it.

Maybe I messed up when trying to avoid protected nameplates taint, though; if semlar says they're completely separate maybe I should mess with it again some more.


Edit: Sounds like my implementation of the check was bogus. I had no idea frame:IsForbidden() was a thing, so I was using a bunch of different conditionals together that were meant to prevent modifications from being done to friendly nameplates while you were in an instance, but I guess that wasn't really failproof and some of it got through anyway.

Using frame:IsForbidden() and not even changing any of the rest of my code appears to prevent taint errors when within instances and friendly nameplates are enabled. Or at least, disabling the checks triggers an error whenever a friendly nameplate comes on screen, and when I re-enable them those errors no longer occur.

Well that's pretty cool, that was barely any work, and basically, ignore most of the other things I said earlier about nameplate taint, because it's wrong.

nKweo 04-01-18 01:10 AM

That's super cool. Guess we all learned something then:). Funny how newbie questions such as mine can sometimes even (indirectly) help seasoned coders. And thanks again for your time and thoughts.

Ammako 04-01-18 07:30 AM

Psh I'm not a seasoned coder. I just do whatever gives me the results I want. To sum it up: I don't code, I hack.
I hang around and sometimes people show up with better and usually easier/more straightforward ways to do things, and that's how I learn to do better I guess.

I blame the lack of proper API documentation from Blizzard... fansites often just never get updated with new API documentation (For ex: nameplates are still undocumented on wowpedia, and the 7.2.0 API changes don't even mention forbidden nameplates.) So you have to just mess with it and feel your way around until you figure out how things work, or find the right people to ask for info, who happen to have done the research already.
Though I hear something is in the works with 8.0, as far as official documentation from Blizzard goes...

nKweo 04-01-18 03:02 PM

Correction: more seasoned coders :D. I made a small correction to the aforementioned function by the way. I tried to be a smart ass by fiddling with the original...

Chekoz9 10-24-20 11:55 PM

Pls Help
 
Ok so Idk anything about programming just been modifying the script using some of your suggestions and the error is still a thing plus 2 percentages are shown in the nameplates if you guys could help me solve it or pass the right script so I can copy and paste it I would appreciatte it.

This is the script that I got so far:

-- 06/22/2015 1.0.5 toc update for 6.2
-- 04/19/2015 1.0.4 fix for lua error when chat bubbles enabled
-- 02/24/2015 1.0.3 toc update for 6.1
-- 10/14/2014 1.0.2 6.0 patch
-- 05/20/2014 1.0.1 initial release

local frequency = 0.2 -- how frequently to look for new nameplates and update visible percents
local numChildren = 0 -- number of WorldFrame's children
local overlays = {} -- indexed by overlay frame added to each nameplate's statusBar

local frame = CreateFrame("Frame",nil,UIParent)
frame.timer = 0
frame.knownChildren = 0 -- number of WorldFrame's children that we know about

-- updates the percentOverlay text on the nameplate's statusbar
local function UpdatePercent(self)
local parent = self:GetParent()
local value = parent:GetValue()
local _,maxValue = parent:GetMinMaxValues()
if maxValue and value<maxValue then
self:SetText(format("%d%%",100*value/maxValue))
else
self:SetText("") -- blank if no relevant values or value is maxValue (100% life)
end
end

-- when a nameplate shows, add it to frame.statusBars
local function ShowPercent(self)
overlays[self.percentOverlay] = 1
end

-- when a nameplate hides, remove it from frame.statusBars
local function HidePercent(self)
overlays[self.percentOverlay] = nil
self.percentOverlay:SetText("") -- blank for when nameplate reused
end

local IsNameplate = function(unit)
if type(unit) ~= "string" then return false end
if ( string.match(unit,"nameplate") ~= "nameplate" and string.match(unit,"NamePlate") ~= "NamePlate" ) then
return false
else
return true
end
end


-- look for new nameplates that don't have a percent overlay and add one
function frame:ScanNameplates(...)
for i=1,select("#",...) do
local plate = select(i,...)
local name = plate:GetName()
if name and name:match("^NamePlate") then
-- the statusBar is the first child of the first child of the nameplate
local statusBar = plate:GetChildren():GetChildren()
if not statusBar.percentOverlay then
statusBar.percentOverlay = statusBar:CreateFontString(nil,"OVERLAY","GameFontHighlightSmall")
local percent = statusBar.percentOverlay
percent:SetPoint("RIGHT")
statusBar:HookScript("OnShow",HidePercent)
statusBar:HookScript("OnHide",HidePercent)
overlays[statusBar.percentOverlay] = 1 -- add new child to next update batch
end
end
end
end

function frame:OnUpdate(elapsed)
self.timer = self.timer + elapsed
if self.timer > frequency then
self.timer = 0
-- first look for any new nameplates (if WorldFrame has a new kid, it's likely a nameplate)
numChildren = WorldFrame:GetNumChildren()
if numChildren > self.knownChildren then
self.knownChildren = numChildren
self:ScanNameplates(WorldFrame:GetChildren())
end
-- next update percents for all visible nameplate statusBars
for overlay in pairs(overlays) do
UpdatePercent(overlay)
end
end
end
frame:SetScript("OnUpdate",frame.OnUpdate)

-- Add health percent text to (unprotected) nameplates (credit: nPlates by Grimsbain)
hooksecurefunc("CompactUnitFrame_UpdateStatusText", function(frame)
if frame:IsForbidden() or ( UnitIsFriend("player",frame.displayedUnit) and not UnitIsUnit(frame.displayedUnit,"player") ) then return end
if not frame.healthBar.percent then
frame.healthBar.percent = frame.healthBar:CreateFontString(nil,"OVERLAY")
frame.healthBar.percent:SetPoint("RIGHT",frame.healthBar)
frame.healthBar.percent:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE")
end
local percentcalc = ceil((UnitHealth(frame.displayedUnit) / UnitHealthMax(frame.displayedUnit)) * 100)
frame.healthBar.percent:SetFormattedText("%d%%",percentcalc)
-- frame.healthBar.percent:Show()
end)

SDPhantom 10-27-20 07:04 AM

This is similar to what I did with my personal nameplate addon. I modified it to duplicate the layout of your existing code. If you're running this on Classic, change the first hook from AcquireUnitFrame to OnNamePlateCreated.
Lua Code:
  1. local TextObjects={};
  2.  
  3. hooksecurefunc(NamePlateDriverFrame,"AcquireUnitFrame",function(self,base)
  4.     if base:IsForbidden() then return; end
  5.  
  6.     local uf=base.UnitFrame; if TextObjects[uf] then return; end
  7.     local healthtext=uf.healthBar:CreateFontString(nil,"OVERLAY",FontName);--"NumberFontNormalSmall");
  8.     healthtext:SetPoint("RIGHT",0,0);
  9.     healthtext:SetFont(STANDARD_TEXT_FONT,12,"OUTLINE");
  10.  
  11.     TextObjects[uf]=healthtext;
  12. end);
  13.  
  14. hooksecurefunc("CompactUnitFrame_UpdateHealth",function(self)
  15.     local healthtext=TextObjects[self]; if not healthtext then return; end
  16.     if UnitIsUnit(frame.displayedUnit,"player") or not UnitIsFriend("player",frame.displayedUnit) then
  17.         healthtext:SetFormattedText("%.0f%%",100*UnitHealth(self.displayedUnit)/UnitHealthMax(self.displayedUnit));
  18.     else healthtext:SetText(nil); end
  19. end);


All times are GMT -6. The time now is 02:03 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI