Thread Tools Display Modes
10-23-15, 06:43 AM   #1
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
Need help with SpellBook taint

I'm currently working on an addon that interracts with the SpellBook but I seem to taint it which causes errors when trying to cast a spell from the book (because CastSpell() is protected).

The idea is that I have a button showing a spell, and when clicked it opens the SpellBook to the page where the spell is located.
As far as I've seen there's no function for this so I basically go through all the tabs, pages and slots to find the correct spell.
The issue is that this, and even just toggling the SpellBook, will taint it.
So I'm trying to figure out if there's a way I can fix it.
Full code can be found on my GitHub

Once the button is pressed the only relevant code is the following:
(line 122- 141)
It get the name of the spell, then calls the function to scout through the spellbook for that name.
If it's found it plays a highlight animation, else it just opens on the default page.
And then it opens the SpellBook if it's not open yet.
Lua Code:
  1. elseif (self.unlockType == UNLOCKTYPE_SPELL) then
  2.     local spellName = self.SpellName:GetText();
  3.     local success, slot = OpenSpellBookAtSpell(spellName);
  4.     if success then
  5.         ILW_SpellBookHighlight:ClearAllPoints();
  6.         ILW_SpellBookHighlight:SetPoint("TOPLEFT", slot, "TOPRIGHT");
  7.         ILW_SpellBookHighlight:Show();
  8.         ILW_SpellBookHighlight.increasing = true;
  9.         ILW_SpellBookHighlight:SetAlpha(0);
  10.     else
  11.         print("|cFFFFD100ILWhat:|r |cFFFF5555" .. spellName .." is not in the spellbook. It might be hidden.|r");
  12.         SpellBookSkillLineTab_OnClick(_G["SpellBookSkillLineTab2"]);
  13.         while (SpellBookPrevPageButton:IsEnabled()) do
  14.             SpellBookPrevPageButton_OnClick();
  15.         end
  16.     end
  17.        
  18.     if (not SpellBookFrame:IsShown()) then
  19.         ToggleSpellBook(BOOKTYPE_SPELL);
  20.     end

The function to scout works like this:
(line 47 - 105)
It simply starts at the first tab, checks all the slots until it reaches an empty one or the last one.
Goes through the different pages on that tab until it's done and goes to the second tab.
if it finds the spell it stays on that page and returns success and the slot (to archor the highlight to)
Lua Code:
  1. local function OpenSpellBookAtSpell(searchName)
  2.     local tabNr = 1;
  3.     local maxTabs = 2;
  4.     local buttonNr = 1;
  5.     local buttonPerPage = 12;
  6.     local spellName = "";
  7.     local firsTimeInTab = false;
  8.    
  9.     SpellBookSkillLineTab_OnClick(_G["SpellBookSkillLineTab"..tabNr]);
  10.     while (tabNr <= maxTabs) do
  11.    
  12.         -- go to the first page
  13.         if (not firsTimeInTab) then
  14.             while (SpellBookPrevPageButton:IsEnabled()) do
  15.                 SpellBookPrevPageButton_OnClick();
  16.             end
  17.             firsTimeInTab = true;
  18.         end
  19.    
  20.         -- if target slot has a spell in it
  21.         if _G["SpellButton"..buttonNr.."SpellName"] ~= nil and _G["SpellButton"..buttonNr.."SpellName"]:IsShown() then
  22.             -- if the current tab still has unchecked spells
  23.             if (buttonNr <= buttonPerPage) then
  24.                
  25.                 spellName = _G["SpellButton"..buttonNr.."SpellName"]:GetText();
  26.                
  27.                 if (spellName == searchName) then
  28.                     -- Found the spell, end the world
  29.                     return true, _G["SpellButton"..buttonNr];
  30.                 end
  31.                
  32.                 buttonNr = buttonNr + 2;
  33.  
  34.                 -- reached limit on uneven, go even
  35.                 -- Needed because slotNr goes L->R U->D while spells go U->D L->R
  36.                 if (buttonNr > 12 and buttonNr %2 == 1) then
  37.                     buttonNr = 2;
  38.                 end
  39.             end
  40.         else -- else check for next page
  41.            
  42.             -- has next page, flip page
  43.             if (SpellBookNextPageButton:IsEnabled() ) then
  44.                
  45.                 SpellBookNextPageButton_OnClick();
  46.                 buttonNr = 1;
  47.             else -- else next tab
  48.                
  49.                 buttonNr = 1;
  50.                 tabNr = tabNr + 1;
  51.                 SpellBookSkillLineTab_OnClick(_G["SpellBookSkillLineTab"..tabNr]);
  52.                 firsTimeInTab = false;
  53.             end        
  54.         end
  55.     end
  56.    
  57.     return false;
  58.    
  59. end

The template for the buttons are in the XML file.
Template lines 15 to 136
Actual buttons at 242 - 300

So I'm trying to figure out if there is a way to prevent the taint, or if there's just no way to do it correctly.

Last edited by LanceDH : 10-23-15 at 07:05 AM.
  Reply With Quote
10-23-15, 10:27 AM   #2
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 341
Not at home yet, can't run live tests, but here's my wild guess after I checked your code on GH... You have quite many Show/Hide calls, but you dun always check if character is in combat or not, you can't toggle frame visibility whilst in combat... You have to use InCombatLockdown() function to check whether it's safe to make those calls or not.

And I strongly recommend using taint log, it helps quite much...
__________________

Last edited by lightspark : 10-23-15 at 11:01 AM. Reason: checked code on github
  Reply With Quote
10-23-15, 12:35 PM   #3
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
I think your only chance of getting this to work correctly without breaking the spellbook is to create a secure button which executes a macro to /click the spellbook forward/back buttons to the proper page, because calling any of the spellbook functions directly is going to taint everything it touches.

That being said, why not just make it so people can drag the spell off the button you're displaying, rather than open the spellbook? It seems like an extra step, unless I'm not understanding what your addon is meant to do.
  Reply With Quote
10-23-15, 12:45 PM   #4
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
Originally Posted by lightspark View Post
Not at home yet, can't run live tests, but here's my wild guess after I checked your code on GH... You have quite many Show/Hide calls, but you dun always check if character is in combat or not, you can't toggle frame visibility whilst in combat... You have to use InCombatLockdown() function to check whether it's safe to make those calls or not.

And I strongly recommend using taint log, it helps quite much...
It's not the best way to do things, but I have the 2main frames not work during combat and hide once you enter combat.
The other ones are (in theory) impossible to trigger without either of those 2 frames being shown.
I used UnitAffectingCombat("player") for it though. Achieves the same thing but might look into using the function you gave instead.
As for taint log, totally forgot that was a thing.

Originally Posted by semlar View Post
I think your only chance of getting this to work correctly without breaking the spellbook is to create a secure button which executes a macro to /click the spellbook forward/back buttons to the proper page, because calling any of the spellbook functions directly is going to taint everything it touches.

