diff --git a/libs/libunitscan.lua b/libs/libunitscan.lua new file mode 100644 index 00000000..54d62506 --- /dev/null +++ b/libs/libunitscan.lua @@ -0,0 +1,156 @@ +-- load pfUI environment +setfenv(1, pfUI:GetEnvironment()) + +--[[ libunitscan ]]-- +-- A pfUI library that detects and saves all kind of unit related informations. +-- Such as level, class, elite-state and playertype. Each query causes the library +-- to automatically scan for the target if not already existing. Player-data is +-- persisted within the pfUI_playerDB where the mob data is a throw-away table. +-- The automatic target scanner is only working for vanilla due to client limitations +-- on further expansions. +-- +-- External functions: +-- GetUnitData(name, active) +-- Returns information of the given unitname. Returns nil if no match is found. +-- When nothing is found and the active flag is set, the autoscanner will +-- automatically pick it up and try to fill the missing entry by targetting the unit. +-- +-- class[String] - The class of the unit +-- level[Number] - The level of the unit +-- elite[String] - The elite state of the unit (See UnitClassification()) +-- player[Boolean] - Returns true if unit is a player +-- +-- Internal functions: +-- libcast:AddData(db, name, class, level, elite) +-- Adds unit data to a given db. Where db should be either "players" or "mobs" +-- + +-- return instantly when another librange is already active +if pfUI.api.libunitscan then return end + +local units = { players = {}, mobs = {} } +local queue = { } + +function GetUnitData(name, active) + if units["players"][name] then + local ret = units["players"][name] + return ret.class, ret.level, ret.elite, true + elseif units["mobs"][name] then + local ret = units["mobs"][name] + return ret.class, ret.level, ret.elite, nil + elseif active then + queue[name] = true + libunitscan:Show() + end +end + +local function AddData(db, name, class, level, elite) + if not name then return end + units[db][name] = units[db][name] or {} + units[db][name].class = class or units[db][name].class + units[db][name].level = level or units[db][name].level + units[db][name].elite = elite or units[db][name].elite + queue[name] = nil +end + +local libunitscan = CreateFrame("Frame", "pfUnitScan", UIParent) +libunitscan:RegisterEvent("PLAYER_ENTERING_WORLD") +libunitscan:RegisterEvent("FRIENDLIST_UPDATE") +libunitscan:RegisterEvent("GUILD_ROSTER_UPDATE") +libunitscan:RegisterEvent("RAID_ROSTER_UPDATE") +libunitscan:RegisterEvent("PARTY_MEMBERS_CHANGED") +libunitscan:RegisterEvent("PLAYER_TARGET_CHANGED") +libunitscan:RegisterEvent("WHO_LIST_UPDATE") +libunitscan:RegisterEvent("CHAT_MSG_SYSTEM") +libunitscan:RegisterEvent("UPDATE_MOUSEOVER_UNIT") +libunitscan:SetScript("OnEvent", function() + if event == "PLAYER_ENTERING_WORLD" then + + -- load pfUI_playerDB + units.players = pfUI_playerDB + + -- update own character details + local name = UnitName("player") + local _, class = UnitClass("player") + local level = UnitLevel("player") + AddData("players", name, class, level) + + elseif event == "FRIENDLIST_UPDATE" then + local name, class, level + for i = 1, GetNumFriends() do + name, level, class = GetFriendInfo(i) + class = L["class"][Class] or nil + AddData("players", name, class, level) + end + + elseif event == "GUILD_ROSTER_UPDATE" then + local name, class, level + for i = 1, GetNumGuildMembers() do + name, _, _, level, class = GetGuildRosterInfo(i) + class = L["class"][Class] or nil + AddData("players", name, class, level) + end + + elseif event == "RAID_ROSTER_UPDATE" then + local name, class, SubGroup, level + for i = 1, GetNumRaidMembers() do + name, _, SubGroup, level, class = GetRaidRosterInfo(i) + class = L["class"][Class] or nil + AddData("players", name, class, level) + end + + elseif event == "PARTY_MEMBERS_CHANGED" then + local name, class, level, unit + for i = 1, GetNumPartyMembers() do + unit = "party" .. i + _, class = UnitClass(unit) + name = UnitName(unit) + level = UnitLevel(unit) + AddData("players", name, class, level) + end + + elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then + local name, class, level + for i = 1, GetNumWhoResults() do + name, _, level, _, class, _ = GetWhoInfo(i) + class = L["class"][Class] or nil + AddData("players", name, class, level) + end + + elseif event == "UPDATE_MOUSEOVER_UNIT" or event == "PLAYER_TARGET_CHANGED" then + local scan = event == "PLAYER_TARGET_CHANGED" and "target" or "mouseover" + local name, class, level + if UnitIsPlayer(scan) then + _, class = UnitClass(scan) + level = UnitLevel(scan) + name = UnitName(scan) + AddData("players", name, class, level) + else + _, class = UnitClass(scan) + elite = UnitClassification(scan) + level = UnitLevel(scan) + name = UnitName(scan) + AddData("mobs", name, class, level, elite) + end + end +end) + +-- since TargetByName can only be triggered within vanilla, +-- we can't auto-scan targets on further expansions. +if pfUI.client <= 11200 then + libunitscan:SetScript("OnUpdate", function() + -- don't scan when another unit is in target + if UnitExists("target") or UnitName("target") then return end + + for name in pairs(queue) do + -- try to target the unknown unit + TargetByName(name, true) + ClearTarget() + queue[name] = nil + end + + this:Hide() + end) +end + +pfUI.api.libunitscan = libunitscan diff --git a/modules/chat.lua b/modules/chat.lua index 88be0119..9bb6d958 100644 --- a/modules/chat.lua +++ b/modules/chat.lua @@ -337,13 +337,6 @@ pfUI:RegisterModule("chat", function () pfUI.chat:RegisterEvent("PLAYER_ENTERING_WORLD") pfUI.chat:RegisterEvent("UI_SCALE_CHANGED") - pfUI.chat:RegisterEvent("FRIENDLIST_UPDATE") - pfUI.chat:RegisterEvent("GUILD_ROSTER_UPDATE") - pfUI.chat:RegisterEvent("RAID_ROSTER_UPDATE") - pfUI.chat:RegisterEvent("PARTY_MEMBERS_CHANGED") - pfUI.chat:RegisterEvent("PLAYER_TARGET_CHANGED") - pfUI.chat:RegisterEvent("WHO_LIST_UPDATE") - pfUI.chat:RegisterEvent("CHAT_MSG_SYSTEM") local function ChatOnMouseWheel() if (arg1 > 0) then @@ -586,76 +579,12 @@ pfUI:RegisterModule("chat", function () end pfUI.chat:SetScript("OnEvent", function() - if event == "PLAYER_ENTERING_WORLD" or event == "UI_SCALE_CHANGED" then - pfUI.chat:RefreshChat() - FCF_DockUpdate() - if C.chat.right.enable == "0" and C.chat.right.alwaysshow == "0" then - pfUI.chat.right:Hide() - end - elseif event == "FRIENDLIST_UPDATE" or event == "PLAYER_ENTERING_WORLD" then - local Name, Class, Level - for i = 1, GetNumFriends() do - Name, Level, Class = GetFriendInfo(i) - Class = L["class"][Class] or nil - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - end - elseif event == "GUILD_ROSTER_UPDATE" or event == "PLAYER_ENTERING_WORLD" then - local Name, Class, Level - for i = 1, GetNumGuildMembers() do - Name, _, _, Level, Class = GetGuildRosterInfo(i) - Class = L["class"][Class] or nil - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - end - - elseif event == "RAID_ROSTER_UPDATE" or event == "PLAYER_ENTERING_WORLD" then - local Name, Class, SubGroup, Level - for i = 1, GetNumRaidMembers() do - Name, _, SubGroup, Level, Class = GetRaidRosterInfo(i) - Class = L["class"][Class] or nil - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - end - - elseif event == "PARTY_MEMBERS_CHANGED" or event == "PLAYER_ENTERING_WORLD" then - local Name, Class, Level, Unit - for i = 1, GetNumPartyMembers() do - Unit = "party" .. i - _, Class = UnitClass(Unit) - Name = UnitName(Unit) - Level = UnitLevel(Unit) - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - end - - elseif event == "PLAYER_TARGET_CHANGED" then - local Name, Class, Level - if not UnitIsPlayer("target") or not UnitIsFriend("player", "target") then - return - end - _, Class = UnitClass("target") - Level = UnitLevel("target") - Name = UnitName("target") - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - - elseif event == "WHO_LIST_UPDATE" or event == "CHAT_MSG_SYSTEM" then - local Name, Class, Level - for i = 1, GetNumWhoResults() do - Name, _, Level, _, Class, _ = GetWhoInfo(i) - Class = L["class"][Class] or nil - if Name and Level and Class and pfUI_playerDB then - pfUI_playerDB[Name] = { class = Class, level = Level } - end - end - end - end) + pfUI.chat:RefreshChat() + FCF_DockUpdate() + if C.chat.right.enable == "0" and C.chat.right.alwaysshow == "0" then + pfUI.chat.right:Hide() + end + end) for i=1, NUM_CHAT_WINDOWS do _G["ChatFrame" .. i .. "UpButton"]:Hide() @@ -850,9 +779,8 @@ pfUI:RegisterModule("chat", function () for name in gfind(text, "|Hplayer:(.-)|h") do local color = unknowncolorhex local match = false - -- search player in database - if pfUI_playerDB[name] and pfUI_playerDB[name].class ~= nil then - local class = pfUI_playerDB[name].class + local class = GetUnitData(name) + if class then if class ~= UNKNOWN then color = string.format("%02x%02x%02x", RAID_CLASS_COLORS[class].r * 255, diff --git a/modules/nameplates.lua b/modules/nameplates.lua index b4f2e527..efc2271e 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -3,10 +3,6 @@ pfUI:RegisterModule("nameplates", function () local font_size = C.nameplates.use_unitfonts == "1" and C.global.font_unit_size or C.global.font_size pfUI.nameplates = CreateFrame("Frame", nil, UIParent) - pfUI.nameplates.players = pfUI_playerDB - pfUI.nameplates.mobs = {} - pfUI.nameplates.targets = {} - pfUI.nameplates.scanqueue = {} -- catch all nameplates pfUI.nameplates.scanner = CreateFrame("Frame", "pfNameplateScanner", UIParent) @@ -33,41 +29,6 @@ pfUI:RegisterModule("nameplates", function () end end end - - -- [[ scan missing names ]] - for index, name in pairs(pfUI.nameplates.scanqueue) do - -- remove entry if already scanned - if pfUI.nameplates.targets[name] == "OK" then - table.remove(pfUI.nameplates.scanqueue, index) - else - if UnitName("mouseover") == name then - if UnitIsPlayer("mouseover") then - local _, class = UnitClass("mouseover") - pfUI.nameplates.players[name] = {} - pfUI.nameplates.players[name]["class"] = class - elseif UnitClassification("mouseover") then - local elite = UnitClassification("mouseover") - pfUI.nameplates.mobs[name] = elite - end - pfUI.nameplates.targets[name] = "OK" - - elseif not UnitName("target") then - TargetByName(name, true) - - if UnitIsPlayer("target") then - local _, class = UnitClass("target") - pfUI.nameplates.players[name] = {} - pfUI.nameplates.players[name]["class"] = class - elseif UnitClassification("target") then - local elite = UnitClassification("target") - pfUI.nameplates.mobs[name] = elite - end - pfUI.nameplates.targets[name] = "OK" - - ClearTarget() - end - end - end end) local plate_width = C.nameplates.width + 50 @@ -82,7 +43,6 @@ pfUI:RegisterModule("nameplates", function () this.nameplate.parent = this this.healthbar = this:GetChildren() this.healthbar:SetScript("OnEnter", function() return nil end) - for i, frame in pairs({this:GetRegions()}) do if NAMEPLATE_OBJECTORDER[i] == "_" then frame.Show = function() return end @@ -351,14 +311,12 @@ pfUI:RegisterModule("nameplates", function () end if C.nameplates.players == "1" then - if pfUI.nameplates.targets[this.name:GetText()] == "OK" then - if not pfUI.nameplates.players[this.name:GetText()] then this:Hide() end - end + local _, _, _, uplayer = GetUnitData(this.name:GetText(), true) + if uplayer then this:Hide() end end - this.needNameUpdate = true this.needClassColorUpdate = true - this.needLevelColorUpdate = true + this.needBasicColorUpdate = true this.needEliteUpdate = true this.setup = true @@ -371,24 +329,21 @@ pfUI:RegisterModule("nameplates", function () local healthbar = this.healthbar local border, glow, name, level, levelicon , raidicon, combopoints = this.border, this.glow, this.name, this.level, this.levelicon , this.raidicon, this.combopoints local unitname = name:GetText() + local uclass, ulevel, uelite, uplayer = GetUnitData(unitname, true) - -- add scan entry if not existing - if this.needNameUpdate and unitname ~= UNKNOWN then - if not pfUI.nameplates.targets[unitname] then - table.insert(pfUI.nameplates.scanqueue, unitname) - end - this.needNameUpdate = nil + if unitname == UNKNOWN then + this.needClassColorUpdate = true + this.needBasicColorUpdate = true + this.needEliteUpdate = true end -- hide non-player frames - if C.nameplates.players == "1" and not this.needNameUpdate then - if pfUI.nameplates.targets[unitname] == "OK" then - if not pfUI.nameplates.players[unitname] then this:Hide() end - end + if C.nameplates.players == "1" then + if uplayer then this:Hide() end end -- hide critters - if C.nameplates.critters == "1" and not this.needNameUpdate then + if C.nameplates.critters == "1" then local red, green, blue, _ = healthbar:GetStatusBarColor() local name_val = unitname for i, critter_val in pairs(L["critters"]) do @@ -399,7 +354,7 @@ pfUI:RegisterModule("nameplates", function () end -- hide totems - if C.nameplates.totems == "1" and not this.needNameUpdate then + if C.nameplates.totems == "1" then for totem in pairs(L["totems"]) do if string.find(unitname, totem) then this:Hide() @@ -414,13 +369,13 @@ pfUI:RegisterModule("nameplates", function () end -- level elite indicator - if this.needEliteUpdate and pfUI.nameplates.mobs[unitname] then + if this.needEliteUpdate and uelite then if level:GetText() ~= nil then - if pfUI.nameplates.mobs[unitname] == "elite" then + if uelite == "elite" then level:SetText(level:GetText() .. "+") - elseif pfUI.nameplates.mobs[unitname] == "rareelite" then + elseif uelite == "rareelite" then level:SetText(level:GetText() .. "R+") - elseif pfUI.nameplates.mobs[unitname] == "rare" then + elseif uelite == "rare" then level:SetText(level:GetText() .. "R") end end @@ -428,72 +383,54 @@ pfUI:RegisterModule("nameplates", function () end -- level colors - if this.needLevelColorUpdate then + if this.needBasicColorUpdate then local red, green, blue, _ = level:GetTextColor() if red > 0.99 and green == 0 and blue == 0 then level:SetTextColor(1,0.4,0.2,0.85) elseif red > 0.99 and green > 0.81 and green < 0.82 and blue == 0 then level:SetTextColor(1,1,1,0.85) end - this.needLevelColorUpdate = nil - end - -- healtbar: update colors - local red, green, blue, _ = healthbar:GetStatusBarColor() - if red ~= healthbar.wantR or green ~= healthbar.wantG or blue ~= healthbar.wantB then - -- set reaction color + -- healthbar: update colors + local red, green, blue, _ = healthbar:GetStatusBarColor() -- reaction: 0 enemy ; 1 neutral ; 2 player ; 3 npc if red > 0.9 and green < 0.2 and blue < 0.2 then healthbar.reaction = 0 - healthbar:SetStatusBarColor(.9,.2,.3,0.8) + healthbar.r, healthbar.g, healthbar.b, healthbar.a = .9, .2, .3, .8 elseif red > 0.9 and green > 0.9 and blue < 0.2 then healthbar.reaction = 1 - healthbar:SetStatusBarColor(1,1,.3,0.8) + healthbar.r, healthbar.g, healthbar.b, healthbar.a = 1, 1, .3, .8 elseif ( blue > 0.9 and red == 0 and green == 0 ) then healthbar.reaction = 2 - healthbar:SetStatusBarColor(0.2,0.6,1,0.8) + healthbar.r, healthbar.g, healthbar.b, healthbar.a = .2, .6, 1, .8 elseif red == 0 and green > 0.99 and blue == 0 then healthbar.reaction = 3 - healthbar:SetStatusBarColor(0.6,1,0,0.8) + healthbar.r, healthbar.g, healthbar.b, healthbar.a = .6, 1, 0, .8 end - healthbar.wantR, healthbar.wantG, healthbar.wantB = healthbar:GetStatusBarColor() - this.needClassColorUpdate = true + this.needBasicColorUpdate = nil end -- add class colors - if this.needClassColorUpdate and pfUI.nameplates.targets[unitname] == "OK" then - -- show class names? + if this.needClassColorUpdate and uplayer and uclass then if healthbar.reaction == 0 then - if C.nameplates["enemyclassc"] == "1" - and pfUI.nameplates.players[unitname] - and pfUI.nameplates.players[unitname]["class"] - and RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]] - then - healthbar:SetStatusBarColor( - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].r, - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].g, - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].b, - 0.9) + if C.nameplates["enemyclassc"] == "1" and RAID_CLASS_COLORS[uclass] then + local color = RAID_CLASS_COLORS[uclass] + healthbar.r, healthbar.g, healthbar.b, healthbar.a = color.r, color.g, color.b, .9 end elseif healthbar.reaction == 2 then - if C.nameplates["friendclassc"] == "1" - and pfUI.nameplates.players[unitname] - and pfUI.nameplates.players[unitname]["class"] - and RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]] - then - healthbar:SetStatusBarColor( - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].r, - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].g, - RAID_CLASS_COLORS[pfUI.nameplates.players[unitname]["class"]].b, - 0.9) + if C.nameplates["friendclassc"] == "1" and RAID_CLASS_COLORS[uclass] then + local color = RAID_CLASS_COLORS[uclass] + healthbar.r, healthbar.g, healthbar.b, healthbar.a = color.r, color.g, color.b, .9 end end - healthbar.wantR, healthbar.wantG, healthbar.wantB = healthbar:GetStatusBarColor() this.needClassColorUpdate = nil end + -- color changes are done within the C-API, we need to overwrite on each paint + healthbar:SetStatusBarColor(healthbar.r, healthbar.g, healthbar.b, healthbar.a) + -- name color local red, green, blue, _ = name:GetTextColor() if red > 0.99 and green == 0 and blue == 0 then diff --git a/pfUI.toc b/pfUI.toc index 5b7fe808..cbbf5843 100644 --- a/pfUI.toc +++ b/pfUI.toc @@ -48,6 +48,7 @@ libs\libspell.lua libs\libcast.lua libs\libdebuff.lua libs\librange.lua +libs\libunitscan.lua # skins skins\blizzard\character.lua