1. I have been trying to get the TargetTextBuffs plugin to work, but there seems to be some recent changes to the API and I'm not sure how to resolve one of them.

    UnitBuff() now appears to report a buffType similar to UnitDebuff(). I solved that error by giving it a variable, but not returning it in the TargetTextBuffs:UpdateBuffs() function.

    The problem I have now is with the buff tooltips. I get the following error in the game.

    Date: 2008-12-28 01:52:22

    ID: 1

    Error occured in: Global

    Count: 1

    Message: [string "GameTooltip:OnEnter"] line 2:

       Usage: GameTooltip:SetUnitBuff("unit", [index] or ["name", "rank"][, "filter"])

    Debug:

       [C]: ?

       [C]: SetUnitBuff()

       [string "*:OnEnter"]:2:

          [string "*:OnEnter"]:1

    AddOns:

      Swatter, v5.1.3910 (SnaggleTooth)

      DevTools, v

      TargetText, v

      TargetTextBuffs, v

      WowLua, v29

      (ck=66)

     

    I have been trying to get the TargetTextBuffs plugin to work, but there seems to be some recent changes to the API and I'm not sure how to resolve one of them.

    UnitBuff() now appears to report a buffType similar to UnitDebuff(). I solved that error by giving it a variable, but not returning it in the TargetTextBuffs:UpdateBuffs() function.

    The problem I have now is with the buff tooltips. I get the following error in the game.

    Date: 2008-12-28 01:52:22

    ID: 1

    Error occured in: Global

    Count: 1

    Message: [string "GameTooltip:OnEnter"] line 2:

       Usage: GameTooltip:SetUnitBuff("unit", [index] or ["name", "rank"][, "filter"])

    Debug:

       [C]: ?

       [C]: SetUnitBuff()

       [string "*:OnEnter"]:2:

          [string "*:OnEnter"]:1

    AddOns:

      Swatter, v5.1.3910 (SnaggleTooth)

      DevTools, v

      TargetText, v

      TargetTextBuffs, v

      WowLua, v29

      (ck=66)

     

    I too had that error.  I gave up and moved on in the book.  I also couldn't get the cooldown part to work.  The image was totaly dark even if it only had 50% of the duration left to go.  I don't know if they are related or not.

    If anybody does find a solution, I'll be interested in hearing it.

    Thanks for the reports. I'm working on a fix for this example now.

    It's been a couple weeks since this last post.... any progress?  I'd really like to finish this example if possible.

    Thank you,

    -- Matthew

    Okay, I am seriously bad at letting anything go so I kept at this one.  I did some digging though the AuraButton_Update() function in BuffFrame.lua and found a lot of differences mostly because they are doing this whole process quite differently, but I noticed that ".id" was nowhere to be found but there was ":SetID()".  Also I noticed that they set the "unit" property.  I've been programming for 20 years but I am seriously new at WoW addons, so I am not entirely sure why this works.  I also am not sure what side effects it might cause.  Anyway, here's the changes I made and it seems to work for me so far... please let me know what you think.

    In TargetTextBuffs:CreateBuffBase in TargetTextBuffs.lua Comment out "frame.id = index" and add the other two lines right below it:

        --frame.id = index

        frame.unit = "target"

        frame:SetID(index)

    I hope this helps someone,

    -- Matthew

    I hope no one is tired of reading my posts yet...

    I think I have solved the cooldown issue too.  The key to the issue is that the return values from UnitBuff() are either wrong or have recently changed.  As I am too new to WoW programming to have any history, I cannot say which for certain.

    On page 228 of the book, the wrapper functions, TargetTextBuffs:UpdatedBuffs() and TargetTextBuffs:UpdateDebuffs() both say they return a value called "timeLeft".  If you look here:

    http://www.wowwiki.com/API_UnitBuff

    You'll see that what is actually returned is "expirationTime".  This is the source of a math error that is sending an incorrect startTime value to "SetCooldown()" in line:

    buff.cooldown:SetCooldown(startTime, duration)

    I hope that's enough for most to fix the issue, if not please reply and I'll be more specific about line numbers.  But do try to figure it out first, that's where the learning is.

    -- Matthew

    Hopefully Matthew can check back and read your response on these changes.  Unfortunately that is his addon and its much easier for him to give you the information than for me to try and figure it out in order to respond.

    Any updates as I'm having issues with this one as well

    Just a FYI,

    my LUA code wasn't working for this chapter so I took the code that is avaible for download of this site. Then changed all the timeLeft to expirationTime and it now working sort of. still getting a error sometime but I can see the buffs now.  If I get a chance I will paste in the error that I'm getting as I'm at work right now.

    I've spent a little time here and there trying to resolve this issue. I found changing the names of the returned objects did not matter, but their positioning did.

    Inside function TargetTextBuffs:UpdateBuffs() I changed return icon, count, duration, timeLeft to

    return icon, count, _, duration, timeLeft

    The next challenge to overcome is an error with this line: buff.cooldown:SetCooldown(startTime, duration) in the TargetTextBuffs.lua: "(*temporary) = "attempt to index field 'cooldown' (a nil value)""

    The following script block:

    if duration and duration > 0 then
      local startTime = GetTime() - (duration - timeLeft)
      buff.cooldown:SetCooldown(startTime, duration)
      buff.cooldown:Show()
    else
      buff.cooldown:Hide()
    end
    

    I am getting closer! I'm really enjoying this book so far! Thanks for taking the time to put it together. =)

    Hrm, cooldowns have changed quite a bit since that version of the book was released. Blizzard changed them into a different kind of object. Is everything working except the cooldowns?

    Yup, everything else works perfect when I comment out the cooldown script. I haven't really messed with debuffs yet, but buffs are good with it commented out.

    I got everything except for the Debuffs working... I have used far too much time trying to fix this so I'm moving on now... This Lua stuff is really messy when you are used to programming in java :S

     print("TargetTextBuffs loaded")
    
     TargetTextBuffs = {}
    
     local buffFrames = {}
     local debuffFrames= {}
    
     local BUFFS_PER_ROW = 8
     local BUFF_SPACING = 2
     local BUFF_TYPE_OFFSET_Y = -7
    
     function TargetTextBuffs:OnLoad(frame)
        frame:RegisterEvent("PLAYER_TARGET_CHANGED")
        frame:RegisterEvent("UNIT_AURA")
     end
    
     function TargetTextBuffs:OnEvent(event, unit)
        if unit == "target" or event == "PLAYER_TARGET_CHANGED" then
            local anchorBuff = self:UpdateBuffs()   -- UpdateBuffs() -> UpdateBuffsBase(function(), buffFrames[], "CreateBuff")
            local anchorDebuff = self:UpdateDebuffs()
    
            if UnitIsFriend("player", "target") then
                self:UpdateAnchors(buffFrames[1], debuffFrames[1], anchorBuff)
            else
                self:UpdateAnchors(debuffFrames[1], buffFrames[1], anchorDebuff)
            end
        end
     end
    
     function TargetTextBuffs:UpdateAnchors(firstFrame, secondFrame, anchorTo)
        if firstFrame and firstFrame:IsShown() then
            firstFrame:SetPoint("TOPLEFT", TargetTextBuffsFrame)
    
            if secondFrame and secondFrame:IsShown() then
                secondFrame:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, 
                    BUFF_TYPE_OFFSET_Y)
            end
        elseif secondFrame and secondFrame:IsShown() then
            secondFrame:SetPoint("TOPLEFT", TargetTextBuffsFrame)
        end
     end
    
     function TargetTextBuffs:CreateBuffBase(index, frames, name, template)
        local frame = CreateFrame("Button", name, TargetTextBuffsFrame, template)
    
        frame.icon = _G[name.."Icon"]
        frame.cooldown = _G[name.."Cooldown"]
        frame.count = _G[name.."Count"]
        frame.border = _G[name.."Border"]
        frame.id = index
    
        --print(frame.count)
    
        local relativeTo, relativePoint, offsetX, offsetY
    
        if index == 1 then
            --default (empty) values
        elseif index % BUFFS_PER_ROW == 1 then
            relativeTo = frames[index - BUFFS_PER_ROW]
            relativePoint = "BOTTOMLEFT"
            offsetX = 0
            offsetY = -BUFF_SPACING
        else
            relativeTo = frames[index - 1]
            relativePoint = "TOPRIGHT"
            offsetX = BUFF_SPACING
            offsetY = 0
        end
    
        frame:SetPoint("TOPLEFT", relativeTo, relativePoint, offsetX, offsetY)
    
        frames[index] = frame
        return frame
     end
    
     function TargetTextBuffs:CreateBuff(index)
        return self:CreateBuffBase(
            index,
            buffFrames,
            "TargetTextBuff"..index,
            "TargetBuffFrameTemplate"
        )
     end
    
     function TargetTextBuffs:CreateDebuff(index)
        return self:CreateBuffBase(
            index,
            debuffFrames,
            "TargetTextDebuff"..index,
            "TargetTextBuffsDebuffTemplate"
        )
     end
    
     function TargetTextBuffs:UpdateBuffsBase(BuffFunc, frames, method)
        local index = 1
    
        local icon, count, duration, expires = BuffFunc(index) -- [line 150](1)
        local anchorBuff
    
        while icon do
            local buff = frames[index] or self[method](self, index) -- (buffFrames[1] empty at first) -> CreateBuff(1) -> CreateBuffBase(1, buffFrames, TargetTextBuff1, TargetBuffFrameTemplate)
            buff:Show()
    
            if index % BUFFS_PER_ROW == 1 then
                anchorBuff = buff
            end
    
            buff.icon:SetTexture(icon)
    
            if count > 1 then
                buff.count:SetText(count)
                buff.count:Show()
            else
                buff.count:Hide()
            end
    
            if duration and duration > 0 then
                local startTime = expires - duration
                buff.cooldown:SetCooldown(startTime, duration)
                buff.cooldown:Show()
            else
                buff.cooldown:Hide()
            end
    
            if buff.border then
                local color
    
                if debuffType then
                    color = DebuffTypeColor[debuffType]
                else
                    color = DebuffTypeColor["none"]
                end
    
                buff.border:SetVertexColor(color.r, color.g, color.b)
            end
    
            index = index + 1
            icon, count, duration, expires = BuffFunc(index)
        end
    
        for i = index, #frames do
            frames[i]:Hide()
        end
    
        return anchorBuff
     end
    
     function TargetTextBuffs:UpdateBuffs()
        return self:UpdateBuffsBase(
            function(index)
                local icon, count, dispelType, duration, expires = select(3, UnitBuff("target", index))
                return icon, count, duration, expires
            end,
            buffFrames,
            "CreateBuff"
        )
     end
    
     function TargetTextBuffs:UpdateDebuffs()
        return self:UpdateBuffsBase(
            function(index)
                local icon, count, debuffType, duration, expires =
                    select(3, UnitAura("target", index, "HARMFUL"))
                return icon, count, duration, expires
            end,
            debuffFrames,
            "CreateDebuff"
        )
     end
    

    PS: Sorry don't know how the 'code' tag is supposed to look, the button just makes `` which doesn't make a code blog

    Alright, well there isn't really a question here, so I'm not sure how to respond. You can create a code block by highlighting your code and pressing the code button.

    Mr. Whitehead: I had 3 problems getting this to work in-game. The first problem (as I encountered them), was the object "TargetBuffButtonFrame" did not exist. I was not sure whether to use "BuffButtonTemplate" or "TargetBuffFrameTemplate". I chose "TargetBuffFrameTemplae" because the code using it seemed closer to the code here. (line 134) I made the same change in the XML. The second problem was that I was getting an error occasionally indicating that "duration" was a string. Also, no cooldown information was appearing on the buffs. Investigation showed the need for a placeholder in the UnitBuff return, to get the numbers in the right place. I discovered in the process that the "timeLeft" variable actually contained the end time for the buff, and corrected the equation to reflect this. (lines 59 and 101) The third problem is not fixed here. This occurs when you place your cursor inside a buff, as you would to see its tooltip. An error occurs in the addon. I have not included what I did for this issue, because it involves new code, rather than changes to existing code, and because all I did was stop the error from occurring. I thought you should choose the solution you wished. -- LUA file

     TargetTextBuffs = {}
    
     local buffFrames = {}
     local debuffFrames = {}
    
     local BUFFS_PER_ROW = 8
     local BUFF_SPACING = 2
     local BUFF_TYPE_OFFSET_Y = -7
    
     function TargetTextBuffs:OnLoad(frame)
       frame:RegisterEvent("PLAYER_TARGET_CHANGED")
       frame:RegisterEvent("UNIT_AURA")
     end
    
     function TargetTextBuffs:OnEvent(event, unit)
       if unit == "target" or event == "PLAYER_TARGET_CHANGED" then
         local anchorBuff = self:UpdateBuffs()
         local anchorDebuff = self:UpdateDebuffs()
    
         if UnitIsFriend("player", "target") then
           -- Buffs on top
           self:UpdateAnchors(buffFrames[1], debuffFrames[1], anchorBuff)
         else
           -- Debuffs on top
           self:UpdateAnchors(debuffFrames[1], buffFrames[1], anchorDebuff)
         end
       end
     end
    
     function TargetTextBuffs:UpdateAnchors(firstFrame, secondFrame, anchorTo)
       if firstFrame and firstFrame:IsShown() then
         firstFrame:SetPoint("TOPLEFT", TargetTextBuffsFrame)
         if secondFrame and secondFrame:IsShown() then
           secondFrame:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, BUFF_TYPE_OFFSET_Y)
         end
       elseif secondFrame and secondFrame:IsShown() then
         secondFrame:SetPoint("TOPLEFT", TargetTextBuffsFrame)
       end
     end
    
     function TargetTextBuffs:UpdateBuffs()
       return self:UpdateBuffsBase(
         function(index)
           -- protects against possible future additions to the returns
           local icon, count, _, duration, timeLeft = select(3, UnitBuff("target", index)) -- added placeholder (jg)
     ``      return icon, count, duration, timeLeft
         end,
         buffFrames, 
         "CreateBuff"
       )
     end
    
     function TargetTextBuffs:UpdateDebuffs()
       return self:UpdateBuffsBase(
         function(index)
           local icon, count, debuffType, duration, timeLeft = select(3, UnitDebuff("target", index))
           return icon, count, duration, timeLeft, debuffType
         end,
         debuffFrames,
         "CreateDebuff"
       )
     end
    
     function TargetTextBuffs:UpdateBuffsBase(BuffFunc, frames, method)
       local index = 1
       local icon, count, duration, timeLeft, debuffType = BuffFunc(index)
       local anchorBuff
    
       while icon do
         local buff = frames[index] or self[method](self, index)
         buff:Show()
    
         if index % BUFFS_PER_ROW == 1 then
           anchorBuff = buff
         end
    
         buff.icon:SetTexture(icon)
    
         if count > 1 then
           buff.count:SetText(count)
           buff.count:Show()
         else
           buff.count:Hide()
         end
    
         if duration and duration > 0 then           -- the inital problem was the buff type (Magic etc) was showing up in duration
             local startTime = timeLeft - duration   -- investigating this, I discovered timeLeft was actually the end time.
             buff.cooldown:SetCooldown(startTime, duration)  -- Now it correctly shows cooldown. (jg)
             buff.cooldown:Show()
         else
             buff.cooldown:Hide()
         end
    
         if buff.border then
           local color
           if debuffType then
             color = DebuffTypeColor[debuffType]
           else
             color = DebuffTypeColor["none"]
           end
           buff.border:SetVertexColor(color.r, color.g, color.b)
         end
    
         index = index + 1
         icon, count, duration, timeLeft, debuffType = BuffFunc(index)
       end
    
       for i = index, #frames do
         frames[i]:Hide()
       end
    
       return anchorBuff
     end
    
     function TargetTextBuffs:CreateBuff(index)
       return self:CreateBuffBase(
         index,
         buffFrames,
         "TargetTextBuff"..index,
         "TargetBuffFrameTemplate"               -- I changed this because TargetBuffButtonFrame does not exist. (jg)
       )
     end
    
     function TargetTextBuffs:CreateDebuff(index)
       return self:CreateBuffBase(
         index,
         debuffFrames,
         "TargetTextDebuff"..index,
         "TargetTextBuffsDebuffTemplate"
       )
     end
    
     function TargetTextBuffs:CreateBuffBase(index, frames, name, template)
       local frame = CreateFrame("Button", name, TargetTextBuffsFrame, template)
    
       frame.icon = _G[name.."Icon"]
       frame.cooldown = _G[name.."Cooldown"]
       frame.count = _G[name.."Count"]
       frame.border = _G[name.."Border"]
       frame.id = index
    
       local relativeTo, relativePoint, offsetX, offsetY
       if index == 1 then
         -- Use default (empty) values
       elseif index % BUFFS_PER_ROW == 1 then
         relativeTo = frames[index - BUFFS_PER_ROW]
         relativePoint = "BOTTOMLEFT"
         offsetX = 0
         offsetY = - BUFF_SPACING
       else
         relativeTo = frames[index - 1]
         relativePoint = "TOPRIGHT"
         offsetX = BUFF_SPACING
         offsetY = 0
       end
       frame:SetPoint("TOPLEFT", relativeTo, relativePoint, offsetX, offsetY)
    
       frames[index] = frame
       return frame
     end
    
     <Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
     ..\UI.xsd">
    
        <!-- Frame for events and anchoring the buttons -->
        <Frame name="TargetTextBuffsFrame" parent="TargetTextFrame">
            <Size x="1" y="1"/>
            <Anchors>
                <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT">
                    <Offset x="8" y="0"/>
                </Anchor>
            </Anchors>
            <Scripts>
                <OnLoad>
                    TargetTextBuffs:OnLoad(self)
                </OnLoad>
                <OnEvent>
                    TargetTextBuffs:OnEvent(event, ...)
                </OnEvent>
            </Scripts>
        </Frame>
         <!-- Changed the line below to correct inherit link -->
        <Frame name="TargetTextBuffsDebuffTemplate" inherits="TargetDebuffFrameTemplate" virtual="true">
            <Size x="21" y="21"/>
            <Scripts>
                <OnLoad>
                    local border = _G[self:GetName().."Border"]
                    border:SetWidth(23)
                    border:SetHeight(23)
                </OnLoad>
            </Scripts>
        </Frame>
    
     </Ui>
    

    Thank you. Unfortunately it seems the author who worked on this addon is no longer helping to provide support for it. I'm glad you were able to get your issues sorted, and hope this will help me in the future if another reader has similar problems.