That being said, why not just make it so people can drag the spell off the button you're displaying, rather than open the spellbook? It seems like an extra step, unless I'm not understanding what your addon is meant to do.
Yea as I was waiting for a reply I pretty much implemented the dragging as well.
I was already going to add that but the reason I wanted to have the SpellBook is because I also do things like open the dungeon journal, talent frame, glyph frame, .. which... I'm probably also tainting beyon belief though I don't think they call protected functions like the SpellBook does.
Another annoyance is that IsPassiveSpell("name") doesn't actually work correctly as it returns false for Tiger Strikes whish is a passive, probably because it triggers another spell with the same name that isn't a passive.
Not the biggest issue but just annoying and might require more work.

I'll check the taint log and see what comes up. worst case I'll have to live without it.


Edit:
Seems WoW just instantly taints any global variable in addons.
But as far as I understand my functions need to be global to call them in my XML code?
Or is there some other way to do this.

Last edited by LanceDH : 10-23-15 at 01:09 PM.
  Reply With Quote
10-23-15, 01:54 PM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
Originally Posted by LanceDH View Post
But as far as I understand my functions need to be global to call them in my XML code?
Correct. Though it's one of many reasons why it's suggested to not use XML. You can do almost everything directly from Lua.
__________________
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
10-23-15, 02:11 PM   #6
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
Originally Posted by SDPhantom View Post
Correct. Though it's one of many reasons why it's suggested to not use XML. You can do almost everything directly from Lua.
Well fk.
Here I've been programming addons in nothing but LUA thinking I'm not doing it the way I should.
And now I, for once, code one with XML..
  Reply With Quote
10-23-15, 02:41 PM   #7
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 341
Originally Posted by LanceDH View Post
It's not the best way to do things, but I have the 2main frames not work during combat and hide once you enter combat.
The other ones are (in theory) impossible to trigger without either of those 2 frames being shown.
I used UnitAffectingCombat("player") for it though. Achieves the same thing but might look into using the function you gave instead.
As for taint log, totally forgot that was a thing.
However, I managed to get one when I clicked ILW_SpellBookTab (button in spellbook) TWICE, not once, but twice, on first click I saw a warning from your addon in my chat, however, on second one I've got a taint (line #491, inside ToggleUnlockedPage function).

Code:
'ILW_UnlockContainer:Hide()'.
!BugGrabber\BugGrabber.lua:573: in function <!BugGrabber\BugGrabber.lua:573>
[C]:: in function 'Hide'
ILearnedWhat\ILearnedWhat-6.2b1.lua:491: in function <ILearnedWhat\ILearnedWhat.lua:486>
ILearnedWhat\ILearnedWhat-6.2b1.lua:504: in function <ILearnedWhat\ILearnedWhat.lua:503>

Originally Posted by LanceDH View Post
Seems WoW just instantly taints any global variable in addons.
But as far as I understand my functions need to be global to call them in my XML code?
Or is there some other way to do this.
Well, write it in pure Lua, safest approach possible.

One more thing. Their API is almost always safe, there are some exceptions though, but NEVER (almost never tbh, there are few exceptions too) use functions blizz implemented in Lua themselves, it almost always causes taints. Your taint you were talking about in OP was caused by usage of blizz' SpellBookSkillLineTab_* functions.

P.S. BTW, sorry, completely missed the point in my first comment, was a bit tired. Went full retard, realized it just now after a short nap,
__________________

Last edited by lightspark : 10-23-15 at 02:49 PM.
  Reply With Quote
10-23-15, 03:11 PM   #8
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
Originally Posted by lightspark View Post
However, I managed to get one when I clicked ILW_SpellBookTab (button in spellbook) TWICE, not once, but twice, on first click I saw a warning from your addon in my chat, however, on second one I've got a taint (line #491, inside ToggleUnlockedPage function).
Had no clue :Hide() also caused errors in combat so can't say I 'protected' any of those.
I guess PLAYER_REGEN_DISABLED, where I hide everything when combat starts, happens before errors start happening.
Will have to fix that now that I know.

Originally Posted by lightspark View Post
Well, write it in pure Lua, safest approach possible.

One more thing. Their API is almost always safe, there are some exceptions though, but NEVER (almost never tbh, there are few exceptions too) use functions blizz implemented in Lua themselves, it almost always causes taints. Your taint you were talking about in OP was caused by usage of blizz' SpellBookSkillLineTab_* functions.

P.S. BTW, sorry, completely missed the point in my first comment, was a bit tired. Went full retard, realized it just now after a short nap,
Wouldn't it be worth it to create frames in XML and use SetScript with local functions in LUA?
Or does the XML itself cause issues?
Having done it both ways now I must say I find XML to make the base for frames a lot easier to use and keep track of things, and doesn't clutter my LUA files as much.
  Reply With Quote
10-23-15, 03:46 PM   #9
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Just wanted to clear something up, since people seem a little confused as to what taint actually is.

Anything an addon does is considered "tainted". Its purpose is to prevent an addon from tricking a blizzard function into executing code that it wouldn't normally be able to do directly, or otherwise influence the result of a protected function.

So, when we call a function like SpellBookPrevPageButton_OnClick, which calls SpellBookFrame_Update, which calls SpellBook_UpdatePlayerTab, which calls SpellBookFrame_UpdateSpells, which calls SpellButton_UpdateButton, which sets "self.offSpecID", which is then read by SpellButton_OnClick when you click the spell button, it directly influences whether or not "CastSpell" is called by the interface.

Since we've "tainted" the execution path by calling a function which ultimately affected a protected function, the interface prevents the protected function from being called.

When you create a global variable, it's tainted because it wasn't created by blizzard's "trusted" code, it was created by your addon, which is untrustworthy. If something in the interface were to read that variable it would taint its execution path.

This doesn't mean that global variables are bad, just that you need to take care not to name them something that would ever be referenced by anything else.
  Reply With Quote
10-23-15, 03:58 PM   #10
lightspark
A Rage Talon Dragon Guard
 
lightspark's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2012
Posts: 341
Originally Posted by LanceDH View Post
Had no clue :Hide() also caused errors in combat so can't say I 'protected' any of those.
I guess PLAYER_REGEN_DISABLED, where I hide everything when combat starts, happens before errors start happening.
Will have to fix that now that I know.
Unfortunately, many things cause taints, some things can't be used by 3rd party, others are restricted to not-in-combat times. Taken from InCombatLockdown function description. Read description on that page, it says what you can't do to a frame.

While in combat:
  • Programatic modification of macros or bindings is not allowed.
  • Some things can't be done on "Protected" frames, their parents, or any frame they are anchored to. These include, but are not restricted to:
    • Hiding the frame using Hide widget method.
    • Showing the frame using Show widget method.
    • Changing the frame's attributes (custom attributes are used by Blizzard secure templates to set up their behavior).
    • Moving the frame by resetting frame's points or anchors (movements initiated by user are still allowed while in combat).


Originally Posted by LanceDH View Post
Wouldn't it be worth it to create frames in XML and use SetScript with local functions in LUA?
Or does the XML itself cause issues?
Having done it both ways now I must say I find XML to make the base for frames a lot easier to use and keep track of things, and doesn't clutter my LUA files as much.
Sure, XML-templates are quite handy, XML is a markup language for a reason. However, debugging things that happens there isn't cool, and you have to expose quite much, it's never 100% safe T_T I also liked this approach in the past, but it's really leaky...

