WoWInterface SVN hoverinfo

[/] [trunk/] [HoverInfo/] [HoverInfo.lua] - Rev 4

Compare with Previous | Blame | View Log

-- Constants
local CACHE_SIZE = 0            -- Max. # of characters in cache
local CACHE_EXPIRE = 60         -- Cached entries will expire after this # of seconds
local INSPECT_DELAY = 0.1       -- Time to hover over unit before NotifyInspect() is called
local TXT_LOADING = "Loading items..."

LoadAddOn("Blizzard_InspectUI")

--[[
        This is a workaround for a bug in blizzard's UI code, that popups
        an error when inspecting a unit when the actual inspection window
        is not open. Supposidely fixed in 4.1

        Alternatively, install InspectFix:
        http://wow.curse.com/downloads/wow-addons/details/inspectfix.aspx
]]
    local function patch()
        local oldfunc1=InspectPaperDollFrame_SetLevel;
        InspectPaperDollFrame_SetLevel=function(...)
            if InspectFrame.unit and UnitExists(InspectFrame.unit) then
                return oldfunc1(...);
            end
        end;
     
        local oldfunc2=InspectGuildFrame_Update;
        InspectGuildFrame_Update=function(...)
            if InspectFrame.unit and UnitExists(InspectFrame.unit) then
                return oldfunc2(...);
            end
        end;
    end
     
    if IsAddOnLoaded("Blizzard_InspectUI") then
        patch();
    else
        local eframe=CreateFrame("Frame");
        eframe:RegisterEvent("ADDON_LOADED");
        eframe:SetScript("OnEvent",function(self,event,arg)
            if event=="ADDON_LOADED" and arg=="Blizzard_InspectUI" then
                self:UnregisterEvent(event);
                patch();
            end
        end);
    end
--[[
        End of workaround. When fixed, delete this whole block.
]]


local Cache = {new = function()
        local self = {}
        self.data = {}
        self.remove = function(guid)
                for i=1,#self.data do
                        if guid==self.data[i].guid then
                                tremove(self.data,i)
                                break
                        end
                end
        end
        self.removeExpired = function()
                local exptime=GetTime()-CACHE_EXPIRE
                local i=1
                while i<=#self.data do
                        if self.data[i].time<exptime then
                                tremove(self.data,i)
                        else
                                i=i+1
                        end
                end
        end
        self.limitSize = function()
                while #self.data>CACHE_SIZE do
                        tremove(self.data,1)
                end
        end
        self.add = function(guid,ilvl,missing,spec1,spec2)
                -- If unit is already in cache, remove it
                self.remove(guid)

                -- Add unit to end of cache
                self.data[#self.data+1] = {
                        ["guid"] = guid,
                        ["ilvl"] = ilvl,
                        ["missing"] = missing,
                        ["spec1"] = spec1,
                        ["spec2"] = spec2,
                        ["time"] = GetTime()
                }
        end
        self.find = function(guid)
                self.removeExpired()
                if CACHE_SIZE > 0 then
                        for i=1,#self.data do
                                if guid==self.data[i].guid then
                                        return self.data[i].ilvl, self.data[i].missing, self.data[i].spec1, self.data[i].spec2
                                end
                        end
                end
                return nil
        end
        return self
end}



local function levelColor(iLevel)
        local max=360
        local H=1-iLevel/max
        if H<0 then
                H=0
        end

        -- Now we have the hue, convert this to rgb, then convert that to hex

        local R,G,B=0,0,0
        var_h=H*6
        if var_h==6 then
                var_h=0
        end
        var_i=math.floor(var_h)
        var_3=var_h-var_i

        if var_i==0 then
                R=1
                G=var_3
        elseif var_i==1 then
                R=1-var_3
                G=1
        elseif var_i==2 then
                G=1
                B=var_3
        elseif var_i==3 then
                G=1-var_3
                B=1 
        elseif var_i==4 then
                R=var_3
                B=1
        else
                R=1
                B=1-var_3
        end
        return format("%02x%02x%02x", (R*255), (G*255), (B*255) );
end

local function getItemLevel(target)
        local total=0
        local count=0
        local missing=0
        for i=1,18 do
                -- Skip shirt slot
                if i~=4 then
                        -- If no item is equiped in a slot, count it as ilvl 0
                        -- An exception would be offhand when a two-hander
                        -- is equipped.

                        local level=0
                        local item=GetInventoryItemLink(target,i)
                        if item ~= nil then
                                _,_,_,level,_,_,_,_,loc = GetItemInfo(item)
                                if loc=="INVTYPE_2HWEAPON" then
                                        count=count-1
                                        missing=missing-1
                                end
                        else
                                missing=missing+1
                        end
                        total=total+level
                        count=count+1
                end
        end
        if count>0 then
                return math.floor(total/count,1),missing
        else
                return 0,missing
        end
end

local function specInfo(group)
        local treename = "None"
        local primary = GetPrimaryTalentTree(true,false,group)
        if primary then
                _,treename = GetTalentTabInfo(primary,true,false,group)
        end
        local _,_,_,_,p1 = GetTalentTabInfo(1,true,false,group)
        local _,_,_,_,p2 = GetTalentTabInfo(2,true,false,group)
        local _,_,_,_,p3 = GetTalentTabInfo(3,true,false,group)
        return format( "%s (%d/%d/%d)",treename or "none",p1 or 0,p2 or 0,p3 or 0 )
