Thread Tools Display Modes
03-04-11, 03:46 PM   #1
grom
A Deviate Faerie Dragon
Join Date: Jun 2006
Posts: 17
Changing/hooking method

...without specifying an object.

I want to change the toning of every status bar without modifying the .blp. So now i want to change the SetStatusBarColor(r, g, b, a) method to basically do SetStatusBarColor(r*0.8, g*0.8, b*0.8, a). This is what i have at the moment:

lua Code:
  1. function ModSetStatusBarColor(self)
  2.     local r, g, b, a = self:GetStatusBarColor()
  3.     r = r*0.8
  4.     g = g*0.8
  5.     b = b*0.8
  6.     self:SetStatusBarColor(r, g, b, a)
  7. end

How can i make it so whenever object:SetStatusBarColor() is executed (by blizzard code or any other addon), my ModSetStatusBarColor() is fired?

I tried google, wowprogramming, wowpedia and the forum search but i think i can't get the search term right.
  Reply With Quote
03-04-11, 03:55 PM   #2
Sniffles
A Black Drake
 
Sniffles's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2010
Posts: 86
Do you mean

hooksecurefunc("functionName", hookfunc)

frame:HookScript("handler", function)

?
  Reply With Quote
03-04-11, 04:04 PM   #3
grom
A Deviate Faerie Dragon
Join Date: Jun 2006
Posts: 17
I tried that, but it needs the frame name as far as i understand it. I want it to work without specifying the frame.

I could probably do PlayerFrameHealthBar:HookScript("SetStatusBarColor", ModSetStatusBarColor(self)) but then i'd have to do it for every frame.

To reword my request:

Some blizzard or addon code executes object:SetStatusBarColor(r, g, b, a). Instead of using the given r, g, b values it should use my modified values (e.g. r*0.8, g*0.8, b*0.8).

Last edited by grom : 03-04-11 at 04:08 PM.
  Reply With Quote
03-04-11, 05:27 PM   #4
hankthetank
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2009
Posts: 64
Not possible. You can hook a specific statusbar's SetStatusBarColor. Every object of the widget creates a metatable with an __index reference to a global SetStatusBarColor() that is not part of FrameXML anymore but of WoW's internals.

The only alternative would be to loop trough all UIParent members and check for frames of GetObjectType() "statusbar", then use hooksecurefunc to safely hook (some of the statusbars may be part of unit frames or other secure template derivates) into the function. A bit excessive for something as fancy as a color replacement imo. You would also face the problem of dynamicly created statusbars (bossmods, cooldown addons etc.). That's something you couldn't track without scaning over and over again, an act that would eat up nearly all game resources.

Last edited by hankthetank : 03-04-11 at 05:32 PM.
  Reply With Quote
03-05-11, 12:36 AM   #5
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
All frames of the same type share their metatable, so you can hook the method like this:
Code:
local Meta = getmetatable( CreateFrame( "StatusBar" ) ).__index;
local SetStatusBarColor = Meta.SetStatusBarColor;
hooksecurefunc( Meta, "SetStatusBarColor", function ( self, R, G, B, ... )
    return SetStatusBarColor( self, R * 0.8, G * 0.8, B * 0.8, ... );
end );
If your addon isn't the first one to load, some mods might bypass this hook by saving a reference to the original SetStatusBarColor method. In practice though, this will probably work for all status bars in a person's UI.
  Reply With Quote
03-05-11, 01:02 AM   #6
hankthetank
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2009
Posts: 64
Oh, true that. I should have thought that comment through. On the other hand the standard interface will call the function only once most of the time and that's before your addon loads. So in practice you wouldn't get around using the suggested brute force technique at least once when loading.

Last edited by hankthetank : 03-05-11 at 01:44 AM.
  Reply With Quote