If you need to use same template many times, instead creating XML-template, create constructor for this widget in Lua. It's easier to debug, it's more flexible and more secure.

P.S. And semlar, as always, explained everything nicely. But I, being a lazy ass, said to almost never use blizz stuff, if they implemented it in Lua. That's almost always true for their OnClick handlers and quite many other functions. I just think it's safest, yet dumbest approach, works for me Ofc there are few handy exceptions, which are safe to use.
__________________

Last edited by lightspark : 10-23-15 at 04:08 PM.
  Reply With Quote
10-23-15, 04:06 PM   #11
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
Originally Posted by semlar View Post
Just wanted to clear something up, since people seem a little confused as to what taint actually is.

Anything an addon does is considered "tainted". Its purpose is to prevent an addon from tricking a blizzard function into executing code that it wouldn't normally be able to do directly, or otherwise influence the result of a protected function.
I always think of taint as Zerg creep.
You send your Zerg into Blizzard's base to do something and it just leaves creep all over the place.
Then Blizzard sends an SCV ro so something, sees the creep, sounds the alarms and the thing gets quarantined.

So what you're saying is, no matter how hard you try to clean your Zerg, it's always going to leave creep behind?
In other words, it's impossible to do what I'm trying to do without blizzard code falling appart, and should just stick with dragging the spells from my own buttons?

Originally Posted by lightspark View Post
However, debugging things that happens there isn't cool
Won't argue with that. Forgot something in your XML? Well I'm just going to tell you something went wrong in your LUA instead.

