Thread Tools Display Modes
05-19-18, 09:50 AM   #1
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,892
Problem setting XML Spell Button Attributes

I thought this might have been due to the BfA changes, but nope, I can't get this to work in Live.

This is the button frame I am trying to set the attributes for so clicking casts the spell, it is contained within a parent frame, although based on recent code changes I may not need to but that's not the problem
Code:
            <Frame parentKey="ButtonFrame" inherits = "XMP_SpellPageTemplate" setAllPoints = "true" hidden = "true">
                <Scripts>
                    <OnLoad>
                        XMP_ButtonFrame_OnLoad(self)
                    </OnLoad>
                    <OnShow>
                        XMP_ButtonFrame_OnShow(self)
                    </OnShow>
                </Scripts>
            </Frame>
        </Frames>
This is the template for this frame, the buttons will be ultimately hidden by default
Code:
    <Frame name = "XMP_SpellPageTemplate" virtual = "true">
        <Frames> 
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "1" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "2" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "3" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "4" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "5" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "6" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "7" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "8" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "9" />
            <Button inherits = "XMP_SpellButtonTemplate"  hidden = "false" id = "10" />
        </Frames>            
    </Frame>
This is the template for the spell button itself
Code:
    <Button name = "XMP_SpellButtonTemplate" inherits = "XMP_IconTemplate,SecureActionButtonTemplate" virtual = "true">
        <Attributes>
            <Attribute name="type" value="spell" />
            <Attribute name="spell" value="1" type="number" />
        </Attributes>

        <Frames>
            <Cooldown inherits="CooldownFrameTemplate" parentKey="Cooldown" />
        </Frames>

        <Scripts>
            <OnAttributeChanged>
                DEFAULT_CHAT_FRAME:AddMessage(self:GetAttribute("spell"))
            </OnAttributeChanged>
        </Scripts>        
    </Button>
And this is my simple icon only button template, that I would hope with the Secure template would allow setting of spell and icon etc.
Code:
    <Button name = "XMP_IconTemplate" parentArray = "Buttons" virtual = "true">
        <Size x="40" y="40"/>

        <Layers>
            <Layer level="BACKGROUND">
                <Texture parentKey="Icon" setAllPoints = "true"/>
            </Layer>
        </Layers>

        <NormalTexture parentKey="NormalTexture" file="Interface\Buttons\UI-EmptySlot" setAllPoints = "true"/>
        <PushedTexture parentKey ="PushedTexture" file="Interface\Buttons\CheckButtonHilight" alphaMode = "ADD" setAllPoints = "true"/>
        <HighlightTexture parentKey = "HighlightTexture" file="Interface\Buttons\CheckButtonHilight"  alphaMode = "ADD" setAllPoints = "true"/>  
    </Button>
I then cycle through a list of spells to create icons for ( from my spell ID list ). Setting of the Button will be different depending on the planned placement of the buttons. I noticed 4 different layouts in my situation but again, it's not important. v is the data portion of my spell table and i is the spellID index of the table. portalIdx is set to where this list's portalButton array indexes will start, similarly for teleportIdx. The spell table has the correct information as expected on both live and bfa but on both the spell isn't set ( whether I use spellID or spellName ) nor working.
Code:
local Button = v.isPortal and XMP_Main.ButtonFrame.Buttons[portalIdx + v.idx] or XMP_Main.ButtonFrame.Buttons[teleportIdx + v.idx]

                Button:SetAttribute("type","spell")
                Button:SetAttribute("spell",i)
                Button.Icon:SetTexture(v.icon)
                Button:SetName(v.name)
My initial addon that I am upgrading was written in pure lua so this worked fine. I noticed that the original code also has the button derive from ActionButtonTemplate, but trying that instead of my own didn't make a difference. Here's my old lua button creation code that works fine.

