Thread Tools Display Modes
08-31-16, 08:07 AM   #1
Benio
A Murloc Raider
Join Date: Jul 2016
Posts: 8
Pandemic mechanic util

Here I present util how to handle with Pandemic mechanic, since I was unable to find any solution.
With a help of #WoWUIDev, this is the Pandemic util I made for my indev Warlock addon (note that the following util uses global Tooltip, but it's just for clarity):

Lua Code:
  1. Tooltip = Tooltip or {};
  2. local Tooltip = Tooltip;
  3.  
  4. Tooltip._tooltip = 'Tooltip._tooltip';
  5. Tooltip._tooltip_frame = CreateFrame('GameTooltip', Tooltip._tooltip, nil, 'GameTooltipTemplate');
  6.  
  7. -- @param {link} itemLink link to item
  8. -- @return {object} text object of line: text from given @itemLink Tooltip
  9. -- @credits Semlar#WoWUIDev
  10. Tooltip.GetTooltip = Tooltip.GetTooltip or function(itemLink)
  11.     Tooltip._tooltip_frame:SetOwner(UIParent, 'ANCHOR_NONE');
  12.     Tooltip._tooltip_frame:SetHyperlink(itemLink);
  13.    
  14.     local O = {}; -- output
  15.     for i = 1, Tooltip._tooltip_frame:NumLines() do
  16.         local line = _G[Tooltip._tooltip ..'TextLeft' ..i];
  17.         local text = line and line:GetText();
  18.         if not text then break end;
  19.         O[i] = tostring(text);
  20.     end;
  21.    
  22.     return O;
  23. end;
  24.  
  25. -- @param {uint|name} itemID item id or itemName item name
  26. -- @return {number|nil} realDuration real spell duration (unaffected by pandemic) or nil if unknown
  27. -- @credits Xuerian#WoWUIDev
  28. Tooltip.GetSpellDuration = Tooltip.GetSpellDuration or function(itemNameORID)
  29.     local link = GetSpellLink(itemNameORID);
  30.     local text = Tooltip.GetTooltip(link);
  31.     for i = 1, #text do
  32.         local duration = string.match(text[i], "over (%d+%.?%d?) sec");
  33.         if duration ~= nil then
  34.             return tonumber(duration);
  35.         end;
  36.     end;
  37.    
  38.     return nil;
  39. end;
  40.  
  41. -- @param {string} unitID unitID
  42. -- @param {name} itemName item name
  43. -- @param {string} rank UnitAura's rank
  44. -- @param {string} filter UnitAura's filter
  45. -- @return {bool|nil} pandemic if aura is affected by pandemic or nil if unknown
  46. Tooltip.GetUnitAuraPandemic = Tooltip.GetUnitAuraPandemic or function(unitID, itemName, rank, filter)
  47.     local realDuration = Tooltip.GetSpellDuration(itemName);
  48.     local _, _, _, _, _, duration = UnitAura(unitID, itemName, rank, filter);
  49.    
  50.     if
  51.             duration == nil
  52.         or  realDuration == nil
  53.     then return nil end;
  54.    
  55.      -- we love float, doesn't we?
  56.     if math.abs(realDuration - duration) < 0.01 then return false end;
  57.     if math.abs(realDuration *1.3 - duration) < 0.01 then return true end;
  58.    
  59.     return nil;
  60. end;

The idea behind this method is to read the spell duration directly from tooltip, using Tooltip.GetSpellDuration, ex.:
Lua Code:
  1. local realDuration = Tooltip.GetSpellDuration("Siphon Life");
The cache it and whenever checking UnitAura for the same spell, we can use cached value if it's not nil, ex.:
Lua Code:
  1. local _, _, _, _, _, duration = UnitAura("target" "Siphon Life", nil, "PLAYER HARMFUL");
  2. duration = realDuration or duration;

Finally, I've added a handy Tooltip.GetUnitAuraPandemic that takes same arguments like UnitAura, and returns if Spell duration already benefits from Pandemic or not.
Example: Before casting Siphon Life on target:
Lua Code:
  1. [15:20:07] Dump: value=Tooltip.GetUnitAuraPandemic("target", "Siphon Life", nil, "PLAYER HARMFUL");
  2. [15:20:07] empty result