Edit:
As for everything LUA, this is unfinished and uncleaned code but I think we can all agree it burns people's eyes out.
Lua Code:
  1. local function CreatePlayFrame()
  2.  
  3.     GwentAddon.draggingOver.timer = 0
  4.     local GwentUpdater = CreateFrame("frame", "GwentUpdater", UIParent)
  5.    
  6.     GwentUpdater:SetScript("OnUpdate", function(self,elapsed)
  7.        
  8.        
  9.         if GwentAddon.cards.draggedCard == nil then return end
  10.        
  11.        
  12.         GwentAddon.draggingOver.timer = GwentAddon.draggingOver.timer + elapsed
  13.         if GwentAddon.draggingOver.timer >= 0.05 then
  14.             GwentAddon.draggingOver.list = nil
  15.         GwentAddon.draggingOver.card = nil
  16.         GwentAddon.draggingOver.mouseX, GwentAddon.draggingOver.mouseY = 0, 0
  17.            
  18.             GwentAddon.draggingOver.list, GwentAddon.draggingOver.area = GwentAddon:GetCardlistMouseOver()
  19.             GwentAddon.draggingOver.mouseX, GwentAddon.draggingOver.mouseY = GetCursorPosition()
  20.            
  21.             if GwentAddon.draggingOver.area == nil then
  22.                 GwentAddon.draggingOver.timer = 0
  23.                 return
  24.             end
  25.  
  26.             if GwentAddon:IsRightTypeForArea(GwentAddon.cards.draggedCard, GwentAddon.draggingOver.area.type) and IsSpyArea(GwentAddon.cards.draggedCard.data.ability, GwentAddon.draggingOver.area)    then
  27.                 for k, card in pairs(GwentAddon.draggingOver.list) do
  28.                     if GwentAddon.cards:UpdateCardSpaceing(card, GwentAddon.draggingOver.mouseX, GwentAddon.draggingOver.mouseY) then
  29.                         GwentAddon.draggingOver.card = card
  30.                     end
  31.                 end
  32.                 GwentAddon:PlaceCardsOnFrame(GwentAddon.draggingOver.list, GwentAddon.draggingOver.area)
  33.             end
  34.             GwentAddon.draggingOver.timer = 0
  35.         end
  36.         end)
  37.    
  38.    
  39.    
  40.         local PlayFrame = CreateFrame("frame", addonName.."PlayFrame", UIParent)
  41.     PlayFrame:SetHeight(780)
  42.     PlayFrame:SetWidth(1200)
  43.     -- PlayFrame:SetAlpha(0.7)
  44.     PlayFrame:SetMovable(true)
  45.     PlayFrame:SetPoint("Center", 0, 0)
  46.     PlayFrame:RegisterForDrag("LeftButton")
  47.     PlayFrame:SetScript("OnDragStart", PlayFrame.StartMoving )
  48.     PlayFrame:SetScript("OnDragStop", PlayFrame.StopMovingOrSizing)
  49.     PlayFrame:EnableMouse(true)
  50.     GwentAddon.frameBaseLevel = PlayFrame:GetFrameLevel()
  51.     local fbl = GwentAddon.frameBaseLevel
  52.    
  53.     PlayFrame:SetBackdrop({bgFile = nil,
  54.       edgeFile = "Interface\\ACHIEVEMENTFRAME\\UI-Achievement-WoodBorder",
  55.       tileSize = 32, edgeSize = 64,
  56.       insets = { left = 0, right = 0, top = 0, bottom = 0 }
  57.       })
  58.    
  59.  
  60.     local bSat = 0.6
  61.    
  62.     PlayFrame.left = PlayFrame:CreateTexture(addonName.."PlayFrame_L")
  63.     PlayFrame.left:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Left")
  64.     PlayFrame.left:SetTexCoord(0, 1, 0, .87)
  65.     PlayFrame.left:SetDrawLayer("ARTWORK", 0)
  66.     PlayFrame.left:SetWidth(16)
  67.     PlayFrame.left:SetVertexColor(bSat, bSat, bSat)
  68.     PlayFrame.left:SetPoint("topleft", PlayFrame, "topleft", 15, -25)
  69.     PlayFrame.left:SetPoint("bottomleft", PlayFrame, "bottomleft", 15, 25)
  70.    
  71.     PlayFrame.right = PlayFrame:CreateTexture(addonName.."PlayFrame_R")
  72.     PlayFrame.right:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Left")
  73.     PlayFrame.right:SetTexCoord(1, 0, 0, .87)
  74.     PlayFrame.right:SetDrawLayer("ARTWORK", 0)
  75.     PlayFrame.right:SetWidth(16)
  76.     PlayFrame.right:SetVertexColor(bSat, bSat, bSat)
  77.     PlayFrame.right:SetPoint("topright", PlayFrame, "topright", -15, -25)
  78.     PlayFrame.right:SetPoint("bottomright", PlayFrame, "bottomright", -15, 25)
  79.    
  80.     PlayFrame.bottom = PlayFrame:CreateTexture(addonName.."PlayFrame_B")
  81.     PlayFrame.bottom:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Top")
  82.     PlayFrame.bottom:SetTexCoord(0, .87, 1, 0)
  83.     PlayFrame.bottom:SetDrawLayer("ARTWORK", 0)
  84.     PlayFrame.bottom:SetHeight(16)
  85.     PlayFrame.bottom:SetVertexColor(bSat, bSat, bSat)
  86.     PlayFrame.bottom:SetPoint("bottomleft", PlayFrame, "bottomleft", 30, 15)
  87.     PlayFrame.bottom:SetPoint("bottomright", PlayFrame, "bottomright", -30, 15)
  88.    
  89.     PlayFrame.top = PlayFrame:CreateTexture(addonName.."PlayFrame_T")
  90.     PlayFrame.top:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Top")
  91.     PlayFrame.top:SetTexCoord(0, .87, 0, 1)
  92.     PlayFrame.top:SetDrawLayer("ARTWORK", 0)
  93.     PlayFrame.top:SetHeight(16)
  94.     PlayFrame.top:SetVertexColor(bSat, bSat, bSat)
  95.     PlayFrame.top:SetPoint("topleft", PlayFrame, "topleft", 30, -15)
  96.     PlayFrame.top:SetPoint("topright", PlayFrame, "topright", -30, -15)
  97.    
  98.     PlayFrame.topleft = PlayFrame:CreateTexture(addonName.."PlayFrame_TL")
  99.     PlayFrame.topleft:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Joint")
  100.     PlayFrame.topleft:SetTexCoord(1, 0, 1, 0)
  101.     PlayFrame.topleft:SetDrawLayer("ARTWORK", 2)
  102.     PlayFrame.topleft:SetWidth(32)
  103.     PlayFrame.topleft:SetHeight(32)
  104.     PlayFrame.topleft:SetVertexColor(bSat, bSat, bSat)
  105.     PlayFrame.topleft:SetPoint("topleft", PlayFrame, 10, -10)
  106.    
  107.     PlayFrame.topleftDetail = PlayFrame:CreateTexture(addonName.."PlayFrame_TLDetail")
  108.     PlayFrame.topleftDetail:SetTexture("Interface\\AchievementFrame\\UI-Achievement-WoodBorder-Corner")
  109.     PlayFrame.topleftDetail:SetTexCoord(0, 1, 0, 1)
  110.     PlayFrame.topleftDetail:SetDrawLayer("ARTWORK", 1)
  111.     PlayFrame.topleftDetail:SetWidth(64)
  112.     PlayFrame.topleftDetail:SetHeight(64)
  113.     PlayFrame.topleftDetail:SetPoint("topleft", PlayFrame, 3, -2)
  114.    
  115.     PlayFrame.topright = PlayFrame:CreateTexture(addonName.."PlayFrame_TR")
  116.     PlayFrame.topright:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Joint")
  117.     PlayFrame.topright:SetTexCoord(0, 1, 1, 0)
  118.     PlayFrame.topright:SetDrawLayer("ARTWORK", 2)
  119.     PlayFrame.topright:SetWidth(32)
  120.     PlayFrame.topright:SetHeight(32)
  121.     PlayFrame.topright:SetVertexColor(bSat, bSat, bSat)
  122.     PlayFrame.topright:SetPoint("topright", PlayFrame, -10, -10)
  123.    
  124.     PlayFrame.toprightDetail = PlayFrame:CreateTexture(addonName.."PlayFrame_TRDetail")
  125.     PlayFrame.toprightDetail:SetTexture("Interface\\AchievementFrame\\UI-Achievement-WoodBorder-Corner")
  126.     PlayFrame.toprightDetail:SetTexCoord(1, 0, 0, 1)
  127.     PlayFrame.toprightDetail:SetDrawLayer("ARTWORK", 1)
  128.     PlayFrame.toprightDetail:SetWidth(64)
  129.     PlayFrame.toprightDetail:SetHeight(64)
  130.     PlayFrame.toprightDetail:SetPoint("topright", PlayFrame, -3, -2)
  131.    
  132.     PlayFrame.bottomleft = PlayFrame:CreateTexture(addonName.."PlayFrame_BL")
  133.     PlayFrame.bottomleft:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Joint")
  134.     PlayFrame.bottomleft:SetTexCoord(1, 0, 0, 1)
  135.     PlayFrame.bottomleft:SetDrawLayer("ARTWORK", 2)
  136.     PlayFrame.bottomleft:SetWidth(32)
  137.     PlayFrame.bottomleft:SetHeight(32)
  138.     PlayFrame.bottomleft:SetVertexColor(bSat, bSat, bSat)
  139.     PlayFrame.bottomleft:SetPoint("bottomleft", PlayFrame, 10, 10)
  140.    
  141.     PlayFrame.bottomleftDetail = PlayFrame:CreateTexture(addonName.."PlayFrame_BLDetail")
  142.     PlayFrame.bottomleftDetail:SetTexture("Interface\\AchievementFrame\\UI-Achievement-WoodBorder-Corner")
  143.     PlayFrame.bottomleftDetail:SetTexCoord(0, 1, 1, 0)
  144.     PlayFrame.bottomleftDetail:SetDrawLayer("ARTWORK", 1)
  145.     PlayFrame.bottomleftDetail:SetWidth(64)
  146.     PlayFrame.bottomleftDetail:SetHeight(64)
  147.     PlayFrame.bottomleftDetail:SetPoint("bottomleft", PlayFrame, 3, 2)
  148.    
  149.     PlayFrame.bottomright = PlayFrame:CreateTexture(addonName.."PlayFrame_BR")
  150.     PlayFrame.bottomright:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Joint")
  151.     PlayFrame.bottomright:SetTexCoord(0, 1, 0, 1)
  152.     PlayFrame.bottomright:SetDrawLayer("ARTWORK", 2)
  153.     PlayFrame.bottomright:SetWidth(32)
  154.     PlayFrame.bottomright:SetHeight(32)
  155.     PlayFrame.bottomright:SetVertexColor(bSat, bSat, bSat)
  156.     PlayFrame.bottomright:SetPoint("bottomright", PlayFrame, -10, 10)
  157.    
  158.     PlayFrame.bottomrightDetail = PlayFrame:CreateTexture(addonName.."PlayFrame_BRDetail")
  159.     PlayFrame.bottomrightDetail:SetTexture("Interface\\AchievementFrame\\UI-Achievement-WoodBorder-Corner")
  160.     PlayFrame.bottomrightDetail:SetTexCoord(1, 0, 1, 0)
  161.     PlayFrame.bottomrightDetail:SetDrawLayer("ARTWORK", 1)
  162.     PlayFrame.bottomrightDetail:SetWidth(64)
  163.     PlayFrame.bottomrightDetail:SetHeight(64)
  164.     PlayFrame.bottomrightDetail:SetPoint("bottomright", PlayFrame, -3, 2)
  165.  
  166.     PlayFrame.header = CreateFrame("frame", addonName.."PlayFrameHeader", PlayFrame)
  167.     PlayFrame.header:SetHeight(106)
  168.     PlayFrame.header:SetWidth(726)
  169.     PlayFrame.header:SetPoint("bottom", PlayFrame ,"top", 0, -41)
  170.     PlayFrame.header:Hide()
  171.    
  172.    
  173.     PlayFrame.headerLeft = PlayFrame.header:CreateTexture(addonName.."PlayFrameHeaderLeft")
  174.     PlayFrame.headerLeft:SetTexture("Interface\\AchievementFrame\\UI-Achievement-Header")
  175.     PlayFrame.headerLeft:SetTexCoord(0, 1, 0, 0.4140625)
  176.     PlayFrame.headerLeft:SetDrawLayer("ARTWORK", 0)
  177.     PlayFrame.headerLeft:SetWidth(512)
  178.     PlayFrame.headerLeft:SetHeight(106)
  179.     PlayFrame.headerLeft:SetPoint("bottomleft", PlayFrame.header)
  180.    
  181.     PlayFrame.headerRight = PlayFrame.header:CreateTexture(addonName.."PlayFrameHeaderRight")
  182.     PlayFrame.headerRight:SetTexture("Interface\\AchievementFrame\\UI-Achievement-Header")
  183.     PlayFrame.headerRight:SetTexCoord(0, 0.419921875, 0.4140625, 0.8046875)
  184.     PlayFrame.headerRight:SetDrawLayer("ARTWORK", 0)
  185.     PlayFrame.headerRight:SetWidth(214)
  186.     PlayFrame.headerRight:SetHeight(100)
  187.     PlayFrame.headerRight:SetPoint("bottomright", PlayFrame.header, 0, -6)
  188.    
  189.     PlayFrame.closeButton = CreateFrame("Button", addonName.."PlayFrameCloseButton", PlayFrame, "UIPanelCloseButton")
  190.     PlayFrame.closeButton:SetFrameLevel(fbl + 1)
  191.     -- PlayFrame.closeButton:SetHitRectInsets(4, 4, 4, 4)
  192.     PlayFrame.closeButton:SetPoint("topright", PlayFrame, 3, 4)
  193.     PlayFrame.closeButton:Show()
  194.     PlayFrame.closeButton:SetScript("OnClick",  function()
  195.         PlayFrame:Hide()
  196.     end)
  197.    
  198.     CreateCardTooltip(PlayFrame)
  199.     --CreateWeatherArea(PlayFrame)
  200.     local sidebar = CreateSidebar(PlayFrame)
  201.     local playfield = CreatePlayField(PlayFrame)
  202.    
  203.     PlayFrame.graveyard = CreateFrame("frame", addonName.."Graveyard", PlayFrame)
  204.     PlayFrame.graveyard:SetHeight(GwentAddon.NUM_CARD_HEIGHT*2)
  205.     PlayFrame.graveyard:SetFrameLevel(fbl + 6)
  206.     PlayFrame.graveyard:SetPoint("left", playfield ,"left", 0, 0)
  207.     PlayFrame.graveyard:SetPoint("right", playfield ,"right", 0, 0)
  208.     PlayFrame.graveyard:Hide()
  209.    
  210.     PlayFrame.graveyard.bg = PlayFrame.graveyard:CreateTexture(addonName.."GraveyardBG")
  211.     PlayFrame.graveyard.bg:SetTexture("Interface\\Cooldown\\LoC-ShadowBG") --:SetTexture(TEXTURE_CARD_BG)
  212.     PlayFrame.graveyard.bg:SetDrawLayer("background", 0)
  213.     PlayFrame.graveyard.bg:SetPoint("topleft", PlayFrame.graveyard)
  214.     PlayFrame.graveyard.bg:SetPoint("bottomright", PlayFrame.graveyard)
  215.    
  216.     PlayFrame.graveyard.cardContainer = CreateFrame("frame", addonName.."GraveyardContainer", PlayFrame.graveyard)
  217.     PlayFrame.graveyard.cardContainer:SetFrameLevel(fbl + 7)
  218.     PlayFrame.graveyard.cardContainer:SetPoint("center", PlayFrame.graveyard)
  219.     PlayFrame.graveyard.cardContainer:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  220.     GwentAddon.areas.graveyard = PlayFrame.graveyard
  221.    
  222.    
  223.     PlayFrame.weather = CreateFrame("frame", addonName.."Weather", playfield)
  224.     --GwentAddon.areas.playerHand = PlayFrame.playerHand
  225.     PlayFrame.weather:SetPoint("right", sidebar, "right", -30, 0)
  226.     PlayFrame.weather:SetHeight(GwentAddon.NUM_CARD_HEIGHT+4)
  227.     PlayFrame.weather:SetWidth(GwentAddon.NUM_CARD_WIDTH * 3+4)
  228.    
  229.     GwentAddon.areas.weather = PlayFrame.weather
  230.    
  231.     PlayFrame.weather.bg = PlayFrame.weather:CreateTexture(addonName.."WeatherBG")
  232.     PlayFrame.weather.bg:SetTexture(TEXTURE_CARD_BG)
  233.     PlayFrame.weather.bg:SetDrawLayer("background", 1)
  234.     PlayFrame.weather.bg:SetPoint("topleft", PlayFrame.weather)
  235.     PlayFrame.weather.bg:SetPoint("bottomright", PlayFrame.weather)
  236.    
  237.     GwentAddon:CreateInnerShadow(PlayFrame.weather, 0.5)
  238.      
  239.     PlayFrame.weather.cardContainer = CreateFrame("frame", addonName.."WeatherContainer", PlayFrame.weather)
  240.     PlayFrame.weather.cardContainer:SetPoint("center", PlayFrame.weather)
  241.     PlayFrame.weather.cardContainer:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  242.    
  243.    
  244.     PlayFrame.player = CreatePlayerDisplay(sidebar, 0, -GwentAddon.NUM_CARD_HEIGHT *2, "player")
  245.     PlayFrame.player.nametag:SetText(GetUnitName("player", false))
  246.     GwentAddon.playerLives.texture1 = PlayFrame.player.life1
  247.     GwentAddon.playerLives.texture2 = PlayFrame.player.life2
  248.    
  249.     -- player hand
  250.     PlayFrame.playerHand = CreateFrame("frame", addonName.."playerHand", playfield)
  251.     GwentAddon.areas.playerHand = PlayFrame.playerHand
  252.     PlayFrame.playerHand:SetPoint("bottom", playfield, "bottom", -30, 10)
  253.     PlayFrame.playerHand:SetHeight(GwentAddon.NUM_CARD_HEIGHT+4)
  254.     PlayFrame.playerHand:SetWidth(GwentAddon.NUM_CARD_WIDTH * 10+4)
  255.    
  256.     GwentAddon:CreateInnerShadow(PlayFrame.playerHand, 0.5)
  257.     -- PlayFrame.playerHand:SetBackdrop({bgFile = TEXTURE_CARD_DARKEN,
  258.       -- edgeFile = nil,
  259.       -- tileSize = 0, edgeSize = 16,
  260.       -- insets = { left = 0, right = 0, top = 0, bottom = 0 }
  261.       -- })
  262.      
  263.     PlayFrame.playerHand.cardContainer = CreateFrame("frame", addonName.."playerHandContainer", PlayFrame.playerHand)
  264.     PlayFrame.playerHand.cardContainer:SetPoint("center", PlayFrame.playerHand)
  265.     PlayFrame.playerHand.cardContainer:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  266.      
  267.     PlayFrame.player.deck = CreateFrame("frame", addonName.."baseDeck", playfield)
  268.     PlayFrame.player.deck:SetPoint("bottomright", playfield, "bottomright", -50, 50)
  269.     PlayFrame.player.deck:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  270.     PlayFrame.player.deck:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  271.     GwentAddon:CreateOuterCardShadow(PlayFrame.player.deck)
  272.    
  273.     PlayFrame.player.decktex = PlayFrame.player.deck:CreateTexture(addonName.."baseDeckBack")
  274.     PlayFrame.player.decktex:SetDrawLayer("ARTWORK", 0)
  275.     PlayFrame.player.decktex:SetTexture(TEXTURE_CUSTOM_PATH.."BackTotallyLegit")
  276.     PlayFrame.player.decktex:SetTexCoord(0, 1, 0, 464/512)
  277.     PlayFrame.player.decktex:SetPoint("topleft", PlayFrame.player.deck)
  278.     PlayFrame.player.decktex:SetPoint("bottomright", PlayFrame.player.deck)
  279.    
  280.     PlayFrame.player.leader = CreateFrame("frame", addonName.."playerHero", sidebar)
  281.     PlayFrame.player.leader:SetPoint("bottomleft", sidebar, "bottomleft", 50, 50)
  282.     PlayFrame.player.leader:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  283.     PlayFrame.player.leader:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  284.     PlayFrame.player.leader:SetScript("OnEnter", function(c) GwentAddon:SetCardTooltip(GwentAddon.lists.playDeck.leader) end)
  285.     PlayFrame.player.leader:SetScript("OnLeave", function(c) GwentAddon.playFrame.cardTooltip:Hide() end)
  286.     PlayFrame.player.leader:SetScript("OnMouseUp", function(c)
  287.                     -- only allow during player turn and when the it's not used yet (mainly for chat spam)
  288.                     if GwentAddon.currentState == GwentAddon.states.playerTurn and not GwentAddon.lists.playDeck.leader.used then
  289.                         PlayFrame.player.leaderTex:SetVertexColor(0.5, 0.5, 0.5)
  290.                         SendAddonMessage(addonName, ""..GwentAddon.messages.leader..GwentAddon.lists.playDeck.leader.data.Id.."#1", "whisper" , GwentAddon.challengerName)
  291.                         GwentAddon.lists.playDeck.leader.used = true
  292.                     end
  293.                 end)
  294.    
  295.    
  296.     GwentAddon:CreateOuterCardShadow(PlayFrame.player.leader)
  297.    
  298.     PlayFrame.player.leaderTex = PlayFrame.player.leader:CreateTexture(addonName.."playerHeroTex")
  299.     PlayFrame.player.leaderTex:SetDrawLayer("ARTWORK", 0)
  300.     PlayFrame.player.leaderTex:SetTexture(TEXTURE_CUSTOM_PATH.."BackTotallyLegit")
  301.     PlayFrame.player.leaderTex:SetTexCoord(0, 1, 0, 464/512)
  302.     PlayFrame.player.leaderTex:SetVertexColor(1, 1, 1)
  303.     PlayFrame.player.leaderTex:SetPoint("topleft", PlayFrame.player.leader)
  304.     PlayFrame.player.leaderTex:SetPoint("bottomright", PlayFrame.player.leader)
  305.    
  306.    
  307.     --GwentAddon:CreateInnerShadow(PlayFrame.player.deck, 0.5)
  308.    
  309.     PlayFrame.player.graveyard = CreateFrame("frame", addonName.."playerGY", playfield)
  310.     PlayFrame.player.graveyard:SetPoint("right", PlayFrame.player.deck, "left", -30, 0)
  311.     PlayFrame.player.graveyard:SetFrameLevel(fbl+2)
  312.     PlayFrame.player.graveyard:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  313.     PlayFrame.player.graveyard:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  314.     PlayFrame.player.graveyard:SetScript("OnMouseUp", function(c)
  315.                     if PlayFrame.graveyard:IsShown() then
  316.                         PlayFrame.graveyard:Hide()
  317.                     else
  318.                         PlayFrame.graveyard:Show()
  319.                     end
  320.                 end)
  321.     --GwentAddon:CreateInnerShadow(PlayFrame.player.graveyard, 0.5)
  322.     GwentAddon:CreateOuterCardShadow(PlayFrame.player.graveyard)
  323.    
  324.     -- PlayFrame.player.graveyardCard = CreateFrame("frame", addonName.."playerGYCard", PlayFrame.player.graveyard)
  325.     -- PlayFrame.player.graveyardCard:SetPoint("topleft", PlayFrame.player.graveyard)
  326.     -- PlayFrame.player.graveyardCard:SetPoint("bottomright", PlayFrame.player.graveyard)
  327.     -- PlayFrame.player.graveyardCard:SetFrameLevel(fbl+2)
  328.     -- GwentAddon:CreateOuterCardShadow(PlayFrame.player.graveyardCard)
  329.     -- PlayFrame.player.graveyardCard:Hide()
  330.    
  331.     PlayFrame.player.graveyardTex = PlayFrame.player.graveyard:CreateTexture(addonName.."playerGYTex")
  332.     PlayFrame.player.graveyardTex:SetDrawLayer("ARTWORK", 3)
  333.     PlayFrame.player.graveyardTex:SetTexture(TEXTURE_CUSTOM_PATH.."Graveyard")
  334.     PlayFrame.player.graveyardTex:SetTexCoord(0, 1, 0, 464/512)
  335.     PlayFrame.player.graveyardTex:SetVertexColor(1, 1, 1)
  336.     PlayFrame.player.graveyardTex:SetPoint("topleft", PlayFrame.player.graveyard)
  337.     PlayFrame.player.graveyardTex:SetPoint("bottomright", PlayFrame.player.graveyard)
  338.    
  339.     --PlayFrame.player.graveyardTex:Hide()
  340.      
  341.     -- player siege
  342.     -- PlayFrame.playerSiege = CreateCardArea("playerSiege", PlayFrame, TEXTURE_TYPE_SIEGE, TEXTURE_WEATHER_RAIN)
  343.     -- PlayFrame.playerSiege.type = "siege"
  344.     -- PlayFrame.playerSiege.isEnemy = false
  345.     -- GwentAddon.areas.playerSiege = PlayFrame.playerSiege
  346.     -- PlayFrame.playerSiege:SetPoint("bottom", PlayFrame.playerHand, "top", 0, 6)
  347.     -- player ranged
  348.     -- PlayFrame.playerRanged = CreateCardArea("playerRanged", PlayFrame, TEXTURE_TYPE_RANGED, TEXTURE_WEATHER_FOG)
  349.     -- PlayFrame.playerRanged.type = "ranged"
  350.     -- PlayFrame.playerRanged.isEnemy = false
  351.     -- GwentAddon.areas.playerRanged = PlayFrame.playerRanged
  352.     -- PlayFrame.playerRanged:SetPoint("bottom", PlayFrame.playerSiege, "top", 0, 6)  
  353.     -- player melee
  354.     -- PlayFrame.playerMelee = CreateCardArea("playerMelee", PlayFrame, TEXTURE_TYPE_MELEE, TEXTURE_WEATHER_FROST)
  355.     -- PlayFrame.playerMelee.type = "melee"
  356.     -- PlayFrame.playerMelee.isEnemy = false
  357.     -- GwentAddon.areas.playerMelee = PlayFrame.playerMelee
  358.     -- PlayFrame.playerMelee:SetPoint("bottom", PlayFrame.playerRanged, "top", 0, 6)
  359.    
  360.    
  361.    
  362.     ------------------------------------------------------------------------------
  363.    
  364.     -- player pass button
  365.     PlayFrame.passButton = CreateFrame("button", addonName.."PlayFrame_PassButton", PlayFrame, "UIPanelButtonTemplate")
  366.     PlayFrame.passButton:SetPoint("bottomleft", PlayFrame.player.details, "topleft", 5, 10)
  367.     PlayFrame.passButton:SetFrameLevel(fbl+2)
  368.     PlayFrame.passButton:SetSize(100, 25)
  369.     PlayFrame.passButton:SetText("Pass")
  370.     PlayFrame.passButton:SetScript("OnClick", PassTurn)
  371.    
  372.     -- player discard button
  373.     PlayFrame.discardButton = CreateFrame("button", addonName.."PlayFrame_DiscardButton", PlayFrame, "UIPanelButtonTemplate")
  374.     PlayFrame.discardButton:SetPoint("bottomleft", PlayFrame.playerHand, "bottomright", 15, 10)
  375.     PlayFrame.discardButton:SetFrameLevel(fbl+2)
  376.     PlayFrame.discardButton:SetSize(100, 25)
  377.     PlayFrame.discardButton:SetText("Redraw")
  378.     PlayFrame.discardButton:SetScript("OnClick", function() GwentAddon.cards:RedrawSelectedCards() end)
  379.     PlayFrame.discardButton:Hide()
  380.    
  381.    
  382.    
  383.     -- enemy hand
  384.     PlayFrame.enemyHand = CreateFrame("frame", addonName.."PlayFrame_EnemyHand", playfield)
  385.     GwentAddon.areas.enemyHand = PlayFrame.enemyHand
  386.     PlayFrame.enemyHand:SetPoint("top", playfield, "top", -30, -10)
  387.     PlayFrame.enemyHand:SetHeight(GwentAddon.NUM_CARD_HEIGHT+4)
  388.     PlayFrame.enemyHand:SetWidth(GwentAddon.NUM_CARD_WIDTH * 10+4)
  389.     GwentAddon:CreateInnerShadow(PlayFrame.enemyHand, 0.5)
  390.     -- PlayFrame.enemyHand:SetBackdrop({bgFile = TEXTURE_CARD_DARKEN,
  391.       -- edgeFile = nil,
  392.       -- tileSize = 0, edgeSize = 16,
  393.       -- insets = { left = 0, right = 0, top = 0, bottom = 0 }
  394.       -- })
  395.      
  396.     PlayFrame.enemyHand.cardContainer = CreateFrame("frame", addonName.."EnemyHandContainer", PlayFrame.enemyHand)
  397.     PlayFrame.enemyHand.cardContainer:SetPoint("center", PlayFrame.enemyHand)
  398.     PlayFrame.enemyHand.cardContainer:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  399.    
  400.     PlayFrame.enemy = CreatePlayerDisplay(sidebar, 0, GwentAddon.NUM_CARD_HEIGHT *2, "player")
  401.     GwentAddon.enemyLives.texture1 = PlayFrame.enemy.life1
  402.     GwentAddon.enemyLives.texture2 = PlayFrame.enemy.life2
  403.    
  404.     PlayFrame.enemy.deck = CreateFrame("frame", addonName.."EnemyDeck", playfield)
  405.     PlayFrame.enemy.deck:SetPoint("topright", playfield, "topright", -50, -50)
  406.     PlayFrame.enemy.deck:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  407.     PlayFrame.enemy.deck:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  408.     GwentAddon:CreateOuterCardShadow(PlayFrame.enemy.deck)
  409.    
  410.     PlayFrame.enemy.decktex = PlayFrame.enemy.deck:CreateTexture(addonName.."EnemyDeckBack")
  411.     PlayFrame.enemy.decktex:SetDrawLayer("ARTWORK", 0)
  412.     PlayFrame.enemy.decktex:SetTexture(TEXTURE_CUSTOM_PATH.."BackTotallyLegit")
  413.     PlayFrame.enemy.decktex:SetTexCoord(0, 1, 0, 464/512)
  414.     PlayFrame.enemy.decktex:SetPoint("topleft", PlayFrame.enemy.deck)
  415.     PlayFrame.enemy.decktex:SetPoint("bottomright", PlayFrame.enemy.deck)
  416.    
  417.     PlayFrame.enemy.leader = CreateFrame("frame", addonName.."enemyHero", sidebar)
  418.     PlayFrame.enemy.leader:SetPoint("topleft", sidebar, "topleft", 50, -50)
  419.     PlayFrame.enemy.leader:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  420.     PlayFrame.enemy.leader:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  421.     PlayFrame.enemy.leader:SetScript("OnEnter", function(c) GwentAddon:SetCardTooltip(_enemyLeader) end)
  422.     PlayFrame.enemy.leader:SetScript("OnLeave", function(c) GwentAddon.playFrame.cardTooltip:Hide() end)
  423.    
  424.     GwentAddon:CreateOuterCardShadow(PlayFrame.enemy.leader)
  425.    
  426.     PlayFrame.enemy.leaderTex = PlayFrame.enemy.leader:CreateTexture(addonName.."enemyHeroTex")
  427.     PlayFrame.enemy.leaderTex:SetDrawLayer("ARTWORK", 0)
  428.     PlayFrame.enemy.leaderTex:SetTexture(TEXTURE_CUSTOM_PATH.."BackTotallyLegit")
  429.     PlayFrame.enemy.leaderTex:SetTexCoord(0, 1, 0, 464/512)
  430.     PlayFrame.enemy.leaderTex:SetPoint("topleft", PlayFrame.enemy.leader)
  431.     PlayFrame.enemy.leaderTex:SetPoint("bottomright", PlayFrame.enemy.leader)
  432.    
  433.     PlayFrame.enemy.graveyard = CreateFrame("frame", addonName.."playerGY", playfield)
  434.     PlayFrame.enemy.graveyard:SetPoint("right", PlayFrame.enemy.deck, "left", -30, 0)
  435.     PlayFrame.enemy.graveyard:SetHeight(GwentAddon.NUM_CARD_HEIGHT)
  436.     PlayFrame.enemy.graveyard:SetWidth(GwentAddon.NUM_CARD_WIDTH)
  437.     GwentAddon:CreateOuterCardShadow(PlayFrame.enemy.graveyard)
  438.    
  439.     PlayFrame.enemy.graveyardTex = PlayFrame.enemy.graveyard:CreateTexture(addonName.."playerGYTex")
  440.     PlayFrame.enemy.graveyardTex:SetDrawLayer("ARTWORK", 3)
  441.     PlayFrame.enemy.graveyardTex:SetTexture(TEXTURE_CUSTOM_PATH.."Graveyard")
  442.     PlayFrame.enemy.graveyardTex:SetTexCoord(0, 1, 0, 464/512)
  443.     PlayFrame.enemy.graveyardTex:SetVertexColor(1, 1, 1)
  444.     PlayFrame.enemy.graveyardTex:SetPoint("topleft", PlayFrame.enemy.graveyard)
  445.     PlayFrame.enemy.graveyardTex:SetPoint("bottomright", PlayFrame.enemy.graveyard)
  446.      
  447.  
  448.  
  449.     return PlayFrame
  450. end