Lua Code:
  1. local Button = CreateFrame("Button",buttonName,UIParent,"ActionButtonTemplate,SecureActionButtonTemplate")
  2.     Button:SetWidth(36)
  3.     Button:SetHeight(36)
  4.    
  5.     Button.spellID = spellID
  6.     Button.spellType = spellType
  7.  
  8.     Button:SetAttribute("type","spell")
  9.     Button:SetAttribute("spell",spellName)
  10.  
  11.        -- Cooldown creation here  ( not relevant to this problem )
  12.        
  13.        -- Events Registered for the button here --
  14.  
  15.        -- OnEvent script set here to execute certain functionality, but no attribute setting so immaterial

I even tried setting a button name up via lua in case that was the reason but nada. Any ideas on what I am missing or misunderstood ? Any help appreciated and tanks in advance.
__________________

Last edited by Xrystal : 05-19-18 at 02:18 PM. Reason: Rewording title
  Reply With Quote
05-19-18, 04:54 PM   #2
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,892
Identified Problem ..

Finally figured out the problem is due to the data table not being generated properly. I must be creating it at the wrong time in Beta and not doing it at all in Live. Manually setting the spell ID on the XML allowed the button click to cast the spell but no icon appearing ( likely due to the problem identified ). So back to tracing the execution route to make sure the right values are set at the right time rofl. Considering setting a full table at the start rather than smaller tables when requested.

