Thread Tools Display Modes
Prev Previous Post   Next Post Next
Old 11-30-09, 10:15 PM   #1
Akryn
A Firelord
AddOn Author - Click to view addons
Join Date: Mar 2008
Posts: 479
How to: Read talents from other players

I've seen an unusually high number of requests for information about this lately, so I thought I'd post a general guide.

A couple of points to start out with:

1. Blizzard will hopefully fix the (poorly designed) talent inspection system at some point (nUI's author has reportedly heard that they plan on doing so ...hopefully that will happen sooner than "soon" but who knows...In any case, when that happens it will hopefully make most of this obsolete.

2. Many people can get the work done for them by using a library instead of doing it yourself. Obviously I'm assuming here that you want to do it yourself, though.

3. You will need to be familiar with events and event handlers. See the wiki if you're not:

http://www.wowwiki.com/Handling_events

------------------------------------------------------------------

It's obvious from a quick search on the wowwiki API page that it's possible to read the talents of the player using functions like GetTalentInfo. From that page:

Code:
local numTabs = GetNumTalentTabs();
for t=1, numTabs do
    local numTalents = GetNumTalents(t);
    for i=1, numTalents do
        nameTalent, icon, tier, column, currRank, maxRank= GetTalentInfo(t,i);
        DEFAULT_CHAT_FRAME:AddMessage("- "..nameTalent..": "..currRank.."/"..maxRank);
    end
end
...which will print the name, etc. of every talent, and demonstrates the basic use of everything involved in reading all player talents. For the sake of simplification, let's turn that into:

Code:
local function DoInspect()
    local nameTalent = GetTalentInfo(1,1)
    DEFAULT_CHAT_FRAME:AddMessage(nameTalent)
end
...which will print the name of the first talent in the first tree of the local player.

It looks like we can get the talents from another player by just passing true as the third argument, but note that the player that the talents will be read from is the inspected player, that is the player that was last inspected by the local player. You cannot read the talents of anyone other than the local player, or the most recently inspected player.

The good news is that addons can trigger an inspection of the target or any party/raid member without opening the inspection window, or doing anything that the person playing will notice. You do this with NotifyInspect("unit") -- note that you must first make sure the unit to inspect is within range, etc. Assuming we want to get the talents of our current target, this gives us:

Code:
--NB: This code will not work as expected!
local function DoInspect()
    if CanInspect("target") then
        NotifyInspect("target")
        local nameTalent = GetTalentInfo(1, 1, true)
        DEFAULT_CHAT_FRAME:AddMessage(nameTalent)
    end
end
This, however, is incomplete. The problem is that NotifyInspect doesn't work instantly. You must stop for now and wait for the WoW client to send the request to the server, and get a response. By far the best way to do this is to register for the event INSPECT_TALENT_READY, which fires when a player has been inspected successfully and data can be read for them. Therefore our example is now:


Code:
--NB: This code will still not work as expected!
local f = CreateFrame("frame")
f:SetScript("OnEvent", function(self)
    self:UnregisterEvent("INSPECT_TALENT_READY") --we only care about the first event after we call NotifyInspect
    local nameTalent = GetTalentInfo(1, 1, true)
    DEFAULT_CHAT_FRAME:AddMessage(nameTalent)
end)

local function DoInspect()
    if CanInspect("target") then
         NotifyInspect("target")
         f:RegisterEvent("INSPECT_TALENT_READY")
    end
end
This will at least produce correct results if the player is not running any other addons and doesn't manually inspect anyone; but, now for the bad news: only one player can be inspected at a time, and there is no way to know who the currently inspected player is. Even if you just called NotifyInspect, any other addon or the default UI can call it between your call and the time the server responds, in which case you will never get a response. Even worse, the other addon *will* and it will still come in the form of the INSPECT_TALENT_READY event; meaning that if you call GetTalentInfo now, you will be reading data from whoever the other addon inspected!

To correct for this, we need to make sure that no one has called NotifyInspect since the last time we did. This can be done with hooksecurefunc:
http://www.wowwiki.com/API_hooksecurefunc
...briefly, when you call hooksecurefunc, you specify a function to watch. Every time that funciton gets called by anyone, it will trigger a call to a second function that you specify. In this case we want to watch for anyone calling NotifyInspect and, if anyone does, note that it was called so that we can ignore the result and try again. This gives us:

Code:
--NB: This code will work most of the time but is not perfect yet.

local inspectTainted

hooksecurefunc("NotifyInspect", function() inspectTainted = true end)

local f = CreateFrame("frame")
f:SetScript("OnEvent", function(self)
    self:UnregisterEvent("INSPECT_TALENT_READY")
    if inspectTainted then
        if CanInspect("target") then -- in this case we could just call DoInspect (if it was higher in the file), but i'll write it out for the example
            self:RegisterEvent("INSPECT_TALENT_READY")
            NotifyInspect("target")
            inspectTainted = false
        end
    else
        local nameTalent = GetTalentInfo(1, 1, true)
        DEFAULT_CHAT_FRAME:AddMessage(nameTalent)
    end
end)

local function DoInspect()
    if CanInspect("target") then
        NotifyInspect("target")
        inspectTainted = false
        f:RegisterEvent("INSPECT_TALENT_READY")
    end
end
And there you have it. To be 100% accurate, you'll need some way of dealing with the possibility that the target doesn't exist, or is out of range. Also be wary of latency issues which sometimes cause INSPECT_TALENT_READY to not fire. If you are going to scan many players at once (like a full raid), you will want to set a timeout on each attempt and/or player (as well as timers between inspections, and conflict detection/backoff timers if you want to play nice). If you're just scanning one person, this may not be as important, but you can still use an OnUpdate script to try again if you don't get any result after a certain period of time.

Last edited by Akryn : 11-30-09 at 10:21 PM.
Akryn is offline   Reply With Quote
 

Go BackWoWInterface » Developer Discussions » Tutorials & Other Helpful Info. » How to: Read talents from other players

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