Files
coa-leatrix-plus/libs/LibCompat-1.0/Libs/LibGroupTalents-1.0/LibTalentQuery-1.0.lua
T
2023-05-20 18:30:38 +03:00

359 lines
11 KiB
Lua

--[[
Name: LibTalentQuery-1.0
Revision: $Rev: 84 $
Author: Rich Martel (richmartel@gmail.com)
Documentation: http://wowace.com/wiki/LibTalentQuery-1.0
SVN: svn://svn.wowace.com/wow/libtalentquery-1-0/mainline/trunk
Description: Library to help with querying unit talents
Dependancies: LibStub, CallbackHandler-1.0
License: LGPL v2.1
Example Usage:
local TalentQuery = LibStub:GetLibrary("LibTalentQuery-1.0")
TalentQuery.RegisterCallback(self, "TalentQuery_Ready")
local raidTalents = {}
...
TalentQuery:Query(unit)
...
function MyAddon:TalentQuery_Ready(e, name, realm, unitid)
local isnotplayer = not UnitIsUnit(unitid, "player")
local spec = {}
for tab = 1, GetNumTalentTabs(isnotplayer) do
local treename, _, pointsspent = GetTalentTabInfo(tab, isnotplayer)
tinsert(spec, pointsspent)
end
raidTalents[UnitGUID(unitid)] = spec
end
]]
local MAJOR, MINOR = "LibTalentQuery-1.0", 90000 + tonumber(("$Rev: 84 $"):match("(%d+)"))
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
local INSPECTDELAY = 1
local INSPECTTIMEOUT = 5
if not lib.events then
lib.events = LibStub("CallbackHandler-1.0"):New(lib)
end
local validateTrees
local enteredWorld = IsLoggedIn()
local frame = lib.frame
if not frame then
frame = CreateFrame("Frame", MAJOR .. "_Frame")
lib.frame = frame
end
frame:UnregisterAllEvents()
frame:RegisterEvent("INSPECT_TALENT_READY")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PLAYER_LEAVING_WORLD")
frame:RegisterEvent("PLAYER_LOGIN")
frame:SetScript("OnEvent", function(this, event, ...)
return lib[event](lib, ...)
end)
do
local lastUpdateTime = 0
frame:SetScript("OnUpdate", function(this, elapsed)
lastUpdateTime = lastUpdateTime + elapsed
if lastUpdateTime > INSPECTDELAY then
lib:CheckInspectQueue()
lastUpdateTime = 0
end
end)
frame:Hide()
end
local inspectQueue = lib.inspectQueue or {}
lib.inspectQueue = inspectQueue
local garbageQueue = lib.garbageQueue or {} -- Added a second queue to things. Inspects that initially fail are now
lib.garbageQueue = garbageQueue -- thrown into second queue will will be processed once main queue is empty
if next(inspectQueue) then
frame:Show()
end
local UnitIsPlayer = _G.UnitIsPlayer
local UnitName = _G.UnitName
local UnitExists = _G.UnitExists
local UnitGUID = _G.UnitGUID
local GetNumRaidMembers = _G.GetNumRaidMembers
local GetNumPartyMembers = _G.GetNumPartyMembers
local UnitIsVisible = _G.UnitIsVisible
local UnitIsConnected = _G.UnitIsConnected
local UnitCanAttack = _G.UnitCanAttack
local CanInspect = _G.CanInspect
local function UnitFullName(unit)
local name, realm = UnitName(unit)
local namerealm = realm and realm ~= "" and name .. "-" .. realm or name
return namerealm
end
-- GuidToUnitID
local function GuidToUnitID(guid)
local prefix, min, max = "raid", 1, GetNumRaidMembers()
if max == 0 then
prefix, min, max = "party", 0, GetNumPartyMembers()
end
-- Prioritise getting direct units first because other players targets
-- can change between notify and event which can bugger things up
for i = min, max do
local unit = i == 0 and "player" or prefix .. i
if (UnitGUID(unit) == guid) then
return unit
end
end
-- This properly detects target units
if (UnitGUID("target") == guid) then
return "target"
elseif (UnitGUID("focus") == guid) then
return "focus"
elseif (UnitGUID("mouseover") == guid) then
return "mouseover"
end
for i = min, max + 3 do
local unit
if i == 0 then
unit = "player"
elseif i == max + 1 then
unit = "target"
elseif i == max + 2 then
unit = "focus"
elseif i == max + 3 then
unit = "mouseover"
else
unit = prefix .. i
end
if (UnitGUID(unit .. "target") == guid) then
return unit .. "target"
elseif (i <= max and UnitGUID(unit.."pettarget") == guid) then
return unit .. "pettarget"
end
end
return nil
end
-- Query
function lib:Query(unit)
if (UnitLevel(unit) < 10 or UnitName(unit) == UNKNOWN) then
return
end
self.lastQueuedInspectReceived = nil
if UnitIsUnit(unit, "player") then
self.events:Fire("TalentQuery_Ready", UnitName("player"), nil, "player")
else
if type(unit) ~= "string" then
error(("Bad argument #2 to 'Query'. Expected %q, received %q (%s)"):format("string", type(unit), tostring(unit)), 2)
elseif not UnitExists(unit) or not UnitIsPlayer(unit) then
error(("Bad argument #2 to 'Query'. %q is not a valid player unit"):format(tostring(unit)), 2)
elseif not UnitExists(unit) or not UnitIsPlayer(unit) then
error(("Bad argument #2 to 'Query'. %q does not require a server query before reading talents"):format("player"), 2)
else
local name = UnitFullName(unit)
if (not inspectQueue[name]) then
inspectQueue[name] = UnitGUID(unit)
garbageQueue[name] = nil
end
frame:Show()
end
end
end
-- CheckInspectQueue
-- Originally, it would wait until no pending NotifyInspect() were expected, and then do it's own.
-- It was also only bother looking at ready results if it had triggered the Notify for that occasion.
-- For the changes I've done, no assumption is made about which mod is performing NotifyInspect().
-- We note the name, unit, time of any inspects done whether from this queue or any other source,
-- we remove from our queue any we were expecting, and use a seperate event in case extra talent
-- info is any time wanted (opportunistic refreshes etc) - Zeksie, 20th May 2009
function lib:CheckInspectQueue()
if (_G.InspectFrame and _G.InspectFrame:IsShown()) then
return
end
if (not self.lastInspectTime or self.lastInspectTime < GetTime() - INSPECTTIMEOUT) then
self.lastInspectPending = 0
end
if (self.lastInspectPending > 0 or not enteredWorld) then
return
end
if (self.lastQueuedInspectReceived and self.lastQueuedInspectReceived < GetTime() - 60) then
-- No queued results received for a minute, so purge the queue as invalid and move on with our lives
self.lastQueuedInspectReceived = nil
inspectQueue = {}
lib.inspectQueue = inspectQueue
garbageQueue = {}
lib.garbageQueue = garbageQueue
frame:Hide()
return
end
for name,guid in pairs(inspectQueue) do
local unit = GuidToUnitID(guid)
if (not unit) then
inspectQueue[name] = nil
else
if (UnitIsVisible(unit) and UnitIsConnected(unit) and not UnitCanAttack("player", unit) and not UnitCanAttack(unit, "player") and CanInspect(unit) and UnitClass(unit)) then
NotifyInspect(unit)
break
else
garbageQueue[name] = guid -- Not available, throw into secondary queue and continue
inspectQueue[name] = nil
end
end
end
if (not next(inspectQueue)) then
if (next(garbageQueue)) then
-- Retry initially failed inspects
lib.inspectQueue = garbageQueue
inspectQueue = lib.inspectQueue
lib.garbageQueue = {}
garbageQueue = lib.garbageQueue
else
frame:Hide()
end
end
end
-- NotifyInspect
if not lib.NotifyInspect then -- don't hook twice
hooksecurefunc("NotifyInspect", function(...) return lib:NotifyInspect(...) end)
end
function lib:NotifyInspect(unit)
if (not (UnitExists(unit) and UnitIsVisible(unit) and UnitIsConnected(unit) and CheckInteractDistance(unit, 4))) then
return
end
self.lastInspectUnit = unit
self.lastInspectGUID = UnitGUID(unit)
self.lastInspectTime = GetTime()
self.lastInspectName = UnitFullName(unit)
self.lastInspectPending = self.lastInspectPending + 1
local isnotplayer = not UnitIsUnit("player", unit)
self.lastInspectTree = GetTalentTabInfo(1, isnotplayer) -- Talent tree names are available immediately
end
-- Reset
function lib:Reset()
self.lastInspectPending = 0
self.lastInspectUnit = nil
self.lastInspectTime = nil
self.lastInspectName = nil
self.lastInspectGUID = nil
self.lastInspectTree = nil
end
-- INSPECT_TALENT_READY
function lib:INSPECT_TALENT_READY()
self.lastInspectPending = self.lastInspectPending - 1
-- Results are valid only when we have received as many events as we have posted notifies
if (self.lastInspectName and self.lastInspectPending == 0) then
-- Check unit ID is still pointing to same actual unit
if (UnitGUID(self.lastInspectUnit) == self.lastInspectGUID) then
local guid = inspectQueue[self.lastInspectName]
inspectQueue[self.lastInspectName] = nil
local name, realm = strsplit("-", self.lastInspectName)
self.lastQueuedInspectReceived = GetTime()
-- Notify of expected talent results
local isnotplayer = not UnitIsUnit("player", self.lastInspectName)
local group = GetActiveTalentGroup(isnotplayer)
local tree1, _, spent1 = GetTalentTabInfo(1, isnotplayer, nil, group)
if (tree1 ~= self.lastInspectTree) then
-- Expected talent tree name to be the same as it was when we triggered the NotifyInspect()
garbageQueue[self.lastInspectName] = self.lastInspectGUID
self:Reset()
self:CheckInspectQueue()
return
elseif (validateTrees) then
-- Double checking here. Check the tree name matches what we expect for this class
local _, class = UnitClass(self.lastInspectUnit)
if (tree1 ~= validateTrees[class]) then
garbageQueue[self.lastInspectName] = self.lastInspectGUID
self:Reset()
self:CheckInspectQueue()
return
end
end
local tree2, _, spent2 = GetTalentTabInfo(2, isnotplayer, nil, group)
local tree3, _, spent3 = GetTalentTabInfo(3, isnotplayer, nil, group)
if ((spent1 or 0) + (spent2 or 0) + (spent3 or 0) > 0) then
if (guid) then
-- It was in our queue
self.events:Fire("TalentQuery_Ready", name, realm, self.lastInspectUnit)
else
-- Also notify of non-expected ones, as it's entirely useful to refresh them if they're there
-- It is up to the receiving applicating to determine whether they want to receive the information
self.events:Fire("TalentQuery_Ready_Outsider", name, realm, self.lastInspectUnit)
end
else
-- Tree came back with zero points spent, probably an issue while logging in
garbageQueue[self.lastInspectName] = guid
end
end
self:Reset()
self:CheckInspectQueue()
end
end
function lib:PLAYER_ENTERING_WORLD()
-- We can't inspect other's talents until now
-- We just get 0/0/0 back even though we get an INSPECT_TALENT_READY event
enteredWorld = true
end
function lib:PLAYER_LEAVING_WORLD()
enteredWorld = nil
end
function lib:PLAYER_LOGIN()
validateTrees = {
DRUID = "Balance",
PRIEST = "Discipline",
ROGUE = "Assassination",
HUNTER = "Beast Mastery",
WARLOCK = "Affliction",
WARRIOR = "Arms",
DEATHKNIGHT = "Blood",
PALADIN = "Holy",
SHAMAN = "Elemental",
MAGE = "Arcane",
}
if (GetLocale() ~= "enUS" and GetLocale() ~= "enGB") then
-- LibBabble-TalentTree-3.0 only loaded if present and not enUS
local LBT = LibStub("LibBabble-TalentTree-3.0", true)
if (not LBT) then
LoadAddOn("LibBabble-TalentTree-3.0")
LBT = LibStub("LibBabble-TalentTree-3.0", true)
end
LBT = LBT and LBT:GetLookupTable()
if (LBT) then
for class,tree1 in pairs(validateTrees) do
validateTrees[class] = LBT[tree1]
end
else
validateTrees = nil
end
end
self.PLAYER_LOGIN = nil
end
lib:Reset()