03-05-11, 08:51 AM   #7
grom
A Deviate Faerie Dragon
Join Date: Jun 2006
Posts: 17
Originally Posted by Saiket View Post
All frames of the same type share their metatable, so you can hook the method like this:
Code:
local Meta = getmetatable( CreateFrame( "StatusBar" ) ).__index;
local SetStatusBarColor = Meta.SetStatusBarColor;
hooksecurefunc( Meta, "SetStatusBarColor", function ( self, R, G, B, ... )
    return SetStatusBarColor( self, R * 0.8, G * 0.8, B * 0.8, ... );
end );
If your addon isn't the first one to load, some mods might bypass this hook by saving a reference to the original SetStatusBarColor method. In practice though, this will probably work for all status bars in a person's UI.
Thanks, this works fine for everything but the nameplates (i named the addon '!!aa' so it's loaded before the rest). After reading a little bit about metatables on lua.org, i think i understand what your code does. The reason nameplates aren't colored is, that nameplate health bars are not corresponding to SetStatusBarColor in the metatable. Is this correct? Well, i tried some things via /script:

/script print(getmetatable(NamePlate1:GetChildren()))
/script print(getmetatable(CreateFrame("StatusBar")))

/script local healthBar=NamePlate1:GetChildren(); print(healthBar); healthBar:SetStatusBarColor(0,0,0) -> this works

And i came to the conclusion, that i don't understand nameplates at all.

At first start up, nameplates start with NamePlate1 (up to NamePlate4), two tables per nameplate (the first seems to be the health bar) and one metatable for all (getmetatable(NamePlate1/2/3/4:GetChildren()) returns the same metatable as getmetatable(CreateFrame("StatusBar"))). After reloading the interface, they start with NamePlate5 (to ...8) and NamePlate1 to ...4 don't exist anymore. Those new nameplates have new tables but the same metatable as 1 to 4. Then i log out and log in again, nameplates start at ...9 with new tables and a new metatable.
  Reply With Quote
03-05-11, 11:50 AM   #8
hankthetank
A Theradrim Guardian
AddOn Author - Click to view addons
Join Date: Jul 2009
Posts: 64
Naturally they share the same metamethods. All objects of the same widget do, that is exactly why the above code works. You hook into their prototype.

The reason you see different values inside the metatable after a relog is that the memory for the functions is dynamically allocated when logging in. _index.SetStatusBarColor may return function:123AF6 before and function:AB34FF after a relog but it's still a reference to the same function, just with a newly generated handle. Between /reloadui prototypes seem to stay in memory so the output for the metatables is the same.

And nameplates are anonymous children of WorldFrame that are dynamically created on demand. When you reload the ui or relog all frames are created again, so are the nameplates and their tables.

I can confirm however that they have their original statusbar color first while calling SetStatusBarColor directly jumps into the function hook like it should, which can only mean that SetStatusBarColor isn't called when the frame is being created... I guess. Maybe the Blizzard code calls HealthBar:GetStatusBarTexture():SetVertexColor() instead or something like that.

Last edited by hankthetank : 03-05-11 at 11:57 AM.
  Reply With Quote
03-05-11, 05:38 PM   #9
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
WoW actually manipulates the nameplates directly in C++, bypassing their Lua methods and thus skipping any hooks. Unfortunately, that leaves no way to detect color changes short of checking their color every time the screen repaints.

If you're determined to make the nameplates match though, you could overlay a transparent black texture over the nameplate bars as soon as they get created.
  Reply With Quote
03-06-11, 11:28 AM   #10
grom
A Deviate Faerie Dragon
Join Date: Jun 2006
Posts: 17
Originally Posted by Saiket View Post
WoW actually manipulates the nameplates directly in C++, bypassing their Lua methods and thus skipping any hooks. Unfortunately, that leaves no way to detect color changes short of checking their color every time the screen repaints.

If you're determined to make the nameplates match though, you could overlay a transparent black texture over the nameplate bars as soon as they get created.
How would i do that? The 'as soon as they get created' part. I thought that there'd be an event to look for but i couldn't find one. Then i looked through rNameplates and it looks for nameplates every 0.33 sec via WorldFrame:GetChildren(). I suppose that's the 'brute force' method hank spoke of.

This is my latest approach (similar to rNameplates):

lua Code:
  1. local frame = CreateFrame("Frame")
  2. frame:SetScript("OnEvent", function(self, event)
  3.     if event == "NAMEPLATEPOPUPEVENT" then
  4.         local num = select('#', WorldFrame:GetChildren())
  5.         for i=2, num do
  6.             select(i, WorldFrame:GetChildren()):HookScript("OnShow", function(self)
  7.                 local name = string.find(self:GetName(), "NamePlate")
  8.                 if name then
  9.                     local R, G, B, A = self:GetChildren():GetStatusBarColor()
  10.                     self:GetChildren():SetStatusBarColor(R*0.5, G*0.5, B*0.5, A)
  11.                 end
  12.             end)
  13.         end
  14.     end
  15. end)
  16. frame:RegisterEvent("NAMEPLATEPOPUPEVENT")

Edit: So after further blatant copying from rNameplates i tried this:

lua Code:
  1. local frame = CreateFrame("Frame")
  2. local lastupdate = 0
  3.  
  4. frame:SetScript("OnUpdate", function(self, elapsed)
  5.     lastupdate = lastupdate + elapsed
  6.     if lastupdate > 0.33 then
  7.         lastupdate = 0
  8.         local num = select('#', WorldFrame:GetChildren())
  9.         for i=2, num do
  10.             select(i, WorldFrame:GetChildren()):HookScript("OnShow", function(self)
  11.                 local name = string.find(self:GetName(), "NamePlate")
  12.                 if name then
  13.                     local R, G, B = self:GetChildren():GetStatusBarColor()
  14.                     self:GetChildren():SetStatusBarColor(1, 0, 0)
  15.                 end
  16.             end)
  17.         end
  18.     end
  19. end)

It works for some time, then stops working, starts working again. Hiding nameplates and showing them again sometimes helps, sometimes it doesn't. I get a serious urge to give up on the case.

Edit: I just thought that there's little reason to hook the script at this point?!

Last edited by grom : 03-06-11 at 12:43 PM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Changing/hooking method


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