Lua Code:
  1. function addonData:GenerateSpellsPerMap()
  2.     local factionGroup, factionName = UnitFactionGroup("player")
  3.     local portalIdx = 1
  4.     local teleportIdx = 1
  5.    
  6.     self.MapSpells = {}
  7.     DEFAULT_CHAT_FRAME:AddMessage("GenerateSpellsPerMap 1")  
  8.     for i,v in pairs(self.Spells) do
  9.         local mapInfo = C_Map.GetMapInfo(v.map)
  10.  
  11.         self.MapSpells[v.map] = self.MapSpells[v.map] or {}
  12.  
  13.         local spell = Spell:CreateFromSpellID(i)
  14.  
  15.         DEFAULT_CHAT_FRAME:AddMessage("GenerateSpellsPerMap 2 : " .. i)
  16.  
  17.         spell:ContinueOnSpellLoad(function()
  18.             local spellName = GetSpellInfo(i)
  19.             local id,fileID = GetSpellTexture(i)
  20.             local idx = v.isPortal and portalIdx or teleportIdx
  21.             if v.faction == factionName or "Neutral" then
  22.                 DEFAULT_CHAT_FRAME:AddMessage("Generating data for " .. i .. " : " .. spellName .. " : " .. fileID)  -- Contains values
  23.  
  24.                 addonData.MapSpells[v.map].Spells = addonData.MapSpells[v.map].Spells or {}
  25.                 addonData.MapSpells[v.map].Spells[i] = addonData.MapSpells[v.map].Spells[i] or {}
  26.                 addonData.MapSpells[v.map].Spells[i].isPortal = v.isPortal
  27.                 addonData.MapSpells[v.map].Spells[i].name = spellName
  28.                 addonData.MapSpells[v.map].Spells[i].icon = fileID
  29.                 addonData.MapSpells[v.map].Spells[i].buttonIdx = idx
  30.  
  31.                 if v.isPortal then
  32.                     portalIdx = portalIdx + 1
  33.                 else
  34.                     teleportIdx = teleportIdx + 1
  35.                 end
  36.                 DEFAULT_CHAT_FRAME:AddMessage("GenerateSpellsPerMap 3 : " .. addonData.MapSpells[v.map].Spells[i].name)  -- Contains value
  37.             end
  38.         end)
  39.        
  40.     end
  41.    
  42.     DEFAULT_CHAT_FRAME:AddMessage("GenerateSpellsPerMap 4 : " .. #addonData.MapSpells[addonData.activePage].Spells)  -- attempt to index field error
  43. end

Based on this latest debugging session it appears the new callback feature to ensure spell data is available also resets the data it sets when you leave, even on an addon wide data table. It is starting to look like I will have to use this function for every spell button that I want to set the spell data to. Hard coding the button attribute setting code to a specific value works ( without icon at the moment ) but trying to access the data value it has a hiccough ... more research, but getting closer.
__________________

Last edited by Xrystal : 05-19-18 at 05:55 PM.
  Reply With Quote
05-20-18, 06:01 AM   #3
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,892
Resolved Problem

Whew .. finally .. I suspect I messed up a validation step somewhere as I can't see anything specific being different from before rofl.

This is generated when a map button is clicked
Lua Code:
  1. --[[ Generate list of spells available for the requested map ]]
  2. function addonData:GenerateMapSpellList(mapID, mapButton)
  3.     local portalOffsetIdx = 1
  4.     local teleportOffsetIdx = 1
  5.     local factionGroup, factionName = UnitFactionGroup("player")
  6.     mapButton.Spells = {}
  7.     for spellID,spellData in pairs(addonData.Spells) do
  8.         local rightMap = spellData.map == addonData.activePage
  9.         local rightFaction = spellData.faction == factionName
  10.         local neutralFaction = spellData.faction == "Neutral"
  11.         local isPortal = spellData.isPortal
  12.         if rightMap and (rightFaction or neutralFaction) then
  13.             local index = teleportOffsetIdx
  14.             if isPortal then
  15.                 index = portalOffsetIdx
  16.             end
  17.             mapButton.Spells = mapButton.Spells or {}
  18.             mapButton.Spells[spellID] = index
  19.             if isPortal then
  20.                 portalOffsetIdx = portalOffsetIdx + 1
  21.             else
  22.                 teleportOffsetIdx = teleportOffsetIdx + 1
  23.             end            
  24.         end
  25.     end    
  26. end

This is called when the selected button frame is being displayed ( it is actually one frame with the button attribute values changing and the unused buttons being hidden as required)
Lua Code:
  1. --[[ Set the Buttons up with what is available for the maps ]]
  2. local function XMP_SetMapSpells()
  3.     local teleportIdx = 0
  4.     local portalIdx = 5
  5.     local usedButtons = {}    
  6.    
  7.     for spellID, buttonOffset in pairs(addonData.activeButton.Spells) do
  8.         local isPortal = addonData.Spells[spellID].isPortal
  9.         local index = teleportIdx + buttonOffset
  10.         if isPortal then index = portalIdx + buttonOffset end
  11.         local spellName,textureID = addonData:GetSpellInfo(spellID)
  12.         local button = XMP_Main.ButtonFrame.Buttons[index]
  13.         button:SetAttribute("type","spell")
  14.         button:SetAttribute("spell",spellID)
  15.         button.Icon:SetTexture(textureID)
  16.         button.NormalTexture:SetTexture("")
  17.         table.insert(usedButtons,index)
  18.         currentIdx = currentIdx + 1            
  19.     end
  20.  
  21.     for i = 1,#XMP_Main.ButtonFrame.Buttons do
  22.         if ( tContains(usedButtons,i) ) then
  23.             XMP_Main.ButtonFrame.Buttons[i]:Show()
  24.         else
  25.             XMP_Main.ButtonFrame.Buttons[i]:Hide()
  26.         end
  27.     end
  28. end

And this is what I did to get the spell data loaded in memory
Lua Code:
  1. --[[ Get the required information for the spellID ]]
  2. function addonData:GetSpellInfo(spellID)
  3.    
  4.     local spell = Spell:CreateFromSpellID(spellID)
  5.    
  6.     local spellName = nil
  7.     local textureID = nil
  8.    
  9.     spell:ContinueOnSpellLoad(function()
  10.         spellName = GetSpellInfo(spellID)
  11.         local id,fileID = GetSpellTexture(spellID)
  12.         textureID = fileID
  13.     end)
  14.     return spellName,textureID
  15.    
  16. end

Definitely looking better than my current version of the addon. A few more additions and it should be ready

Hopefully some of this will help other's trying to do something similar and hitting problems
__________________
  Reply With Quote
05-20-18, 06:31 AM   #4
Yukyuk
A Chromatic Dragonspawn
 
Yukyuk's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2015
Posts: 179
As Hannibal Smith used to say “I love it when a plan comes together.”
__________________
Better to fail then never have tried at all.
  Reply With Quote
05-21-18, 07:32 PM   #5
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,892
Alternative Spell Cache Option

After some further testing it appears that the SpellMixin way of working isn't exactly the greatest. It's definitely lightweight but it doesn't work for unknown spells until a second call. However, the following seems to work everytime on a fresh login.

Lua Code:
  1. --[[ Cache the on demand values ]]--
  2. local function CacheSpellData(spellID)
  3.     addonData.SpellCache = {}
  4.     addonData.SpellCache[spellID] = {}
  5.    
  6.     local id,fileID = GetSpellTexture(spellID)
  7.     local name = GetSpellInfo(spellID)
  8.     local isKnown = IsSpellKnown(spellID)
  9.    
  10.     addonData.SpellCache[spellID].TextureID = fileID    
  11.     addonData.SpellCache[spellID].Name = name
  12.     addonData.SpellCache[spellID].isKnown = isKnown
  13.    
  14. end
  15.  
  16. --[[ Get the required information for the spellID, if not cached, cache it ]]
  17. function addonData:GetSpellInfo(spellID)
  18.     if not self.SpellCache[spellID] then
  19.         CacheSpellData(spellID)
  20.     end
  21.     local cache = self.SpellCache[spellID]
  22.     return cache.Name, cache.TextureID, cache.isKnown
  23. end
  24.  
  25. --[[ Monitor for SpellCache Request Results ]]--
  26. local function EventWatcher(self,event,...)
  27.     if event == "PLAYER_ENTERING_WORLD" then
  28.         for i,v in pairs(addonData.Spells) do
  29.             C_Spell.RequestLoadSpellData(i)
  30.         end        
  31.     elseif event == "SPELL_DATA_LOAD_RESULT" then
  32.         local spellID,success = ...
  33.         if success and addonData.Spells[spellID] then
  34.             CacheSpellData(spellID)
  35.         end
  36.     end
  37. end
  38.  
  39. local f = CreateFrame("Frame")
  40. f:RegisterEvent("PLAYER_ENTERING_WORLD")
  41. f:RegisterEvent("SPELL_DATA_LOAD_RESULT")
  42. f:SetScript("OnEvent", EventWatcher)
__________________
  Reply With Quote
05-21-18, 09:36 PM   #6
dssd
A Fallenroot Satyr
Join Date: May 2016
Posts: 25
I don't think you need to worry about caching or the spell loading stuff. The only spell data you have to wait to load is spell descriptions and spell subtexts. If you're not using either of those, then the data is available immediately and always.
  Reply With Quote
05-21-18, 10:11 PM   #7
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,892
Originally Posted by dssd View Post
I don't think you need to worry about caching or the spell loading stuff. The only spell data you have to wait to load is spell descriptions and spell subtexts. If you're not using either of those, then the data is available immediately and always.
Unfortunately not in regards to unknown spells, at least on Battle for Azeroth Beta, that I want to display as 'future' spells in my addon. But the spell icon only becomes available on at least one request for the data, two on unknown unless I use the newer option I pointed out. Maybe due to the alpha/beta status of bfa they are still adding portions to the on demand functionality. I can't remember whether the name retrieval was a problem for unknown spells as well, seeing as both the name and icon were grabbed at the same time they were changed at the same time. Maybe by the time the expansion comes out I'll have to adjust the code again to reflect the newer changes

My Legion version of the addon works fine in that regards (apart from the problem I initially reported here that I realised must have been a programming fault at my end) but the addon needed a rewrite due to the number of portal and teleport spells available. So, with Battle for Azeroth looming I decided to do a full rewrite and that is when I noticed the spell problems.

But in either case, the multiple problems I was pointing out are now solved, either by unknowingly correcting faults at my end or by implementing the new functionality.
__________________
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Problem setting XML Spell Button Attributes

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