end

c = CreateFrame("Frame") 
c.TimeSinceLastUpdate = 0
c.currentGUID = nil     -- GUID of unit that triggered NotifyInspect() call
c.currentUnit = nil
c.currentTime = nil
c.inspectCache = Cache:new()
c.inspectTime = nil
-- c:RegisterEvent("INSPECT_READY")

c:SetScript("OnEvent",function(self,event,guid,...)
        if event=="INSPECT_READY" then
                self:UnregisterEvent("INSPECT_READY")

                -- Now, because this fires prematurely 9 out of 10 times,
                -- and itemdata is using the streaming system, we start
                -- a timer, that will keep checking if we have everything yet.
                -- Only when we do, we can inspect the unit...
                -- This is handled in the OnUpdate event!
                -- Don't you just love how useful some events are? This event
                -- should be renamed INSPECT_KINDASORTA_READY_BUTNOTREALLY
                -- It would be just as useful, but at least have an accurate
                -- name...

                local active = GetActiveTalentGroup(true, false)
                if active~=1 then offspec=1 else offspec=2 end
                local spec1 = specInfo(active)
                local spec2 = specInfo(offspec)

                GameTooltip:AddLine( "Current Spec: "..spec1 )
                GameTooltip:AddLine( "Alt. Spec: "..spec2 )
                GameTooltip:AddLine( TXT_LOADING )
                GameTooltip:Show()

                self.inspectTime = GetTime() + 0.2
        end
end)

c:SetScript("OnUpdate",function(self,elapsed)
        local now = GetTime()

        if self.inspectTime and now >= self.inspectTime then
                local _,unit = GameTooltip:GetUnit()
                if unit then
                        guid = UnitGUID(unit)
                        if guid==self.currentGUID then
                                -- Determine if iteminfo is complete yet
                                local done = true
                                for i=1,18 do
                                        if GetInventoryItemTexture(unit, i) and not GetInventoryItemLink(unit, i) then
                                                -- GetTexture always return stuff but GetLink is not.
                                                done = false
                                                break
                                        end
                                end


                                if done then
                                        ile,missing=getItemLevel(unit)
                                        local offspec=2
                                        local active = GetActiveTalentGroup(true, false)
                                        if active~=1 then offspec=1 end
                                        local spec1 = specInfo(active)
                                        local spec2 = specInfo(offspec)
                                        ClearInspectPlayer()

                                        self.inspectCache.add(guid,ile,missing,spec1,spec2)

                                        -- Replace the "Loading iLvls..." text with item level info
                                        for i=2, GameTooltip:NumLines() do
                                                if (_G["GameTooltipTextLeft"..i]:GetText() or "") == TXT_LOADING then
                                                        _G["GameTooltipTextLeft"..i]:SetFormattedText("%s", getTTilevel(ile,missing) );
                                                end
                                        end
                                        GameTooltip:Show()

                                        self.inspectTime = nil
                                else
                                        -- Nope try again in 0.2 secs
                                        -- print( "Data still loading..." )
                                        -- Would be nice if we can do some animation
                                        -- in the tooltip...
                                        self.inspectTime = now + 0.2
                                end

                        else
                                -- Current tooltip is for different unit
                                -- print( "Aborting inspection: Data is for differnt unit than target" )
                                self.inspectTime = nil
                        end
                else
                        -- No tooltip, no unit....
                        -- print( "Aborting inspection: No unit targeted" )
                        self.inspectTime = nil
                end
        end


        if (c.currentTime == nil) or ((now - c.currentTime) < INSPECT_DELAY) then return end
        
        -- If current tooltip is still for the requested unit,
        -- fire a notify request for it
        local _,unit = GameTooltip:GetUnit()
        if unit and (unit==self.currentUnit) and UnitIsPlayer(unit) and CheckInteractDistance(unit,1) and CanInspect(unit) then
                guid = UnitGUID(unit)
                if guid==self.currentGUID then
                        -- it SEEMS that calling this several times reduces
                        -- missing items. Still happens though:(

                        self:RegisterEvent("INSPECT_READY")
                        NotifyInspect(unit)
                end
        else
                -- print ("Cannot inspect unit")
        end
        c.currentTime = nil
end)

function getTTilevel(ilevel,missing)
        local color=levelColor(ilevel)

        local s = "iLevel: |cff"..color..tostring(ilevel).."|r"
        if missing>0 then
                s = s.." (|cffff0000"..missing.."|r items missing)"
        end
        return s
end

GameTooltip:HookScript("OnTooltipSetUnit",function(self,...)
        local _,unit = self:GetUnit();
        if (not unit) then
                local mFocus = GetMouseFocus()
                if (mFocus) and (mFocus.unit) then
                        unit=mFocus.unit
                end
        end

        if unit and UnitIsPlayer(unit) then
                guid = UnitGUID(unit)
                ilvl,missing,spec1,spec2 = c.inspectCache.find(guid)
                if ilvl then
                        GameTooltip:AddLine( "Current Spec: "..spec1 )
                        GameTooltip:AddLine( "Alt. Spec: "..spec2 )
                        GameTooltip:AddLine( getTTilevel(ilvl,missing) )
                        GameTooltip:Show()
                else
                        c.currentGUID = guid
                        c.currentUnit = unit
                        c.currentTime = GetTime()
                end
        end
end);

Compare with Previous | Blame