Last edited by LanceDH : 10-23-15 at 04:17 PM.
  Reply With Quote
10-23-15, 04:29 PM   #12
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Originally Posted by LanceDH View Post
So what you're saying is, no matter how hard you try to clean your Zerg, it's always going to leave creep behind?
In other words, it's impossible to do what I'm trying to do without blizzard code falling appart, and should just stick with dragging the spells from my own buttons?
If you call SpellBookPrevPageButton_OnClick (or anything that updates what the spellbook is displaying), it will break the spell buttons because of how their click function is implemented.

If you make a macro that /clicks the page buttons, it will behave the same way as if the player had clicked them directly, because it requires a hardware event to function and is considered more secure than your addon calling the function directly.
  Reply With Quote
10-23-15, 07:40 PM   #13
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
Originally Posted by LanceDH View Post
Well fk.
Here I've been programming addons in nothing but LUA thinking I'm not doing it the way I should.
And now I, for once, code one with XML..
XML was the way back in the early versions of WoW before the Widget functions were made available to the Lua side. Since then, Lua has been the preferred method for the ease of debugging and it no longer requires globals to pass between Widgets and the rest of the addon.
__________________
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
10-23-15, 09:41 PM   #14
Gethe
RealUI Developer
 
Gethe's Avatar
Premium Member
Featured
Join Date: Sep 2008
Posts: 942
On the subject of functions in XML, in 6.1 Blizzard added a "mixin" attribute for frames (note: not sure if it's only for frames). This adds the functions from the referenced global table into the attributed frame, allowing you to access those functions (and maybe other data, I have not tested this) via self in both XML and Lua.

Blizzard uses this mostly in the Garrison UI (since 6.2), though it was first used in the Heirloom Collection (in 6.1). They create the global table "GarrisonFollowerList" in Blizzard_GarrisonSharedTemplates.lua, and is used as a mixin for many frames. You can see Blizzard_GarrisonRecruiterUI.xml where they use the OnShow and OnHide methods from the mixin table within the applicable scripts.

Many addons create at least one global to house various bits of data, so this same global can easily be used as a mixin for XML created frames. That said, debugging would still be much easier in Lua so there probably won't be many authors switching to use the new attribute; however, it could be very useful for use in eg. templates where XML is required.
__________________
Knowledge = Power; Be OP


Last edited by Gethe : 10-23-15 at 09:43 PM.
  Reply With Quote
10-23-15, 10:08 PM   #15
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,877
The frame has the attribute: mixin="GarrisonFollowerList"
but the script calls: GarrisonFollowListEditBox_OnTextChanged
which is a standalone global function so I think I'm missing something here or it's a nonsensical bit of coding on Blizzards part.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
10-23-15, 10:22 PM   #16
Gethe
RealUI Developer
 
Gethe's Avatar
Premium Member
Featured
Join Date: Sep 2008
Posts: 942
Originally Posted by Fizzlemizz View Post
The frame has the attribute: mixin="GarrisonFollowerList"
but the script calls: GarrisonFollowListEditBox_OnTextChanged
which is a standalone global function so I think I'm missing something here or it's a nonsensical bit of coding on Blizzards part.
No, that's a script on the edit box not the frame with the mixin.
__________________
Knowledge = Power; Be OP

  Reply With Quote
10-23-15, 10:36 PM   #17
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,877
Originally Posted by Gethe View Post
No, that's a script on the edit box not the frame with the mixin.
Ah, OK, took a second look to see where/how it was implemented for OnShow/OnHide.

I'll have to give it a closer look to understand what it might actually bring to the table. DUF and DART are still largely XML based.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
10-24-15, 05:15 AM   #18
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Originally Posted by LanceDH View Post
Won't argue with that. Forgot something in your XML? Well I'm just going to tell you something went wrong in your LUA instead.

Edit:
As for everything LUA, this is unfinished and uncleaned code but I think we can all agree it burns people's eyes out.
Not exactly practicing what I preach, but you could just localize the widget to reduce bloat:
Lua Code:
  1. local Left = PlayFrame:CreateTexture("$parent_L", "ARTWORK", 0)
  2. Left:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Left")
  3. Left:SetTexCoord(0, 1, 0, .87)
  4. Left:SetWidth(16)
  5. Left:SetVertexColor(bSat, bSat, bSat)
  6. Left:SetPoint("topleft", PlayFrame, 15, -25)
  7. Left:SetPoint("bottomleft", PlayFrame, 15, 25)
  8. PlayFrame.left = Left
  9.  
  10. local Right = PlayFrame:CreateTexture("$parent_R", "ARTWORK", 0)
  11. Right:SetTexture("Interface\\AchievementFrame\\UI-Achievement-MetalBorder-Left")
  12. Right:SetTexCoord(1, 0, 0, .87)
  13. Right:SetWidth(16)
  14. Right:SetVertexColor(bSat, bSat, bSat)
  15. Right:SetPoint("topright", PlayFrame, -15, -25)
  16. Right:SetPoint("bottomright", PlayFrame, -15, 25)
  17. PlayFrame.right = Right

On-topic:
I spent a lot of time trying to get this stuff sorted, because I use a fake cursor in ConsolePort that clicks on buttons and stuff in the interface. Since it had to be robust and refrain from tainting every single thing it touches, it uses the secure action button system along with a clickbutton attribute. The attribute is changed every time the user moves the cursor to another button, thus never calling any specific click scripts and never tainting the buttons it clicks.

The problem you're facing here is whether you can scan the spell book in a secure environment, which can only be done with secure handlers - and they offer a very limited part of the API. Also note that you can't reference non-secure frames in combat in a secure environment, meaning the paging buttons can't be clicked programmatically in combat using that approach. Personally, I think you should think of a different design to solve your problem.
__________________

Last edited by MunkDev : 10-24-15 at 05:29 AM.
  Reply With Quote
10-24-15, 11:13 AM   #19
LanceDH
A Cyclonian
 
LanceDH's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2012
Posts: 41
I decided to just go with dragging from my own buttons instead.
I'm still tainting the other frames but so far they don't seems to cause problems with any protected functions.
If they end up doing so.. I fear the whole point of the addon is lost.

As for the bloat, I just hate wasting half the code in my LUA files on visual stuff.
If I get to making another addon I might try out just putting all visual stuff in its own LUA file and see how I like that.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Need help with SpellBook taint

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