After casting first Siphon Life on target:
Lua Code:
  1. [15:20:12] Dump: value=Tooltip.GetUnitAuraPandemic("target", "Siphon Life", nil, "PLAYER HARMFUL");
  2. [15:20:12] [1]=false
After casting second Siphon Life on target:
Lua Code:
  1. [15:20:15] Dump: value=Tooltip.GetUnitAuraPandemic("target", "Siphon Life", nil, "PLAYER HARMFUL");
  2. [15:20:15] [1]=true
  Reply With Quote
08-31-16, 08:22 AM   #2
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Even setting aside the conceptual flaws (your code is way more complicated than it needs to be to do what it does) there are several basic issues with your code that make it pretty unsuitable as an example of how anything should be done.

Lua Code:
  1. Tooltip = Tooltip or {};

This creates a global variable named "Tooltip". You say you know this, yet you did it anyway. This is 100% bad and you should never do it for any reason. Doing it in something you're calling an example for others to use is even worse than doing it in code for yourself.

Lua Code:
  1. Tooltip._tooltip = 'Tooltip._tooltip';
  2. Tooltip._tooltip_frame = CreateFrame('GameTooltip', Tooltip._tooltip, nil, 'GameTooltipTemplate');

This creates a global variable named "Tooltip._tooltip" which, though slightly less bad than a global "Tooltip", is still not good. Global variables should always follow two simple rules:

1. It should be unique, so there is no chance of it colliding with another badly named or accidentally leaked global. At best, global variable collision will make an addon break. At worst, it will break the entire UI and make the game unplayable.

2. It should clearly identify the origin (ie. what addon created it) and purpose of the value it contains, so if it shows up in an error message, stack trace, /framestack tooltip, or other debugging context, there is some hope of the user figuring out where it's coming from.

Lua Code:
  1. Tooltip.GetTooltip = Tooltip.GetTooltip or function(itemLink)

Since this code is running in the main chunk of your addon, it's only ever executed once. There will never already be a "GetTooltip" method on your "Tooltip" object, so there's no point in checking for one before defining it.

Lua Code:
  1. Tooltip.GetSpellDuration = Tooltip.GetSpellDuration or function(itemNameORID)

Lua Code:
  1. Tooltip.GetUnitAuraPandemic = Tooltip.GetUnitAuraPandemic or function(unitID, itemName, rank, filter)

Same as above. No reason to check for things that you already know don't exist.

Lua Code:
  1. if not text then break end;

While this won't break anything in the context of scanning an aura tooltip, it's also not necessary in that context -- if tooltip:NumLines() is 3, then the tooltip has 3 lines with text; you don't need to verify that there's text -- and will break scanning in other types of tooltips, since item tooltips, for example, can have empty lines in the middle of them.

Lua Code:
  1. return nil;

Writing this at the end of a function does absolutely nothing, and there's no reason to write it. It just takes up space for no reason.
__________________
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.

Last edited by Phanx : 08-31-16 at 08:26 AM.
  Reply With Quote
08-31-16, 09:09 AM   #3
Benio
A Murloc Raider
Join Date: Jul 2016
Posts: 8
Originally Posted by Phanx View Post
Even setting aside the conceptual flaws (your code is way more complicated than it needs to be to do what it does) there are several basic issues with your code that make it pretty unsuitable as an example of how anything should be done.

Lua Code:
  1. Tooltip = Tooltip or {};

This creates a global variable named "Tooltip". You say you know this, yet you did it anyway. This is 100% bad and you should never do it for any reason. Doing it in something you're calling an example for others to use is even worse than doing it in code for yourself.

Lua Code:
  1. Tooltip._tooltip = 'Tooltip._tooltip';
  2. Tooltip._tooltip_frame = CreateFrame('GameTooltip', Tooltip._tooltip, nil, 'GameTooltipTemplate');

This creates a global variable named "Tooltip._tooltip" which, though slightly less bad than a global "Tooltip", is still not good. Global variables should always follow two simple rules:

1. It should be unique, so there is no chance of it colliding with another badly named or accidentally leaked global. At best, global variable collision will make an addon break. At worst, it will break the entire UI and make the game unplayable.

2. It should clearly identify the origin (ie. what addon created it) and purpose of the value it contains, so if it shows up in an error message, stack trace, /framestack tooltip, or other debugging context, there is some hope of the user figuring out where it's coming from.
I though that my note about tainted global is clear enough.
It's just for clarity:

Originally Posted by Benio View Post
note that the following util uses global Tooltip, but it's just for clarity
If you wish implementing this util in your addon, then sure, you should follow all those tainting rules.

Originally Posted by Phanx View Post
Lua Code:
  1. Tooltip.GetTooltip = Tooltip.GetTooltip or function(itemLink)

Since this code is running in the main chunk of your addon, it's only ever executed once. There will never already be a "GetTooltip" method on your "Tooltip" object, so there's no point in checking for one before defining it.

Lua Code:
  1. Tooltip.GetSpellDuration = Tooltip.GetSpellDuration or function(itemNameORID)

Lua Code:
  1. Tooltip.GetUnitAuraPandemic = Tooltip.GetUnitAuraPandemic or function(unitID, itemName, rank, filter)

Same as above. No reason to check for things that you already know don't exist.
It's util. Util (or utility) is indented to be used multiply times.
This means, that it is highly propable that a few addons will use sam utility.
Then we can just use already existing one.

Originally Posted by Phanx View Post
Lua Code:
  1. if not text then break end;

While this won't break anything in the context of scanning an aura tooltip, it's also not necessary in that context -- if tooltip:NumLines() is 3, then the tooltip has 3 lines with text; you don't need to verify that there's text -- and will break scanning in other types of tooltips, since item tooltips, for example, can have empty lines in the middle of them.
Optimalization. This util is indented to be used to retrieve spell info. No spell I am working with in my indev addon have empty line before mentioned duration. They have empty line after it and thus, it's worth skipping this.

Originally Posted by Phanx View Post
Lua Code:
  1. return nil;

Writing this at the end of a function does absolutely nothing, and there's no reason to write it. It just takes up space for no reason.
Clarity. Some people likes to see what function returns. However you're absolutelly free to remove it, if you only wish.
  Reply With Quote
08-31-16, 06:12 PM   #4
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Benio View Post
I though that my note about tainted global is clear enough.
It's just for clarity:
Not good enough. If you're going to call it an example, it should be a good example of working code. People will copy/paste "example code" without understanding it fully (or at all) and if your code does things wrong, so will theirs. Doing things wrong doesn't provide "clarity" -- it's just "doing things wrong".

Originally Posted by Benio View Post
It's util. Util (or utility) is indented to be used multiply times.
This means, that it is highly propable that a few addons will use sam utility.
Then we can just use already existing one.
If you intend for this to be a reusable library, write it as a proper library and make an actual release instead of a forum post.

If you intend for the code to be copied and pasted into addon files, it should be entirely local, and not create any globals at all. There's not nearly enough overhead to justify putting this in the global namespace, especially with some of the worst global names possible.

Originally Posted by Benio View Post
Clarity. Some people likes to see what function returns. However you're absolutelly free to remove it, if you only wish.
Again, this doesn't provide "clarity". It just adds clutter and confusion. When you post "example code" as a snippet in a forum post, you should be aware that 99.9999999999% of the people who are going to use it will not have a good understanding of the language or the API, and will just use the code as-is, probably without even reading it. Using bad coding paradigms and redundant/unnecessary/inefficient code is the opposite of helpful.
__________________
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-31-16, 06:57 PM   #5
Benio
A Murloc Raider
Join Date: Jul 2016
Posts: 8
Oh ok then… move it to proper place then please. Even if it's a trash. Sorry for inconvenience. Just wanted to help.

Last edited by Benio : 08-31-16 at 07:26 PM.
  Reply With Quote
08-31-16, 08:32 PM   #6
Ketho
A Pyroguard Emberseer
 
Ketho's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,026
Originally Posted by Benio View Post
Lua Code:
  1. if
  2.         duration == nil
  3.     or  realDuration == nil
  4. then return nil end;

Honestly speaking this indent style/structure is kind of special, I don't mean to say anything with that

It's just the first time I see this style
  Reply With Quote

WoWInterface » Developer Discussions » Tutorials & Other Helpful Info. » Pandemic mechanic util

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