diff --git a/ElvUI/Core/API.lua b/ElvUI/Core/API.lua index 4148083..1a64403 100644 --- a/ElvUI/Core/API.lua +++ b/ElvUI/Core/API.lua @@ -1,5 +1,5 @@ local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB - +local ElvUF = E.oUF --Lua functions local _G = _G local wipe, date = wipe, date @@ -210,6 +210,20 @@ function E:ExitVehicleShowFrames(_, unit) end end +function E:GetThreatStatusColor(status, nothreat) + local color = ElvUF.colors.threat[status] + if color then + return color.r, color.g, color.b, color.a or 1 + elseif nothreat then + if status == -1 then -- how or why? + return 1, 1, 1, 1 + else + return .7, .7, .7, 1 + end + end +end + + function E:RequestBGInfo() RequestBattlefieldScoreData() end diff --git a/ElvUI/Core/Cooldowns.lua b/ElvUI/Core/Cooldowns.lua index 33bbede..60979ff 100644 --- a/ElvUI/Core/Cooldowns.lua +++ b/ElvUI/Core/Cooldowns.lua @@ -162,11 +162,13 @@ function E:Cooldown_Options(timer, db, parent) end end -function E:CreateCooldownTimer(parent) - local timer = CreateFrame("Frame", parent:GetName() and "$parentTimer" or nil, parent) +function E:CreateCooldownTimer(parent, displayParent) + local timer = CreateFrame("Frame", parent:GetName() and parent:GetName().."Timer" or nil, displayParent) + hooksecurefunc(parent, "Hide", function() timer:Hide() end) + hooksecurefunc(parent, "Show", function() timer:Show() end) timer:SetFrameLevel(parent:GetFrameLevel() + 1) timer:Hide() - timer:SetAllPoints() + timer:SetAllPoints(parent) timer.parent = parent parent.timer = timer @@ -199,9 +201,9 @@ function E:CreateCooldownTimer(parent) end E.RegisteredCooldowns = {} -function E:OnSetCooldown(start, duration) +function E:OnSetCooldown(start, duration, recursive) if (not self.forceDisabled) and (start and duration) and (duration > MIN_DURATION) then - local timer = self.timer or E:CreateCooldownTimer(self) + local timer = self.timer or E:CreateCooldownTimer(self, self:GetParent()) timer.start = start timer.duration = duration timer.endTime = start + duration @@ -213,14 +215,22 @@ function E:OnSetCooldown(start, duration) end end -function E:RegisterCooldown(cooldown) +function E:RegisterCooldown(cooldown, module) if not cooldown.isHooked then hooksecurefunc(cooldown, "SetCooldown", E.OnSetCooldown) + if cooldown:GetParent().isNamePlate then + cooldown.Show = cooldown.Hide + cooldown:Hide() + end cooldown.isHooked = true end if not cooldown.isRegisteredCooldown then - local module = (cooldown.CooldownOverride or "global") + if module then + cooldown.CooldownOverride = module + else + module = (cooldown.CooldownOverride or "global") + end if not E.RegisteredCooldowns[module] then E.RegisteredCooldowns[module] = {} end tinsert(E.RegisteredCooldowns[module], cooldown) @@ -228,23 +238,26 @@ function E:RegisterCooldown(cooldown) end end -function E:GetCooldownColors(db) - if not db then db = E.db.cooldown end -- just incase someone calls this without a first arg use the global - local c13 = E:RGBToHex(db.hhmmColorIndicator.r, db.hhmmColorIndicator.g, db.hhmmColorIndicator.b) -- color for timers that are soon to expire - local c12 = E:RGBToHex(db.mmssColorIndicator.r, db.mmssColorIndicator.g, db.mmssColorIndicator.b) -- color for timers that are soon to expire - local c11 = E:RGBToHex(db.expireIndicator.r, db.expireIndicator.g, db.expireIndicator.b) -- color for timers that are soon to expire - local c10 = E:RGBToHex(db.secondsIndicator.r, db.secondsIndicator.g, db.secondsIndicator.b) -- color for timers that have seconds remaining - local c9 = E:RGBToHex(db.minutesIndicator.r, db.minutesIndicator.g, db.minutesIndicator.b) -- color for timers that have minutes remaining - local c8 = E:RGBToHex(db.hoursIndicator.r, db.hoursIndicator.g, db.hoursIndicator.b) -- color for timers that have hours remaining - local c7 = E:RGBToHex(db.daysIndicator.r, db.daysIndicator.g, db.daysIndicator.b) -- color for timers that have days remaining - local c6 = db.hhmmColor -- HH:MM color - local c5 = db.mmssColor -- MM:SS color - local c4 = db.expiringColor -- color for timers that are soon to expire - local c3 = db.secondsColor -- color for timers that have seconds remaining - local c2 = db.minutesColor -- color for timers that have minutes remaining - local c1 = db.hoursColor -- color for timers that have hours remaining - local c0 = db.daysColor -- color for timers that have days remaining - return c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13 +do + local function HEX(db) return E:RGBToHex(db.r, db.g, db.b) end + function E:GetCooldownColors(db) + if not db then db = E.db.cooldown end -- just incase someone calls this without a first arg use the global + return + db.daysColor, + db.hoursColor, + db.minutesColor, + db.secondsColor, + db.expiringColor, + db.mmssColor, + db.hhmmColor, + HEX(db.daysIndicator), + HEX(db.hoursIndicator), + HEX(db.minutesIndicator), + HEX(db.secondsIndicator), + HEX(db.expireIndicator), + HEX(db.mmssColorIndicator), + HEX(db.hhmmColorIndicator) + end end function E:UpdateCooldownOverride(module) diff --git a/ElvUI/Core/Core.lua b/ElvUI/Core/Core.lua index 71db42d..bc6a2d9 100644 --- a/ElvUI/Core/Core.lua +++ b/ElvUI/Core/Core.lua @@ -60,6 +60,7 @@ E.myname = UnitName("player") E.myrealm = GetRealmName() E.mynameRealm = format('%s - %s', E.myname, E.myrealm) -- contains spaces/dashes in realm (for profile keys) E.version = GetAddOnMetadata("ElvUI", "Version") +E.versionNum = tonumber(E.version) E.wowpatch, E.wowbuild = GetBuildInfo() E.wowbuild = tonumber(E.wowbuild) E.resolution = GetCVar("gxResolution") @@ -1134,6 +1135,14 @@ function E:DBConversions() if E.private.skins.blizzard.greeting ~= nil then E.private.skins.blizzard.greeting = nil end + + -- VERSION 7.0 -- nameplate overhaul + if not E.db.version or E.db.version < 7 then + -- wipe nameplates + E:CopyTable(self.db.nameplates, P.nameplates) + end + + E.db.version = E.versionNum end function E:RefreshModulesDB() @@ -1143,6 +1152,37 @@ function E:RefreshModulesDB() UnitFrames.db = self.db.unitframe --new ref end +do + -- Shamelessly taken from AceDB-3.0 and stripped down by Simpy + function E:CopyDefaults(dest, src) + for k, v in pairs(src) do + if type(v) == 'table' then + if not rawget(dest, k) then rawset(dest, k, {}) end + if type(dest[k]) == 'table' then E:CopyDefaults(dest[k], v) end + elseif rawget(dest, k) == nil then + rawset(dest, k, v) + end + end + + return dest + end + + function E:RemoveDefaults(db, defaults) + setmetatable(db, nil) + + for k, v in pairs(defaults) do + if type(v) == 'table' and type(db[k]) == 'table' then + E:RemoveDefaults(db[k], v) + if next(db[k]) == nil then db[k] = nil end + elseif db[k] == defaults[k] then + db[k] = nil + end + end + + return db + end +end + function E:Initialize() twipe(self.db) twipe(self.global) diff --git a/ElvUI/Core/Distributor.lua b/ElvUI/Core/Distributor.lua index f4657eb..3a0b0a8 100644 --- a/ElvUI/Core/Distributor.lua +++ b/ElvUI/Core/Distributor.lua @@ -8,8 +8,8 @@ local tonumber, type, gsub, pairs, pcall, loadstring = tonumber, type, gsub, pai local len, format, split, strmatch = strlen, format, strsplit, strmatch local CreateFrame = CreateFrame -local IsInRaid, UnitInRaid = IsInRaid, UnitInRaid -local IsInGroup, UnitInParty = IsInGroup, UnitInParty +local IsInRaid, UnitInRaid = GenerateClosure(C_Player, C_Player.IsInRaid, C_Player), UnitInRaid +local IsInGroup, UnitInParty = GenerateClosure(C_Player, C_Player.IsInGroup, C_Player), UnitInParty -- local LE_PARTY_CATEGORY_HOME = LE_PARTY_CATEGORY_HOME -- local LE_PARTY_CATEGORY_INSTANCE = LE_PARTY_CATEGORY_INSTANCE local ACCEPT, CANCEL, YES, NO = ACCEPT, CANCEL, YES, NO diff --git a/ElvUI/Core/Tags.lua b/ElvUI/Core/Tags.lua index 2c54622..dc830b9 100644 --- a/ElvUI/Core/Tags.lua +++ b/ElvUI/Core/Tags.lua @@ -1,788 +1,1448 @@ -local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') local ElvUF = E.oUF +local Tags = ElvUF.Tags +local LCS = E.Libs.LCS +local RangeCheck = E.Libs.RangeCheck local Translit = E.Libs.Translit -local translitMark = "!" +local translitMark = '!' ---Lua functions -local select = select -local tonumber = tonumber -local find = string.find -local floor = math.floor -local format = string.format -local gmatch = gmatch -local gsub = gsub -local match = string.match -local utf8lower = string.utf8lower -local utf8sub = string.utf8sub ---WoW API / Variables +local _G = _G +local next, type, gmatch, gsub, format = next, type, gmatch, gsub, format +local ipairs, pairs, wipe, floor, ceil = ipairs, pairs, wipe, floor, ceil +local strfind, strmatch, strlower, strsplit = strfind, strmatch, strlower, strsplit +local utf8lower, utf8sub, utf8len = string.utf8lower, string.utf8sub, string.utf8len + +local GetCurrentTitle = GetCurrentTitle +local GetCVarBool = GetCVarBool local GetGuildInfo = GetGuildInfo local GetInstanceInfo = GetInstanceInfo -local GetNumPartyMembers = GetNumPartyMembers +local GetNumGroupMembers = GenerateClosure(C_Player.GetNumGroupMembers, C_Player) +local GetPVPRankInfo = GetPVPRankInfo local GetPVPTimer = GetPVPTimer -local GetQuestGreenRange = GetQuestGreenRange -local GetThreatStatusColor = GetThreatStatusColor +local GetQuestDifficultyColor = GetQuestDifficultyColor +local GetRaidRosterInfo = GetRaidRosterInfo +local GetRuneCooldown = GetRuneCooldown local GetTime = GetTime +local GetTitleName = GetTitleName local GetUnitSpeed = GetUnitSpeed +local HasPetUI = HasPetUI +local IsInGroup = GenerateClosure(C_Player.IsInGroup, C_Player) +local IsInRaid = IsInRaid +local IsInInstance = IsInInstance +local UnitAffectingCombat = UnitAffectingCombat local UnitClass = UnitClass local UnitClassification = UnitClassification local UnitDetailedThreatSituation = UnitDetailedThreatSituation local UnitExists = UnitExists +local UnitFactionGroup = UnitFactionGroup local UnitGUID = UnitGUID local UnitHealth = UnitHealth local UnitHealthMax = UnitHealthMax local UnitIsAFK = UnitIsAFK local UnitIsConnected = UnitIsConnected -local UnitIsDND = UnitIsDND local UnitIsDead = UnitIsDead local UnitIsDeadOrGhost = UnitIsDeadOrGhost +local UnitIsDND = UnitIsDND +local UnitIsFeignDeath = UnitIsFeignDeath local UnitIsGhost = UnitIsGhost +local UnitIsPlayer = UnitIsPlayer local UnitIsPVP = UnitIsPVP local UnitIsPVPFreeForAll = UnitIsPVPFreeForAll -local UnitIsPlayer = UnitIsPlayer local UnitIsUnit = UnitIsUnit local UnitLevel = UnitLevel -local UnitName = UnitName -local UnitPVPName = UnitPVPName local UnitPower = UnitPower local UnitPowerMax = UnitPowerMax local UnitPowerType = UnitPowerType +local UnitPVPName = UnitPVPName +local UnitPVPRank = UnitPVPRank local UnitReaction = UnitReaction -local DEFAULT_AFK_MESSAGE = DEFAULT_AFK_MESSAGE -local SPELL_POWER_MANA = SPELL_POWER_MANA +local UnitSex = UnitSex + +local C_QuestLog_GetTitleForQuestID = GetTitleForQuestID +local C_QuestLog_GetQuestDifficultyLevel = function(questID) + local questIndex = GetQuestLogIndexByID(questID) + if questIndex then + return select(2, GetQuestLogTitle(questIndex)) + end +end + +local LEVEL = strlower(LEVEL) local PVP = PVP +-- GLOBALS: ElvUF, Hex, _TAGS, _COLORS + +local RefreshNewTags -- will turn true at EOF +function E:AddTag(tagName, eventsOrSeconds, func) + + if type(eventsOrSeconds) == 'number' then + Tags.OnUpdateThrottle[tagName] = eventsOrSeconds + else + Tags.Events[tagName] = eventsOrSeconds + end + + Tags.Methods[tagName] = func + + if RefreshNewTags then + Tags:RefreshEvents(tagName) + Tags:RefreshMethods(tagName) + end +end + +function E:CallTag(tag, ...) + local func = ElvUF.Tags.Methods[tag] + if func then + return func(...) + end +end + +function E:TagUpdateRate(second) + Tags:SetEventUpdateTimer(second) +end + +--Expose local functions for plugins onto this table +E.TagFunctions = {} + ------------------------------------------------------------------------ --- Tags +-- Tag Extra Events ------------------------------------------------------------------------ -local function abbrev(name) - local letters, lastWord = "", match(name, ".+%s(.+)$") +Tags.SharedEvents.INSTANCE_ENCOUNTER_ENGAGE_UNIT = true +Tags.SharedEvents.PLAYER_GUILD_UPDATE = true +Tags.SharedEvents.PLAYER_TALENT_UPDATE = true +Tags.SharedEvents.QUEST_LOG_UPDATE = true + +------------------------------------------------------------------------ +-- Tag Functions +------------------------------------------------------------------------ + +local function UnitEffectiveLevel(unit) + return UnitLevel(unit) +end +E.TagFunctions.UnitEffectiveLevel = UnitEffectiveLevel + +local function UnitName(unit) + local name, realm = _G.UnitName(unit) + + if realm and realm ~= '' then + return name, realm + else + return name + end +end +E.TagFunctions.UnitName = UnitName + +local function Abbrev(name) + local letters, lastWord = '', strmatch(name, '.+%s(.+)$') if lastWord then - for word in gmatch(name, ".-%s") do - local firstLetter = utf8sub(gsub(word, "^[%s%p]*", ""), 1, 1) + for word in gmatch(name, '.-%s') do + local firstLetter = utf8sub(gsub(word, '^[%s%p]*', ''), 1, 1) if firstLetter ~= utf8lower(firstLetter) then - letters = format("%s%s. ", letters, firstLetter) + letters = format('%s%s. ', letters, firstLetter) end end - name = format("%s%s", letters, lastWord) + name = format('%s%s', letters, lastWord) end return name end +E.TagFunctions.Abbrev = Abbrev -local unitStatus = {} -local function getUnitStatus(unit) - if not UnitIsPlayer(unit) then return end - local guid = UnitGUID(unit) - if UnitIsAFK(unit) then - if not unitStatus[guid] or unitStatus[guid] and unitStatus[guid][1] ~= "AFK" then - unitStatus[guid] = {"AFK", GetTime()} +local ClassPowers = { + DEATHKNIGHT = 5, +} + +local function GetClassPower(Class) + local min, max, r, g, b + + -- try special powers or combo points + local barType = ClassPowers[Class] + if barType then + local dk = Class == 'DEATHKNIGHT' + min = (dk and 0) or UnitPower('player', barType) + max = (dk and 6) or UnitPowerMax('player', barType) + + if dk then + for i = 1, max do + local _, _, runeReady = GetRuneCooldown(i) + if runeReady then + min = min + 1 + end + end end - elseif UnitIsDND(unit) then - if not unitStatus[guid] or unitStatus[guid] and unitStatus[guid][1] ~= "DND" then - unitStatus[guid] = {"DND", GetTime()} - end - elseif UnitIsDead(unit) or UnitIsGhost(unit) then - if not unitStatus[guid] or unitStatus[guid] and unitStatus[guid][1] ~= "Dead" then - unitStatus[guid] = {"Dead", GetTime()} - end - elseif not UnitIsConnected(unit) then - if not unitStatus[guid] or unitStatus[guid] and unitStatus[guid][1] ~= "Offline" then - unitStatus[guid] = {"Offline", GetTime()} + + if min > 0 then + local power = ElvUF.colors.ClassBars[Class] + local color = (dk and ElvUF.colors.class.DEATHKNIGHT) or power + r, g, b = color.r, color.g, color.b end else - unitStatus[guid] = nil + min = GetComboPoints("player", "target") + max = 5 + + if min > 0 then + local combo = ElvUF.colors.ComboPoints + local c1, c2, c3 = combo[1], combo[2], combo[3] + r, g, b = ElvUF:ColorGradient(min, max, c1.r, c1.g, c1.b, c2.r, c2.g, c2.b, c3.r, c3.g, c3.b) + end end - if unitStatus[guid] ~= nil then - local status = unitStatus[guid][1] - local timer = GetTime() - unitStatus[guid][2] - local mins = floor(timer / 60) - local secs = floor(timer - (mins * 60)) - return format("%s (%01.f:%02.f)", status, mins, secs) - else - return nil - end + return min or 0, max or 0, r or 1, g or 1, b or 1 end +E.TagFunctions.GetClassPower = GetClassPower +------------------------------------------------------------------------ +-- Looping +------------------------------------------------------------------------ -ElvUF.Tags.Events["afk"] = "PLAYER_FLAGS_CHANGED" -ElvUF.Tags.Methods["afk"] = function(unit) - local isAFK = UnitIsAFK(unit) - if isAFK then - return format("|cffFFFFFF[|r|cffFF0000%s|r|cFFFFFFFF]|r", DEFAULT_AFK_MESSAGE) - else - return nil - end -end +for _, vars in ipairs({'',':min',':max'}) do + E:AddTag(format('range%s', vars), 0.1, function(unit) + if UnitIsConnected(unit) and not UnitIsUnit(unit, 'player') then + local minRange, maxRange = RangeCheck:GetRange(unit, true) -ElvUF.Tags.Events["healthcolor"] = "UNIT_HEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED" -ElvUF.Tags.Methods["healthcolor"] = function(unit) - if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then - return Hex(0.84, 0.75, 0.65) - else - local r, g, b = ElvUF:ColorGradient(UnitHealth(unit), UnitHealthMax(unit), 0.69, 0.31, 0.31, 0.65, 0.63, 0.35, 0.33, 0.59, 0.33) - return Hex(r, g, b) - end -end - -ElvUF.Tags.Events["name:abbrev"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["name:abbrev"] = function(unit) - local name = UnitName(unit) - - if name and find(name, "%s") then - name = abbrev(name) - end - - return name ~= nil and name or "" + if vars == ':min' then + if minRange then + return format('%d', minRange) + end + elseif vars == ':max' then + if maxRange then + return format('%d', maxRange) + end + elseif minRange or maxRange then + return format('%s - %s', minRange or '??', maxRange or '??') + end + end + end) end for textFormat in pairs(E.GetFormattedTextStyles) do - local tagTextFormat = strlower(gsub(textFormat, "_", "-")) - ElvUF.Tags.Events[format("health:%s", tagTextFormat)] = "UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED" - ElvUF.Tags.Methods[format("health:%s", tagTextFormat)] = function(unit) + local tagFormat = strlower(gsub(textFormat, '_', '-')) + E:AddTag(format('health:%s', tagFormat), 'UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED', function(unit) local status = UnitIsDead(unit) and L["Dead"] or UnitIsGhost(unit) and L["Ghost"] or not UnitIsConnected(unit) and L["Offline"] if status then return status else return E:GetFormattedText(textFormat, UnitHealth(unit), UnitHealthMax(unit)) end - end + end) - ElvUF.Tags.Events[format("health:%s-nostatus", tagTextFormat)] = "UNIT_HEALTH_FREQUENT UNIT_MAXHEALTH" - ElvUF.Tags.Methods[format("health:%s-nostatus", tagTextFormat)] = function(unit) + E:AddTag(format('health:%s-nostatus', tagFormat), 'UNIT_HEALTH UNIT_MAXHEALTH', function(unit) return E:GetFormattedText(textFormat, UnitHealth(unit), UnitHealthMax(unit)) - end + end) - ElvUF.Tags.Events[format("power:%s", tagTextFormat)] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER" - ElvUF.Tags.Methods[format("power:%s", tagTextFormat)] = function(unit) - local pType = UnitPowerType(unit) - local min = UnitPower(unit, pType) - - if min == 0 and tagTextFormat ~= "deficit" then - return "" - else - return E:GetFormattedText(textFormat, UnitPower(unit, pType), UnitPowerMax(unit, pType)) + E:AddTag(format('power:%s', tagFormat), 'UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER', function(unit) + local powerType = UnitPowerType(unit) + local min = UnitPower(unit, powerType) + if min ~= 0 then + return E:GetFormattedText(textFormat, min, UnitPowerMax(unit, powerType)) end - end + end) - ElvUF.Tags.Events[format("mana:%s", tagTextFormat)] = "UNIT_MANA UNIT_MAXMANA" - ElvUF.Tags.Methods[format("mana:%s", tagTextFormat)] = function(unit) - local min = UnitPower(unit, SPELL_POWER_MANA) - - if min == 0 and tagTextFormat ~= "deficit" then - return "" - else - return E:GetFormattedText(textFormat, UnitPower(unit, SPELL_POWER_MANA), UnitPowerMax(unit, SPELL_POWER_MANA)) + E:AddTag(format('mana:%s', tagFormat), 'UNIT_MAXPOWER UNIT_DISPLAYPOWER UNIT_MANA', function(unit) + local min = UnitPower(unit, 0) + if min ~= 0 then + return E:GetFormattedText(textFormat, min, UnitPowerMax(unit, 0)) end - end - - ElvUF.Tags.Events[format("energy:%s", tagTextFormat)] = "UNIT_MAXENERGY UNIT_ENERGY_FREQUENT" - ElvUF.Tags.Methods[format("energy:%s", tagTextFormat)] = function(unit) - local min = UnitPower(unit, 3) - - if min == 0 and tagTextFormat ~= "deficit" then - return "" - else - return E:GetFormattedText(textFormat, UnitPower(unit, 3), UnitPowerMax(unit, 3)) - end - end - - ElvUF.Tags.Events[format("rage:%s", tagTextFormat)] = "UNIT_MAXRAGE UNIT_RAGE_FREQUENT" - ElvUF.Tags.Methods[format("rage:%s", tagTextFormat)] = function(unit) + end) + + E:AddTag(format('rage:%s', tagFormat), 'UNIT_MAXRAGE UNIT_DISPLAYPOWER UNIT_RAGE', function(unit) local min = UnitPower(unit, 1) - - if min == 0 and tagTextFormat ~= "deficit" then - return "" - else - return E:GetFormattedText(textFormat, UnitPower(unit, 1), UnitPowerMax(unit, 1)) + if min ~= 0 then + return E:GetFormattedText(textFormat, min, UnitPowerMax(unit, 1)) end + end) + + E:AddTag(format('energy:%s', tagFormat), 'UNIT_MAXENERGY UNIT_DISPLAYPOWER UNIT_ENERGY', function(unit) + local min = UnitPower(unit, 3) + if min ~= 0 then + return E:GetFormattedText(textFormat, min, UnitPowerMax(unit, 3)) + end + end) + + if tagFormat ~= 'percent' then + E:AddTag(format('health:%s:shortvalue', tagFormat), 'UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED', function(unit) + local status = not UnitIsFeignDeath(unit) and UnitIsDead(unit) and L["Dead"] or UnitIsGhost(unit) and L["Ghost"] or not UnitIsConnected(unit) and L["Offline"] + if status then + return status + else + local min, max = UnitHealth(unit), UnitHealthMax(unit) + return E:GetFormattedText(textFormat, min, max, nil, true) + end + end) + + E:AddTag(format('health:%s-nostatus:shortvalue', tagFormat), 'UNIT_HEALTH UNIT_MAXHEALTH', function(unit) + local min, max = UnitHealth(unit), UnitHealthMax(unit) + return E:GetFormattedText(textFormat, min, max, nil, true) + end) + + E:AddTag(format('power:%s:shortvalue', tagFormat), 'UNIT_DISPLAYPOWER UNIT_POWER_FREQUENT UNIT_MAXPOWER', function(unit) + local powerType = UnitPowerType(unit) + local min = UnitPower(unit, powerType) + if min ~= 0 and tagFormat ~= 'deficit' then + return E:GetFormattedText(textFormat, min, UnitPowerMax(unit, powerType), nil, true) + end + end) + + E:AddTag(format('mana:%s:shortvalue', tagFormat), 'UNIT_POWER_FREQUENT UNIT_MAXPOWER', function(unit) + return E:GetFormattedText(textFormat, UnitPower(unit, 0), UnitPowerMax(unit, 0), nil, true) + end) end end -for textFormat, length in pairs({veryshort = 5, short = 10, medium = 15, long = 20}) do - ElvUF.Tags.Events[format("health:deficit-percent:name-%s", textFormat)] = "UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED" - ElvUF.Tags.Methods[format("health:deficit-percent:name-%s", textFormat)] = function(unit) +for textFormat, length in pairs({ veryshort = 5, short = 10, medium = 15, long = 20 }) do + E:AddTag(format('health:deficit-percent:name-%s', textFormat), 'UNIT_HEALTH UNIT_MAXHEALTH UNIT_NAME_UPDATE', function(unit) local cur, max = UnitHealth(unit), UnitHealthMax(unit) local deficit = max - cur if deficit > 0 and cur > 0 then - return _TAGS["health:percent-nostatus"](unit) + return _TAGS['health:deficit-percent:nostatus'](unit) else - return _TAGS[format("name:%s", textFormat)](unit) + return _TAGS[format('name:%s', textFormat)](unit) end - end + end) - ElvUF.Tags.Events[format("name:abbrev:%s", textFormat)] = "UNIT_NAME_UPDATE" - ElvUF.Tags.Methods[format("name:abbrev:%s", textFormat)] = function(unit) + E:AddTag(format('name:abbrev:%s', textFormat), 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) local name = UnitName(unit) - - if name and find(name, "%s") then - name = abbrev(name) + if name and strfind(name, '%s') then + name = Abbrev(name) end - return name ~= nil and E:ShortenString(name, length) or "" - end + if name then + return E:ShortenString(name, length) + end + end) - ElvUF.Tags.Events[format("name:%s", textFormat)] = "UNIT_NAME_UPDATE" - ElvUF.Tags.Methods[format("name:%s", textFormat)] = function(unit) + E:AddTag(format('name:%s', textFormat), 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) local name = UnitName(unit) - return name ~= nil and E:ShortenString(name, length) or nil - end + if name then + return E:ShortenString(name, length) + end + end) - ElvUF.Tags.Events[format("name:%s:status", textFormat)] = "UNIT_NAME_UPDATE UNIT_CONNECTION PLAYER_FLAGS_CHANGED UNIT_HEALTH_FREQUENT" - ElvUF.Tags.Methods[format("name:%s:status", textFormat)] = function(unit) + E:AddTag(format('name:%s:status', textFormat), 'UNIT_NAME_UPDATE UNIT_CONNECTION PLAYER_FLAGS_CHANGED UNIT_HEALTH INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) local status = UnitIsDead(unit) and L["Dead"] or UnitIsGhost(unit) and L["Ghost"] or not UnitIsConnected(unit) and L["Offline"] local name = UnitName(unit) - if (status) then + if status then return status - else - return name ~= nil and E:ShortenString(name, length) or nil + elseif name then + return E:ShortenString(name, length) end - end + end) - ElvUF.Tags.OnUpdateThrottle[format("name:%s:statustimer", textFormat)] = 1 - ElvUF.Tags.Methods[format("name:%s:statustimer", textFormat)] = function(unit) - local status = getUnitStatus(unit) - if status then return status end - local name = UnitName(unit) - return name ~= nil and E:ShortenString(name, length) or nil - end - - ElvUF.Tags.Events[format("name:%s:translit", textFormat)] = "UNIT_NAME_UPDATE" - ElvUF.Tags.Methods[format("name:%s:translit", textFormat)] = function(unit) + E:AddTag(format('name:%s:translit', textFormat), 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) local name = Translit:Transliterate(UnitName(unit), translitMark) - return name ~= nil and E:ShortenString(name, length) or nil - end + if name then + return E:ShortenString(name, length) + end + end) - ElvUF.Tags.Events[format("target:%s", textFormat)] = "UNIT_TARGET" - ElvUF.Tags.Methods[format("target:%s", textFormat)] = function(unit) - local targetName = UnitName(unit.."target") - return targetName ~= nil and E:ShortenString(targetName, length) or nil - end + E:AddTag(format('target:%s', textFormat), 'UNIT_TARGET', function(unit) + local targetName = UnitName(unit..'target') + if targetName then + return E:ShortenString(targetName, length) + end + end) - ElvUF.Tags.Events[format("target:%s:translit", textFormat)] = "UNIT_TARGET" - ElvUF.Tags.Methods[format("target:%s:translit", textFormat)] = function(unit) - local targetName = Translit:Transliterate(UnitName(unit.."target"), translitMark) - return targetName ~= nil and E:ShortenString(targetName, length) or nil - end + E:AddTag(format('target:%s:translit', textFormat), 'UNIT_TARGET', function(unit) + local targetName = Translit:Transliterate(UnitName(unit..'target'), translitMark) + if targetName then + return E:ShortenString(targetName, length) + end + end) end -ElvUF.Tags.OnUpdateThrottle["name:statustimer"] = 1 -ElvUF.Tags.Methods["name:statustimer"] = function(unit) - local status = getUnitStatus(unit) - if status then return status end - return UnitName(unit) -end +------------------------------------------------------------------------ +-- Regular +------------------------------------------------------------------------ -ElvUF.Tags.Events["health:max"] = "UNIT_MAXHEALTH" -ElvUF.Tags.Methods["health:max"] = function(unit) +E:AddTag('classcolor:target', 'UNIT_TARGET', function(unit) + return _TAGS.classcolor(unit..'target') +end) + +E:AddTag('target', 'UNIT_TARGET', function(unit) + local targetName = UnitName(unit..'target') + if targetName then + return targetName + end +end) + +E:AddTag('target:translit', 'UNIT_TARGET', function(unit) + local targetName = UnitName(unit..'target') + if targetName then + return Translit:Transliterate(targetName, translitMark) + end +end) + +E:AddTag('health:max', 'UNIT_MAXHEALTH', function(unit) local max = UnitHealthMax(unit) + return E:GetFormattedText('CURRENT', max, max) +end) - return E:GetFormattedText("CURRENT", max, max) -end +E:AddTag('health:max:shortvalue', 'UNIT_MAXHEALTH', function(unit) + local _, max = UnitHealth(unit), UnitHealthMax(unit) -ElvUF.Tags.Events["health:deficit-percent:name"] = "UNIT_HEALTH UNIT_MAXHEALTH" -ElvUF.Tags.Methods["health:deficit-percent:name"] = function(unit) + return E:GetFormattedText('CURRENT', max, max, nil, true) +end) + +E:AddTag('health:deficit-percent:name', 'UNIT_HEALTH UNIT_MAXHEALTH UNIT_NAME_UPDATE', function(unit) local currentHealth = UnitHealth(unit) local deficit = UnitHealthMax(unit) - currentHealth if deficit > 0 and currentHealth > 0 then - return _TAGS["health:percent-nostatus"](unit) + return _TAGS['health:percent-nostatus'](unit) else return _TAGS.name(unit) end -end +end) -ElvUF.Tags.Events["power:max"] = "UNIT_MAXENERGY UNIT_MAXFOCUS UNIT_MAXMANA UNIT_MAXRAGE UNIT_MAXRUNIC_POWER" -ElvUF.Tags.Methods["power:max"] = function(unit) +E:AddTag('power:max', 'UNIT_DISPLAYPOWER UNIT_MAXPOWER', function(unit) + local powerType = UnitPowerType(unit) + local max = UnitPowerMax(unit, powerType) + + return E:GetFormattedText('CURRENT', max, max) +end) + +E:AddTag('power:max:shortvalue', 'UNIT_DISPLAYPOWER UNIT_MAXPOWER', function(unit) local pType = UnitPowerType(unit) local max = UnitPowerMax(unit, pType) - return E:GetFormattedText("CURRENT", max, max) -end + return E:GetFormattedText('CURRENT', max, max, nil, true) +end) -ElvUF.Tags.Methods["manacolor"] = function() - local mana = PowerBarColor.MANA - local altR, altG, altB = mana.r, mana.g, mana.b - local color = ElvUF.colors.power.MANA - if color then - return Hex(color[1], color[2], color[3]) +E:AddTag('mana:max:shortvalue', 'UNIT_MAXPOWER', function(unit) + local max = UnitPowerMax(unit, 0) + + return E:GetFormattedText('CURRENT', max, max, nil, true) +end) + +E:AddTag('difficultycolor', 'UNIT_LEVEL PLAYER_LEVEL_UP', function(unit) + local color = GetQuestDifficultyColor(UnitEffectiveLevel(unit)) + return Hex(color) +end) + +E:AddTag('selectioncolor', 'UNIT_NAME_UPDATE UNIT_FACTION INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) + local selection = NP:UnitSelectionType(unit) + local cs = ElvUF.colors.selection[selection] + return (cs and Hex(cs)) or '|cFFcccccc' +end) + +E:AddTag('classcolor', 'UNIT_NAME_UPDATE UNIT_FACTION INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) + if UnitIsPlayer(unit) then + local _, unitClass = UnitClass(unit) + local cs = ElvUF.colors.class[unitClass] + return (cs and Hex(cs)) or '|cFFcccccc' else - return Hex(altR, altG, altB) + local cr = ElvUF.colors.reaction[UnitReaction(unit, 'player')] + return (cr and Hex(cr)) or '|cFFcccccc' end -end +end) -ElvUF.Tags.Events["mana:max"] = "UNIT_MAXMANA" -ElvUF.Tags.Methods["mana:max"] = function(unit) - local max = UnitPowerMax(unit, SPELL_POWER_MANA) +E:AddTag('namecolor', 'UNIT_TARGET', function(unit) + return _TAGS.classcolor(unit) +end) - return E:GetFormattedText("CURRENT", max, max) -end - -ElvUF.Tags.Events["difficultycolor"] = "UNIT_LEVEL PLAYER_LEVEL_UP" -ElvUF.Tags.Methods["difficultycolor"] = function(unit) - local r, g, b - local level = UnitLevel(unit) - if level > 1 then - local DiffColor = UnitLevel(unit) - UnitLevel("player") - if DiffColor >= 5 then - r, g, b = 0.69, 0.31, 0.31 - elseif DiffColor >= 3 then - r, g, b = 0.71, 0.43, 0.27 - elseif DiffColor >= -2 then - r, g, b = 0.84, 0.75, 0.65 - elseif -DiffColor <= GetQuestGreenRange() then - r, g, b = 0.33, 0.59, 0.33 - else - r, g, b = 0.55, 0.57, 0.61 - end - end - - return Hex(r, g, b) -end - -ElvUF.Tags.Events["namecolor"] = "UNIT_NAME_UPDATE UNIT_FACTION" -ElvUF.Tags.Methods["namecolor"] = function(unit) - local unitReaction = UnitReaction(unit, "player") - local unitPlayer = UnitIsPlayer(unit) - if unitPlayer then - if UnitIsUnit(unit, "player") then - return Hex(E.media.herocolor.r, E.media.herocolor.g, E.media.herocolor.b) - else - local _, class = UnitClass(unit) - local color = RAID_CLASS_COLORS[class] - if not color then - return Hex(1, 1, 1) - end - return color.hex - end - elseif unitReaction then - local reaction = ElvUF.colors.reaction[unitReaction] - return Hex(reaction[1], reaction[2], reaction[3]) +E:AddTag('reactioncolor', 'UNIT_NAME_UPDATE UNIT_FACTION', function(unit) + local unitReaction = UnitReaction(unit, 'player') + if unitReaction then + local color = ElvUF.colors.reaction[unitReaction] + return Hex(color) else - return "|cFFC2C2C2" + return '|cFFc2c2c2' end -end +end) -ElvUF.Tags.Events["smartlevel"] = "UNIT_LEVEL PLAYER_LEVEL_UP" -ElvUF.Tags.Methods["smartlevel"] = function(unit) - local level = UnitLevel(unit) - if level == UnitLevel("player") then - return "" +E:AddTag('smartlevel', 'UNIT_LEVEL PLAYER_LEVEL_UP', function(unit) + local level = UnitEffectiveLevel(unit) + if level == UnitEffectiveLevel('player') then + return nil elseif level > 0 then return level else - return "??" + return '??' end -end +end) -ElvUF.Tags.Events["realm"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["realm"] = function(unit) +E:AddTag('realm', 'UNIT_NAME_UPDATE', function(unit) local _, realm = UnitName(unit) - - if realm and realm ~= "" then + if realm and realm ~= '' then return realm - else - return nil end -end +end) -ElvUF.Tags.Events["realm:dash"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["realm:dash"] = function(unit) +E:AddTag('realm:dash', 'UNIT_NAME_UPDATE', function(unit) local _, realm = UnitName(unit) - - if realm and (realm ~= "" and realm ~= E.myrealm) then - realm = format("-%s", realm) - elseif realm == "" then - realm = nil + if realm and (realm ~= '' and realm ~= E.myrealm) then + return format('-%s', realm) + elseif realm ~= '' then + return realm end +end) - return realm -end +E:AddTag('realm:translit', 'UNIT_NAME_UPDATE', function(unit) + local _, realm = Translit:Transliterate(UnitName(unit), translitMark) + if realm and realm ~= '' then + return realm + end +end) -ElvUF.Tags.Events["realm:translit"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["realm:translit"] = function(unit) +E:AddTag('realm:dash:translit', 'UNIT_NAME_UPDATE', function(unit) local _, realm = Translit:Transliterate(UnitName(unit), translitMark) - if realm and realm ~= "" then + if realm and (realm ~= '' and realm ~= E.myrealm) then + return format('-%s', realm) + elseif realm ~= '' then return realm - else - return nil end -end +end) -ElvUF.Tags.Events["realm:dash:translit"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["realm:dash:translit"] = function(unit) - local _, realm = Translit:Transliterate(UnitName(unit), translitMark) - - if realm and (realm ~= "" and realm ~= E.myrealm) then - realm = format("-%s", realm) - elseif realm == "" then - realm = nil +E:AddTag('threat:percent', 'UNIT_THREAT_LIST_UPDATE UNIT_THREAT_SITUATION_UPDATE GROUP_ROSTER_UPDATE', function(unit) + local _, _, percent = UnitDetailedThreatSituation('player', unit) + if percent and percent > 0 and (IsInGroup() or UnitExists('pet')) then + return format('%.0f%%', percent) end +end) - return realm -end - -ElvUF.Tags.Events["threat:percent"] = "UNIT_THREAT_SITUATION_UPDATE UNIT_THREAT_LIST_UPDATE" -ElvUF.Tags.Methods["threat:percent"] = function(unit) - local _, _, percent = UnitDetailedThreatSituation("player", unit) - if percent and percent > 0 and (GetNumPartyMembers() or UnitExists("pet")) then - return format("%.0f%%", percent) - else - return nil - end -end - -ElvUF.Tags.Events["threat:current"] = "UNIT_THREAT_SITUATION_UPDATE UNIT_THREAT_LIST_UPDATE" -ElvUF.Tags.Methods["threat:current"] = function(unit) - local _, _, percent, _, threatvalue = UnitDetailedThreatSituation("player", unit) - if percent and percent > 0 and (GetNumPartyMembers() or UnitExists("pet")) then +E:AddTag('threat:current', 'UNIT_THREAT_LIST_UPDATE UNIT_THREAT_SITUATION_UPDATE GROUP_ROSTER_UPDATE', function(unit) + local _, _, percent, _, threatvalue = UnitDetailedThreatSituation('player', unit) + if percent and percent > 0 and (IsInGroup() or UnitExists('pet')) then return E:ShortValue(threatvalue) - else - return nil end -end +end) -ElvUF.Tags.Events["threatcolor"] = "UNIT_THREAT_SITUATION_UPDATE UNIT_THREAT_LIST_UPDATE" -ElvUF.Tags.Methods["threatcolor"] = function(unit) - local _, status = UnitDetailedThreatSituation("player", unit) - if status and (GetNumPartyMembers() > 0 or UnitExists("pet")) then - return Hex(GetThreatStatusColor(status)) - else - return nil +E:AddTag('threatcolor', 'UNIT_THREAT_LIST_UPDATE UNIT_THREAT_SITUATION_UPDATE GROUP_ROSTER_UPDATE', function(unit) + local _, status = UnitDetailedThreatSituation('player', unit) + if status and (IsInGroup() or UnitExists('pet')) then + return Hex(E:GetThreatStatusColor(status, true)) end -end +end) -ElvUF.Tags.OnUpdateThrottle["statustimer"] = 1 -ElvUF.Tags.Methods["statustimer"] = getUnitStatus - -ElvUF.Tags.OnUpdateThrottle["pvptimer"] = 1 -ElvUF.Tags.Methods["pvptimer"] = function(unit) +E:AddTag('pvptimer', 1, function(unit) if UnitIsPVPFreeForAll(unit) or UnitIsPVP(unit) then local timer = GetPVPTimer() if timer ~= 301000 and timer ~= -1 then - local mins = floor((timer / 1000) / 60) - local secs = floor((timer / 1000) - (mins * 60)) - return format("%s (%01.f:%02.f)", PVP, mins, secs) + local mins = floor((timer * 0.001) / 60) + local secs = floor((timer * 0.001) - (mins * 60)) + return format('%s (%01.f:%02.f)', PVP, mins, secs) else return PVP end - else - return nil end -end +end) -local baseSpeed = 7 -local speedText = SPEED +E:AddTag('manacolor', 'UNIT_DISPLAYPOWER', function() + local color = ElvUF.colors.power.MANA + return Hex(color) +end) -ElvUF.Tags.OnUpdateThrottle["speed:percent"] = 0.1 -ElvUF.Tags.Methods["speed:percent"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - local currentSpeedInPercent = (currentSpeedInYards / baseSpeed) * 100 +E:AddTag('ragecolor', 'UNIT_DISPLAYPOWER', function() + local color = ElvUF.colors.power.RAGE + return Hex(color) +end) - return format("%s: %d%%", speedText, currentSpeedInPercent) -end +E:AddTag('energycolor', 'UNIT_DISPLAYPOWER', function() + local color = ElvUF.colors.power.ENERGY + return Hex(color) +end) -ElvUF.Tags.OnUpdateThrottle["speed:percent-moving"] = 0.1 -ElvUF.Tags.Methods["speed:percent-moving"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - local currentSpeedInPercent = currentSpeedInYards > 0 and ((currentSpeedInYards / baseSpeed) * 100) - - if currentSpeedInPercent then - currentSpeedInPercent = format("%s: %d%%", speedText, currentSpeedInPercent) +E:AddTag('distance', 0.1, function(realUnit) + if UnitIsConnected(realUnit) and not UnitIsUnit(realUnit, 'player') then + local unit = E:GetGroupUnit(realUnit) or realUnit + local distance = E:GetDistance('player', unit) + if distance then + return format('%.1f', distance) + end end +end) - return currentSpeedInPercent or nil -end - -ElvUF.Tags.OnUpdateThrottle["speed:percent-raw"] = 0.1 -ElvUF.Tags.Methods["speed:percent-raw"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - local currentSpeedInPercent = (currentSpeedInYards / baseSpeed) * 100 - - return format("%d%%", currentSpeedInPercent) -end - -ElvUF.Tags.OnUpdateThrottle["speed:percent-moving-raw"] = 0.1 -ElvUF.Tags.Methods["speed:percent-moving-raw"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - local currentSpeedInPercent = currentSpeedInYards > 0 and ((currentSpeedInYards / baseSpeed) * 100) - - if currentSpeedInPercent then - currentSpeedInPercent = format("%d%%", currentSpeedInPercent) - end - - return currentSpeedInPercent or nil -end - -ElvUF.Tags.OnUpdateThrottle["speed:yardspersec"] = 0.1 -ElvUF.Tags.Methods["speed:yardspersec"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - - return format("%s: %.1f", speedText, currentSpeedInYards) -end - -ElvUF.Tags.OnUpdateThrottle["speed:yardspersec-moving"] = 0.1 -ElvUF.Tags.Methods["speed:yardspersec-moving"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - - return currentSpeedInYards > 0 and format("%s: %.1f", speedText, currentSpeedInYards) or nil -end - -ElvUF.Tags.OnUpdateThrottle["speed:yardspersec-raw"] = 0.1 -ElvUF.Tags.Methods["speed:yardspersec-raw"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - return format("%.1f", currentSpeedInYards) -end - -ElvUF.Tags.OnUpdateThrottle["speed:yardspersec-moving-raw"] = 0.1 -ElvUF.Tags.Methods["speed:yardspersec-moving-raw"] = function(unit) - local currentSpeedInYards = GetUnitSpeed(unit) - - return currentSpeedInYards > 0 and format("%.1f", currentSpeedInYards) or nil -end - -ElvUF.Tags.Events["classificationcolor"] = "UNIT_CLASSIFICATION_CHANGED" -ElvUF.Tags.Methods["classificationcolor"] = function(unit) +E:AddTag('classificationcolor', 'UNIT_CLASSIFICATION_CHANGED', function(unit) local c = UnitClassification(unit) - if c == "rare" or c == "elite" then - return Hex(1, 0.5, 0.25) -- Orange - elseif c == "rareelite" or c == "worldboss" then - return Hex(1, 0, 0) -- Red + if c == 'rare' or c == 'elite' then + return Hex(1, 0.5, 0.25) + elseif c == 'rareelite' or c == 'worldboss' then + return Hex(1, 0, 0) end -end +end) -ElvUF.Tags.SharedEvents.PLAYER_GUILD_UPDATE = true - -ElvUF.Tags.Events["guild"] = "UNIT_NAME_UPDATE PLAYER_GUILD_UPDATE" -ElvUF.Tags.Methods["guild"] = function(unit) - if (UnitIsPlayer(unit)) then - return GetGuildInfo(unit) or nil - end -end - -ElvUF.Tags.Events["guild:brackets"] = "PLAYER_GUILD_UPDATE" -ElvUF.Tags.Methods["guild:brackets"] = function(unit) - local guildName = GetGuildInfo(unit) - - return guildName and format("<%s>", guildName) or nil -end - -ElvUF.Tags.Events["guild:translit"] = "UNIT_NAME_UPDATE PLAYER_GUILD_UPDATE" -ElvUF.Tags.Methods["guild:translit"] = function(unit) +E:AddTag('guild', 'UNIT_NAME_UPDATE PLAYER_GUILD_UPDATE', function(unit) if UnitIsPlayer(unit) then - return Translit:Transliterate(GetGuildInfo(unit), translitMark) or nil + return GetGuildInfo(unit) end -end +end) -ElvUF.Tags.Events["guild:brackets:translit"] = "PLAYER_GUILD_UPDATE" -ElvUF.Tags.Methods["guild:brackets:translit"] = function(unit) - local guildName = Translit:Transliterate(GetGuildInfo(unit), translitMark) - - return guildName and format("<%s>", guildName) or nil -end - -ElvUF.Tags.Events["target"] = "UNIT_TARGET" -ElvUF.Tags.Methods["target"] = function(unit) - local targetName = UnitName(unit.."target") - return targetName or nil -end - -ElvUF.Tags.Events["target:translit"] = "UNIT_TARGET" -ElvUF.Tags.Methods["target:translit"] = function(unit) - local targetName = Translit:Transliterate(UnitName(unit.."target"), translitMark) - return targetName or nil -end - -ElvUF.Tags.Events["guild:rank"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["guild:rank"] = function(unit) - if (UnitIsPlayer(unit)) then - return select(2, GetGuildInfo(unit)) or "" +E:AddTag('group:raid', 'GROUP_ROSTER_UPDATE', function(unit) + if IsInRaid() then + local name, realm = UnitName(unit) + if name then + local nameRealm = (realm and realm ~= '' and format('%s-%s', name, realm)) or name + for i = 1, GetNumGroupMembers() do + local raidName, _, group = GetRaidRosterInfo(i) + if raidName == nameRealm then + return group + end + end + end end -end +end) -ElvUF.Tags.Events["arena:number"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["arena:number"] = function(unit) +E:AddTag('guild:brackets', 'PLAYER_GUILD_UPDATE', function(unit) + local guildName = GetGuildInfo(unit) + if guildName then + return format('<%s>', guildName) + end +end) + +E:AddTag('guild:translit', 'UNIT_NAME_UPDATE PLAYER_GUILD_UPDATE', function(unit) + if UnitIsPlayer(unit) then + local guildName = GetGuildInfo(unit) + if guildName then + return Translit:Transliterate(guildName, translitMark) + end + end +end) + +E:AddTag('guild:brackets:translit', 'PLAYER_GUILD_UPDATE', function(unit) + local guildName = GetGuildInfo(unit) + if guildName then + return format('<%s>', Translit:Transliterate(guildName, translitMark)) + end +end) + +E:AddTag('guild:rank', 'UNIT_NAME_UPDATE', function(unit) + if UnitIsPlayer(unit) then + local _, rank = GetGuildInfo(unit) + if rank then + return rank + end + end +end) + +E:AddTag('arena:number', 'UNIT_NAME_UPDATE', function(unit) local _, instanceType = GetInstanceInfo() - if instanceType == "arena" then + if instanceType == 'arena' then for i = 1, 5 do - if UnitIsUnit(unit, "arena"..i) then + if UnitIsUnit(unit, 'arena'..i) then return i end end end -end +end) -ElvUF.Tags.Events["class"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["class"] = function(unit) - return UnitClass(unit) -end +E:AddTag('class', 'UNIT_NAME_UPDATE', function(unit) + if not UnitIsPlayer(unit) then return end -ElvUF.Tags.Events["name:title"] = "UNIT_NAME_UPDATE" -ElvUF.Tags.Methods["name:title"] = function(unit) + local _, classToken = UnitClass(unit) + if UnitSex(unit) == 3 then + return _G.LOCALIZED_CLASS_NAMES_FEMALE[classToken] + else + return _G.LOCALIZED_CLASS_NAMES_MALE[classToken] + end +end) + +E:AddTag('name:title', 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) + return UnitIsPlayer(unit) and UnitPVPName(unit) or UnitName(unit) +end) + +E:AddTag('title', 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) if UnitIsPlayer(unit) then - return UnitPVPName(unit) + return GetTitleName(GetCurrentTitle()) + end +end) + +E:AddTag('afk', 'PLAYER_FLAGS_CHANGED', function(unit) + if UnitIsAFK(unit) then + return format('|cffFFFFFF[|r|cffFF9900%s|r|cFFFFFFFF]|r', L["AFK"]) + end +end) + +E:AddTag('healthcolor', 'UNIT_HEALTH UNIT_MAXHEALTH UNIT_CONNECTION PLAYER_FLAGS_CHANGED', function(unit) + if UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit) then + return Hex(0.84, 0.75, 0.65) + else + local r, g, b = ElvUF:ColorGradient(UnitHealth(unit), UnitHealthMax(unit), 0.69, 0.31, 0.31, 0.65, 0.63, 0.35, 0.33, 0.59, 0.33) + return Hex(r, g, b) + end +end) + +E:AddTag('status:text', 'PLAYER_FLAGS_CHANGED', function(unit) + if UnitIsAFK(unit) then + return format('|cffFF9900<|r%s|cffFF9900>|r', L["AFK"]) + elseif UnitIsDND(unit) then + return format('|cffFF3333<|r%s|cffFF3333>|r', L["DND"]) + end +end) + +do + local afk = [[|TInterface\FriendsFrame\StatusIcon-Away:16:16|t]] + local dnd = [[|TInterface\FriendsFrame\StatusIcon-DnD:16:16|t]] + E:AddTag('status:icon', 'PLAYER_FLAGS_CHANGED', function(unit) + if UnitIsAFK(unit) then + return afk + elseif UnitIsDND(unit) then + return dnd + end + end) +end + +E:AddTag('name:abbrev', 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) + local name = UnitName(unit) + if name and strfind(name, '%s') then + name = Abbrev(name) + end + + return name +end) + +E:AddTag('name:last', 'UNIT_NAME_UPDATE INSTANCE_ENCOUNTER_ENGAGE_UNIT', function(unit) + local name = UnitName(unit) + if name and strfind(name, '%s') then + name = strmatch(name, '([%S]+)$') + end + + return name +end) + +E:AddTag('health:deficit-percent:nostatus', 'UNIT_HEALTH UNIT_MAXHEALTH', function(unit) + local min, max = UnitHealth(unit), UnitHealthMax(unit) + local deficit = (min / max) - 1 + if deficit ~= 0 then + return E:GetFormattedText('PERCENT', deficit, -1) + end +end) + +E:AddTag('speed:yardspersec-raw', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + return format('%.1f', currentSpeedInYards) +end) + +E:AddTag('speed:yardspersec-moving-raw', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + return currentSpeedInYards > 0 and format('%.1f', currentSpeedInYards) or nil +end) + +------------------------------------------------------------------------ +-- Scoped +------------------------------------------------------------------------ + +do + local faction = { + Horde = [[|TInterface\FriendsFrame\PlusManz-Horde:16:16|t]], + Alliance = [[|TInterface\FriendsFrame\PlusManz-Alliance:16:16|t]] + } + + E:AddTag('faction:icon', 'UNIT_FACTION', function(unit) + return faction[UnitFactionGroup(unit)] + end) +end + +do + local factionColors = { + [''] = '|cFFc2c2c2', + Alliance = '|cFF0099ff', + Horde = '|cFFff3333', + Neutral = '|cFF33ff33' + } + + E:AddTag('factioncolor', 'UNIT_NAME_UPDATE UNIT_FACTION', function(unit) + local englishFaction = E:GetUnitBattlefieldFaction(unit) + return factionColors[englishFaction or ''] + end) +end + +do + local function NameHealthColor(tags,hex,unit,default) + if hex == 'class' or hex == 'reaction' then + return tags.classcolor(unit) or default + elseif hex and strmatch(hex, '^%x%x%x%x%x%x$') then + return '|cFF'..hex + end + + return default + end + E.TagFunctions.NameHealthColor = NameHealthColor + + -- the third arg here is added from the user as like [name:health{ff00ff:00ff00}] or [name:health{class:00ff00}] + E:AddTag('name:health', 'UNIT_NAME_UPDATE UNIT_FACTION UNIT_HEALTH UNIT_MAXHEALTH', function(unit, _, args) + local name = UnitName(unit) + if not name then return '' end + + local min, max, bco, fco = UnitHealth(unit), UnitHealthMax(unit), strsplit(':', args or '') + local to = ceil(utf8len(name) * (min / max)) + + local fill = NameHealthColor(_TAGS, fco, unit, '|cFFff3333') + local base = NameHealthColor(_TAGS, bco, unit, '|cFFffffff') + + return to > 0 and (base..utf8sub(name, 0, to)..fill..utf8sub(name, to+1, -1)) or fill..name + end) +end + +do + local unitStatus = {} + E:AddTag('statustimer', 1, function(unit) + if not UnitIsPlayer(unit) then return end + + local guid = UnitGUID(unit) + local status = unitStatus[guid] + + if UnitIsAFK(unit) then + if not status or status[1] ~= 'AFK' then + unitStatus[guid] = {'AFK', GetTime()} + end + elseif UnitIsDND(unit) then + if not status or status[1] ~= 'DND' then + unitStatus[guid] = {'DND', GetTime()} + end + elseif UnitIsDead(unit) or UnitIsGhost(unit) then + if not status or status[1] ~= 'Dead' then + unitStatus[guid] = {'Dead', GetTime()} + end + elseif not UnitIsConnected(unit) then + if not status or status[1] ~= 'Offline' then + unitStatus[guid] = {'Offline', GetTime()} + end + else + unitStatus[guid] = nil + end + + if status ~= unitStatus[guid] then + status = unitStatus[guid] + end + + if status then + local timer = GetTime() - status[2] + local mins = floor(timer / 60) + local secs = floor(timer - (mins * 60)) + return format('%s (%01.f:%02.f)', L[status[1]], mins, secs) + end + end) +end + +do + local GroupUnits = {} + local frame = CreateFrame('Frame') + frame:RegisterEvent('GROUP_ROSTER_UPDATE') + frame:SetScript('OnEvent', function() + wipe(GroupUnits) + + local groupType, groupSize + if IsInRaid() then + groupType = 'raid' + groupSize = GetNumGroupMembers() + elseif IsInGroup() then + groupType = 'party' + groupSize = GetNumGroupMembers() + else + groupType = 'solo' + groupSize = 1 + end + + for index = 1, groupSize do + local groupUnit = groupType..index + if not UnitIsUnit(groupUnit, 'player') then + GroupUnits[groupUnit] = true + end + end + end) + + for _, var in ipairs({4,8,10,15,20,25,30,35,40}) do + E:AddTag(format('nearbyplayers:%s', var), 0.25, function(realUnit) + local inRange = 0 + + if UnitIsConnected(realUnit) then + local unit = E:GetGroupUnit(realUnit) or realUnit + for groupUnit in pairs(GroupUnits) do + if UnitIsConnected(groupUnit) and not UnitIsUnit(unit, groupUnit) then + local distance = E:GetDistance(unit, groupUnit) + if distance and distance <= var then + inRange = inRange + 1 + end + end + end + end + + if inRange > 0 then + return inRange + end + end) end end + +do + local speedText = _G.SPEED + local baseSpeed = _G.BASE_MOVEMENT_SPEED + E:AddTag('speed:percent', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + local currentSpeedInPercent = (currentSpeedInYards / baseSpeed) * 100 + + return format('%s: %d%%', speedText, currentSpeedInPercent) + end) + + E:AddTag('speed:percent-moving', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + local currentSpeedInPercent = currentSpeedInYards > 0 and ((currentSpeedInYards / baseSpeed) * 100) + + if currentSpeedInPercent then + currentSpeedInPercent = format('%s: %d%%', speedText, currentSpeedInPercent) + end + + return currentSpeedInPercent + end) + + E:AddTag('speed:percent-raw', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + local currentSpeedInPercent = (currentSpeedInYards / baseSpeed) * 100 + + return format('%d%%', currentSpeedInPercent) + end) + + E:AddTag('speed:percent-moving-raw', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + local currentSpeedInPercent = currentSpeedInYards > 0 and ((currentSpeedInYards / baseSpeed) * 100) + + if currentSpeedInPercent then + currentSpeedInPercent = format('%d%%', currentSpeedInPercent) + end + + return currentSpeedInPercent + end) + + E:AddTag('speed:yardspersec', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + return format('%s: %.1f', speedText, currentSpeedInYards) + end) + + E:AddTag('speed:yardspersec-moving', 0.1, function(unit) + local currentSpeedInYards = GetUnitSpeed(unit) + return currentSpeedInYards > 0 and format('%s: %.1f', speedText, currentSpeedInYards) or nil + end) +end + +do + local gold, silver = '|A:nameplates-icon-elite-gold:16:16|a', '|A:nameplates-icon-elite-silver:16:16|a' + local typeIcon = { elite = gold, worldboss = gold, rareelite = silver, rare = silver } + E:AddTag('classification:icon', 'UNIT_NAME_UPDATE', function(unit) + if UnitIsPlayer(unit) then return end + return typeIcon[UnitClassification(unit)] + end) + + local typeName = { rare = L["Rare"], rareelite = L["Rare Elite"], elite = L["Elite"], worldboss = L["Boss"], minus = L["Affix"] } + E:AddTag('classification', 'UNIT_CLASSIFICATION_CHANGED', function(unit) + return typeName[UnitClassification(unit)] + end) +end + +do + local function GetTitleNPC(unit, custom) + if UnitIsPlayer(unit) or (E.Wrath and UnitAffectingCombat('player') and IsInInstance()) then return end + + E.ScanTooltip:SetOwner(_G.UIParent, 'ANCHOR_NONE') + E.ScanTooltip:SetUnit(unit) + E.ScanTooltip:Show() + + -- similar to TT.GetLevelLine + local ttLine = _G[format('ElvUI_ScanTooltipTextLeft%d', GetCVarBool('colorblindmode') and 3 or 2)] + local ttText = ttLine and ttLine:GetText() + local ttLower = ttText and strlower(ttText) + if ttLower and not strfind(ttLower, LEVEL) then + return custom and format(custom, ttText) or ttText + end + end + E.TagFunctions.GetTitleNPC = GetTitleNPC + + E:AddTag('npctitle', 'UNIT_NAME_UPDATE', function(unit) + return GetTitleNPC(unit) + end) + + E:AddTag('npctitle:brackets', 'UNIT_NAME_UPDATE', function(unit) + return GetTitleNPC(unit, '<%s>') + end) +end + +do + local function GetQuestData(unit, which, Hex) + if UnitIsPlayer(unit) or (E.Wrath and UnitAffectingCombat('player') and IsInInstance()) then return end + + E.ScanTooltip:SetOwner(_G.UIParent, 'ANCHOR_NONE') + E.ScanTooltip:SetUnit(unit) + E.ScanTooltip:Show() + + local notMyQuest, activeID + for i = 3, E.ScanTooltip:NumLines() do + local str = _G['ElvUI_ScanTooltipTextLeft' .. i] + local text = str and str:GetText() + if not text or text == '' then return end + + if UnitIsPlayer(text) then + notMyQuest = text ~= E.myname + elseif text and not notMyQuest then + local count, percent = NP.QuestIcons.CheckTextForQuest(text) + + -- this line comes from one line up in the tooltip + local activeQuest = NP.QuestIcons.activeQuests[text] + if activeQuest then activeID = activeQuest end + + if count then + if not which then + return text + elseif which == 'count' then + return percent and format('%s%%', count) or count + elseif which == 'title' and activeID then + local title = C_QuestLog_GetTitleForQuestID(activeID) + local level = Hex and C_QuestLog_GetQuestDifficultyLevel(activeID) + if level then + local colors = GetQuestDifficultyColor(level) + title = format('%s%s|r', Hex(colors), title) + end + + return title + elseif (which == 'info' or which == 'full') and activeID then + local title = C_QuestLog_GetTitleForQuestID(activeID) + local level = Hex and C_QuestLog_GetQuestDifficultyLevel(activeID) + if level then + local colors = GetQuestDifficultyColor(level) + title = format('%s%s|r', Hex(colors), title) + end + + if which == 'full' then + return format('%s: %s', title, text) + else + return format(percent and '%s: %s%%' or '%s: %s', title, count) + end + end + end + end + end + end + E.TagFunctions.GetQuestData = GetQuestData + + E:AddTag('quest:text', 'QUEST_LOG_UPDATE', function(unit) + return GetQuestData(unit, nil, Hex) + end) + + E:AddTag('quest:full', 'QUEST_LOG_UPDATE', function(unit) + return GetQuestData(unit, 'full', Hex) + end) + + E:AddTag('quest:info', 'QUEST_LOG_UPDATE', function(unit) + return GetQuestData(unit, 'info', Hex) + end) + + E:AddTag('quest:title', 'QUEST_LOG_UPDATE', function(unit) + return GetQuestData(unit, 'title', Hex) + end) + + E:AddTag('quest:count', 'QUEST_LOG_UPDATE', function(unit) + return GetQuestData(unit, 'count', Hex) + end) +end + +do + local highestVersion = E.version + E:AddTag('ElvUI-Users', 20, function(unit) + if E.UserList and next(E.UserList) then + local name, realm = UnitName(unit) + if name then + local nameRealm = (realm and realm ~= '' and format('%s-%s', name, realm)) or name + local userVersion = nameRealm and E.UserList[nameRealm] + if userVersion then + if highestVersion < userVersion then + highestVersion = userVersion + end + return (userVersion < highestVersion) and '|cffFF3333E|r' or '|cff3366ffE|r' + end + end + end + end) +end + +do + local classIcon = [[|TInterface\WorldStateFrame\ICONS-CLASSES:32:32:0:0:256:256:%s|t]] + local classIcons = { + WARRIOR = '0:64:0:64', + MAGE = '64:128:0:64', + ROGUE = '128:192:0:64', + DRUID = '192:256:0:64', + HUNTER = '0:64:64:128', + SHAMAN = '64:128:64:128', + PRIEST = '128:192:64:128', + WARLOCK = '192:256:64:128', + PALADIN = '0:64:128:192', + DEATHKNIGHT = '64:128:128:192', + MONK = '128:192:128:192', + DEMONHUNTER = '192:256:128:192', + EVOKER = '0:64:192:256', + } + + E:AddTag('class:icon', 'PLAYER_TARGET_CHANGED', function(unit) + if UnitIsPlayer(unit) then + local _, class = UnitClass(unit) + local icon = classIcons[class] + if icon then + return format(classIcon, icon) + end + end + end) +end + +do -- classic things + local GetPetHappiness = GetPetHappiness + local GetPetFoodTypes = GetPetFoodTypes + + local emotionsIcons = { + [[|TInterface\PetPaperDollFrame\UI-PetHappiness:16:16:0:0:128:64:48:72:0:23|t]], + [[|TInterface\PetPaperDollFrame\UI-PetHappiness:16:16:0:0:128:64:24:48:0:23|t]], + [[|TInterface\PetPaperDollFrame\UI-PetHappiness:16:16:0:0:128:64:0:24:0:23|t]] + } + + local emotionsDiscord = { + E:TextureString(E.Media.ChatEmojis.Rage, ':16:16:0:0:32:32:0:32:0:32'), + E:TextureString(E.Media.ChatEmojis.SlightFrown, ':16:16:0:0:32:32:0:32:0:32'), + E:TextureString(E.Media.ChatEmojis.HeartEyes, ':16:16:0:0:32:32:0:32:0:32') + } + + E:AddTag('happiness', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + local happiness = GetPetHappiness() + if(happiness == 1) then + return ':<' + elseif(happiness == 2) then + return ':|' + elseif(happiness == 3) then + return ':D' + end + end + end) + + E:AddTag('happiness:full', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + return _G['PET_HAPPINESS'..GetPetHappiness()] + end + end) + + E:AddTag('happiness:icon', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + return emotionsIcons[GetPetHappiness()] + end + end) + + E:AddTag('happiness:discord', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + return emotionsDiscord[GetPetHappiness()] + end + end) + + E:AddTag('happiness:color', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + return Hex(_COLORS.happiness[GetPetHappiness()]) + end + end) + + E:AddTag('diet', 'UNIT_HAPPINESS PET_UI_UPDATE', function(unit) + local hasPetUI, isHunterPet = HasPetUI() + if hasPetUI and isHunterPet and UnitIsUnit('pet', unit) then + return GetPetFoodTypes() + end + end) + + E:AddTag('pvp:title', 'UNIT_NAME_UPDATE', function(unit) + if UnitIsPlayer(unit) then + local rank = UnitPVPRank(unit) + local title = GetPVPRankInfo(rank, unit) + + return title + end + end) + + E:AddTag('pvp:rank', 'UNIT_NAME_UPDATE', function(unit) + if UnitIsPlayer(unit) then + local rank = UnitPVPRank(unit) + local _, num = GetPVPRankInfo(rank, unit) + + if num > 0 then + return num + end + end + end) + + local rankIcon = [[|TInterface\PvPRankBadges\PvPRank%02d:12:12:0:0:12:12:0:12:0:12|t]] + E:AddTag('pvp:icon', 'UNIT_NAME_UPDATE', function(unit) + if UnitIsPlayer(unit) then + local rank = UnitPVPRank(unit) + local _, num = GetPVPRankInfo(rank, unit) + + if num > 0 then + return format(rankIcon, num) + end + end + end) +end +------------------------------------------------------------------------ +-- Available Tags +------------------------------------------------------------------------ + E.TagInfo = { - --Colors - ["namecolor"] = {category = "Colors", description = "Colors names by player class or NPC reaction"}, - ["powercolor"] = {category = "Colors", description = "Colors the power text based upon its type"}, - ["energycolor"] = {category = "Colors", description = "Colors the energy text based upon its type"}, - ["ragecolor"] = {category = "Colors", description = "Colors the rage text based upon its type"}, - ["difficultycolor"] = {category = "Colors", description = "Colors the following tags by difficulty, red for impossible, orange for hard, green for easy"}, - ["healthcolor"] = {category = "Colors", description = "Changes color of health text, depending on the unit's current health"}, - ["threatcolor"] = {category = "Colors", description = "Changes color of health, depending on the unit's threat situation"}, - ["classificationcolor"] = {category = "Colors", description = "Changes color of health, depending on the unit's classification"}, - --Classification - ["classification"] = {category = "Classification", description = "Displays the unit's classification (e.g. 'ELITE' and 'RARE')"}, - ["shortclassification"] = {category = "Classification", description = "Displays the unit's classification in short form (e.g. '+' for ELITE and 'R' for RARE)"}, - ["rare"] = {category = "Classification", description = "Displays 'Rare' when the unit is a rare or rareelite"}, - --Guild - ["guild"] = {category = "Guild", description = "Displays the guild name"}, - ["guild:brackets"] = {category = "Guild", description = "Displays the guild name with < > brackets (e.g. )"}, - ["guild:brackets:translit"] = {category = "Guild", description = "Displays the guild name with < > and transliteration (e.g. )"}, - ["guild:rank"] = {category = "Guild", description = "Displays the guild rank"}, - ["guild:translit"] = {category = "Guild", description = "Displays the guild name with transliteration for cyrillic letters"}, - --Health - ["curhp"] = {category = "Health", description = "Displays the current HP without decimals"}, - ["perhp"] = {category = "Health", description = "Displays percentage HP without decimals"}, - ["maxhp"] = {category = "Health", description = "Displays max HP without decimals"}, - ["deficit:name"] = {category = "Health", description = "Displays the health as a deficit and the name at full health"}, - ["health:current"] = {category = "Health", description = "Displays the current health of the unit"}, - ["health:current-max"] = {category = "Health", description = "Displays the current and maximum health of the unit, separated by a dash"}, - ["health:current-max-nostatus"] = {category = "Health", description = "Displays the current and maximum health of the unit, separated by a dash, without status"}, - ["health:current-max-percent"] = {category = "Health", description = "Displays the current and max hp of the unit, separated by a dash (% when not full hp)"}, - ["health:current-max-percent-nostatus"] = {category = "Health", description = "Displays the current and max hp of the unit, separated by a dash (% when not full hp), without status"}, - ["health:current-nostatus"] = {category = "Health", description = "Displays the current health of the unit, without status"}, - ["health:current-percent"] = {category = "Health", description = "Displays the current hp of the unit (% when not full hp)"}, - ["health:current-percent-nostatus"] = {category = "Health", description = "Displays the current hp of the unit (% when not full hp), without status"}, - ["health:deficit"] = {category = "Health", description = "Displays the health of the unit as a deficit (Total Health - Current Health = -Deficit)"}, - ["health:deficit-nostatus"] = {category = "Health", description = "Displays the health of the unit as a deficit, without status"}, - ["health:deficit-percent:name"] = {category = "Health", description = "Displays the health deficit as a percentage and the full name of the unit"}, - ["health:deficit-percent:name-long"] = {category = "Health", description = "Displays the health deficit as a percentage and the name of the unit (limited to 20 letters)"}, - ["health:deficit-percent:name-medium"] = {category = "Health", description = "Displays the health deficit as a percentage and the name of the unit (limited to 15 letters)"}, - ["health:deficit-percent:name-short"] = {category = "Health", description = "Displays the health deficit as a percentage and the name of the unit (limited to 10 letters)"}, - ["health:deficit-percent:name-veryshort"] = {category = "Health", description = "Displays the health deficit as a percentage and the name of the unit (limited to 5 letters)"}, - ["health:max"] = {category = "Health", description = "Displays the maximum health of the unit"}, - ["health:percent"] = {category = "Health", description = "Displays the current health of the unit as a percentage"}, - ["health:percent-nostatus"] = {category = "Health", description = "Displays the unit's current health as a percentage, without status"}, - ["missinghp"] = {category = "Health", description = "Displays the missing health of the unit in whole numbers, when not at full health"}, - --Level - ["smartlevel"] = {category = "Level", description = "Only display the unit's level if it is not the same as yours"}, - ["level"] = {category = "Level", description = "Displays the level of the unit"}, - --Mana - ["mana:current"] = {category = "Mana", description = "Displays the unit's current amount of mana (e.g. 97200)"}, - ["mana:current-percent"] = {category = "Mana", description = "Displays the current amount of mana as a whole number and a percentage, separated by a dash"}, - ["mana:current-max"] = {category = "Mana", description = "Displays the current mana and max mana, separated by a dash"}, - ["mana:current-max-percent"] = {category = "Mana", description = "Displays the current mana and max mana, separated by a dash (% when not full power)"}, - ["mana:percent"] = {category = "Mana", description = "Displays the mana of the unit as a percentage value"}, - ["mana:max"] = {category = "Mana", description = "Displays the unit's maximum mana"}, - ["mana:deficit"] = {category = "Mana", description = "Displays the mana deficit (Total Mana - Current Mana = -Deficit)"}, - ["curmana"] = {category = "Mana", description = "Displays the current mana without decimals"}, - ["maxmana"] = {category = "Mana", description = "Displays the max amount of mana the unit can have"}, - --Names - ["name"] = {category = "Names", description = "Displays the full name of the unit without any letter limitation"}, - ["name:veryshort"] = {category = "Names", description = "Displays the name of the unit (limited to 5 letters)"}, - ["name:short"] = {category = "Names", description = "Displays the name of the unit (limited to 10 letters)"}, - ["name:medium"] = {category = "Names", description = "Displays the name of the unit (limited to 15 letters)"}, - ["name:long"] = {category = "Names", description = "Displays the name of the unit (limited to 20 letters)"}, - ["name:veryshort:translit"] = {category = "Names", description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 5 letters)"}, - ["name:short:translit"] = {category = "Names", description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 10 letters)"}, - ["name:medium:translit"] = {category = "Names", description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 15 letters)"}, - ["name:long:translit"] = {category = "Names", description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 20 letters)"}, - ["name:abbrev"] = {category = "Names", description = "Displays the name of the unit with abbreviation (e.g. 'Shadowfury Witch Doctor' becomes 'S. W. Doctor')"}, - ["name:abbrev:veryshort"] = {category = "Names", description = "Displays the name of the unit with abbreviation (limited to 5 letters)"}, - ["name:abbrev:short"] = {category = "Names", description = "Displays the name of the unit with abbreviation (limited to 10 letters)"}, - ["name:abbrev:medium"] = {category = "Names", description = "Displays the name of the unit with abbreviation (limited to 15 letters)"}, - ["name:abbrev:long"] = {category = "Names", description = "Displays the name of the unit with abbreviation (limited to 20 letters)"}, - ["name:veryshort:status"] = {category = "Names", description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 5 letters)"}, - ["name:short:status"] = {category = "Names", description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 10 letters)"}, - ["name:medium:status"] = {category = "Names", description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 15 letters)"}, - ["name:long:status"] = {category = "Names", description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 20 letters)"}, - ["name:statustimer"] = {category = "Names", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34') Otherwise the unit's Name"}, - ["name:veryshort:statustimer"] = {category = "Names", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34') Otherwise the unit's Name (limited to 5 letters)"}, - ["name:short:statustimer"] = {category = "Names", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34') Otherwise the unit's Name (limited to 10 letters)"}, - ["name:medium:statustimer"] = {category = "Names", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34') Otherwise the unit's Name (limited to 15 letters)"}, - ["name:long:statustimer"] = {category = "Names", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34') Otherwise the unit's Name (limited to 20 letters)"}, - ["name:title"] = {category = "Names", description = "Displays player name and title"}, - --Party and Raid - ["group"] = {category = "Party and Raid", description = "Displays the group number the unit is in ('1' - '8')"}, - ["leader"] = {category = "Party and Raid", description = "Displays 'L' if the unit is the group/raid leader"}, - ["leaderlong"] = {category = "Party and Raid", description = "Displays 'Leader' if the unit is the group/raid leader"}, - --Power - ["power:current"] = {category = "Power", description = "Displays the unit's current amount of power"}, - ["power:current-percent"] = {category = "Power", description = "Displays the current power and power as a percentage, separated by a dash"}, - ["power:current-max"] = {category = "Power", description = "Displays the current power and max power, separated by a dash"}, - ["power:current-max-percent"] = {category = "Power", description = "Displays the current power and max power, separated by a dash (% when not full power)"}, - ["power:percent"] = {category = "Power", description = "Displays the unit's power as a percentage"}, - ["power:max"] = {category = "Power", description = "Displays the unit's maximum power"}, - ["power:deficit"] = {category = "Power", description = "Displays the power as a deficit (Total Power - Current Power = -Deficit)"}, - ["curpp"] = {category = "Power", description = "Displays the unit's current power without decimals"}, - ["perpp"] = {category = "Power", description = "Displays the unit's percentage power without decimals "}, - ["maxpp"] = {category = "Power", description = "Displays the max amount of power of the unit in whole numbers without decimals"}, - ["missingpp"] = {category = "Power", description = "Displays the missing power of the unit in whole numbers when not at full power"}, - --Energy - ["energy:current"] = {category = "Energy", description = "Displays the unit's current amount of energy"}, - ["energy:current-percent"] = {category = "Energy", description = "Displays the current energy and energy as a percentage, separated by a dash"}, - ["energy:current-max"] = {category = "Energy", description = "Displays the current energy and max energy, separated by a dash"}, - ["energy:current-max-percent"] = {category = "Energy", description = "Displays the current energy and max energy, separated by a dash (% when not full energy)"}, - ["energy:percent"] = {category = "Energy", description = "Displays the unit's energy as a percentage"}, - ["energy:max"] = {category = "Energy", description = "Displays the unit's maximum energy"}, - ["energy:deficit"] = {category = "Energy", description = "Displays the energy as a deficit (Total Energy - Current Energy = -Deficit)"}, - --Rage - ["rage:current"] = {category = "Rage", description = "Displays the unit's current amount of rage"}, - ["rage:current-percent"] = {category = "Rage", description = "Displays the current rage and rage as a percentage, separated by a dash"}, - ["rage:current-max"] = {category = "Rage", description = "Displays the current rage and max rage, separated by a dash"}, - ["rage:current-max-percent"] = {category = "Rage", description = "Displays the current rage and max rage, separated by a dash (% when not full rage)"}, - ["rage:percent"] = {category = "Rage", description = "Displays the unit's rage as a percentage"}, - ["rage:max"] = {category = "Rage", description = "Displays the unit's maximum rage"}, - ["rage:deficit"] = {category = "Rage", description = "Displays the rage as a deficit (Total Rage - Current Rage = -Deficit)"}, - --Realm - ["realm"] = {category = "Realm", description = "Displays the server name"}, - ["realm:translit"] = {category = "Realm", description = "Displays the server name with transliteration for cyrillic letters"}, - ["realm:dash"] = {category = "Realm", description = "Displays the server name with a dash in front (e.g. -Realm)"}, - ["realm:dash:translit"] = {category = "Realm", description = "Displays the server name with transliteration for cyrillic letters and a dash in front"}, - --Status - ["status"] = {category = "Status", description = "Displays zzz, dead, ghost, offline"}, - ["statustimer"] = {category = "Status", description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34')"}, - ["afk"] = {category = "Status", description = "Displays if the Unit is afk"}, - ["dead"] = {category = "Status", description = "Displays if the unit is dead"}, - ["resting"] = {category = "Status", description = "Displays zzz if the unit is dead"}, - ["pvp"] = {category = "Status", description = "Displays 'PvP' if the unit is pvp flagged"}, - ["offline"] = {category = "Status", description = "Displays 'OFFLINE' if the unit is disconnected"}, - --Target - ["target"] = {category = "Target", description = "Displays the current target of the unit"}, - ["target:veryshort"] = {category = "Target", description = "Displays the current target of the unit (limited to 5 letters)"}, - ["target:short"] = {category = "Target", description = "Displays the current target of the unit (limited to 10 letters)"}, - ["target:medium"] = {category = "Target", description = "Displays the current target of the unit (limited to 15 letters)"}, - ["target:long"] = {category = "Target", description = "Displays the current target of the unit (limited to 20 letters)"}, - ["target:translit"] = {category = "Target", description = "Displays the current target of the unit with transliteration for cyrillic letters"}, - ["target:veryshort:translit"] = {category = "Target", description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 5 letters)"}, - ["target:short:translit"] = {category = "Target", description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 10 letters)"}, - ["target:medium:translit"] = {category = "Target", description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 15 letters)"}, - ["target:long:translit"] = {category = "Target", description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 20 letters)"}, - --Threat - ["threat"] = {category = "Threat", description = "Displays the current threat"}, - ["threat:percent"] = {category = "Threat", description = "Displays the current threat as a percent"}, - ["threat:current"] = {category = "Threat", description = "Displays the current threat as a value"}, - --Miscellaneous - ["smartclass"] = {category = "Miscellaneous", description = "Displays the player's class or creature's type"}, - ["class"] = {category = "Miscellaneous", description = "Displays the class of the unit, if that unit is a player"}, - ["difficulty"] = {category = "Miscellaneous", description = "Changes color of the next tag based on how difficult the unit is compared to the players level"}, - ["faction"] = {category = "Miscellaneous", description = "Displays 'Aliance' or 'Horde'"}, - ["plus"] = {category = "Miscellaneous", description = "Displays the character '+' if the unit is an elite or rare-elite"}, - ["arena:number"] = {category = "Miscellaneous", description = "Displays the arena number 1-5"}, + -- Class + ['class'] = { category = 'Class', description = "Displays the class of the unit, if that unit is a player" }, + ['class:icon'] = { category = 'Class', description = "Displays the class icon of the unit, if that unit is a player" }, + ['smartclass'] = { category = 'Class', description = "Displays the player's class or creature's type" }, + -- Classification + ['affix'] = { category = 'Classification', description = "Displays low level critter mobs" }, + ['classification:icon'] = { category = 'Classification', description = "Displays the unit's classification in icon form (golden icon for 'ELITE' silver icon for 'RARE')" }, + ['classification'] = { category = 'Classification', description = "Displays the unit's classification (e.g. 'ELITE' and 'RARE')" }, + ['creature'] = { category = 'Classification', description = "Displays the creature type of the unit" }, + ['plus'] = { category = 'Classification', description = "Displays the character '+' if the unit is an elite or rare-elite" }, + ['rare'] = { category = 'Classification', description = "Displays 'Rare' when the unit is a rare or rareelite" }, + ['shortclassification'] = { category = 'Classification', description = "Displays the unit's classification in short form (e.g. '+' for ELITE and 'R' for RARE)" }, + -- Classpower + ['cpoints'] = { category = 'Classpower', description = "Displays amount of combo points the player has (only for player, shows nothing on 0)" }, + -- Colors + ['classificationcolor'] = { category = 'Colors', description = "Changes the text color, depending on the unit's classification" }, + ['difficulty'] = { category = 'Colors', description = "Changes color of the next tag based on how difficult the unit is compared to the players level" }, + ['difficultycolor'] = { category = 'Colors', description = "Colors the following tags by difficulty, red for impossible, orange for hard, green for easy" }, + ['healthcolor'] = { category = 'Colors', description = "Changes the text color, depending on the unit's current health" }, + ['selectioncolor'] = { category = 'Colors', description = "Colors the text, depending on the type of the unit's selection" }, + ['classcolor'] = { category = 'Colors', description = "Colors names by player class or NPC reaction (Ex: [classcolor][name])" }, + ['namecolor'] = { hidden = true, category = 'Colors', description = "Deprecated version of [classcolor]" }, + ['powercolor'] = { category = 'Colors', description = "Colors the power text based upon its type" }, + ['manacolor'] = { category = 'Colors', description = "Colors the power text based on the mana color" }, + ['ragecolor'] = { category = 'Colors', description = "Colors the power text based on the rage color" }, + ['energycolor'] = { category = 'Colors', description = "Colors the power text based on the energy color" }, + ['factioncolor'] = { category = 'Colors', description = "Colors names by Faction (Alliance, Horde, Neutral)" }, + ['reactioncolor'] = { category = 'Colors', description = "Colors names by NPC reaction (Bad/Neutral/Good)" }, + ['threatcolor'] = { category = 'Colors', description = "Changes the text color, depending on the unit's threat situation" }, + ['happiness:color'] = { category = 'Colors', description = "Changes the text color, depending on the pet happiness" }, + -- Guild + ['guild:brackets:translit'] = { category = 'Guild', description = "Displays the guild name with < > and transliteration (e.g. )" }, + ['guild:brackets'] = { category = 'Guild', description = "Displays the guild name with < > brackets (e.g. )" }, + ['guild:rank'] = { category = 'Guild', description = "Displays the guild rank" }, + ['guild:translit'] = { category = 'Guild', description = "Displays the guild name with transliteration for cyrillic letters" }, + ['guild'] = { category = 'Guild', description = "Displays the guild name" }, + -- Health + ['curhp'] = { category = 'Health', description = "Displays the current HP without decimals" }, + ['deficit:name'] = { category = 'Health', description = "Displays the health as a deficit and the name at full health" }, + ['health:current-max-nostatus:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current and max health, without status" }, + ['health:current-max-nostatus'] = { category = 'Health', description = "Displays the current and maximum health of the unit, separated by a dash, without status" }, + ['health:current-max-percent-nostatus:shortvalue'] = { category = 'Health', description = "Shortvalue of current and max hp (% when not full hp, without status)" }, + ['health:current-max-percent-nostatus'] = { category = 'Health', description = "Displays the current and max hp of the unit, separated by a dash (% when not full hp), without status" }, + ['health:current-max-percent:shortvalue'] = { category = 'Health', description = "Shortvalue of current and max hp (% when not full hp)" }, + ['health:current-max-percent'] = { category = 'Health', description = "Displays the current and max hp of the unit, separated by a dash (% when not full hp)" }, + ['health:current-max:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current and max hp, separated by a dash" }, + ['health:current-max'] = { category = 'Health', description = "Displays the current and maximum health of the unit, separated by a dash" }, + ['health:current-nostatus:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current health without status" }, + ['health:current-nostatus'] = { category = 'Health', description = "Displays the current health of the unit, without status" }, + ['health:current-percent-nostatus:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current hp (% when not full hp), without status" }, + ['health:current-percent-nostatus'] = { category = 'Health', description = "Displays the current hp of the unit (% when not full hp), without status" }, + ['health:current-percent:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current hp (% when not full hp)" }, + ['health:current-percent'] = { category = 'Health', description = "Displays the current hp of the unit (% when not full hp)" }, + ['health:current:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's current health (e.g. 81k instead of 81200)" }, + ['health:current'] = { category = 'Health', description = "Displays the current health of the unit" }, + ['health:deficit-nostatus:shortvalue'] = { category = 'Health', description = "Shortvalue of the health deficit, without status" }, + ['health:deficit-nostatus'] = { category = 'Health', description = "Displays the health of the unit as a deficit, without status" }, + ['health:deficit-percent:name-long'] = { category = 'Health', description = "Displays the health deficit as a percentage and the name of the unit (limited to 20 letters)" }, + ['health:deficit-percent:name-medium'] = { category = 'Health', description = "Displays the health deficit as a percentage and the name of the unit (limited to 15 letters)" }, + ['health:deficit-percent:name-short'] = { category = 'Health', description = "Displays the health deficit as a percentage and the name of the unit (limited to 10 letters)" }, + ['health:deficit-percent:name-veryshort'] = { category = 'Health', description = "Displays the health deficit as a percentage and the name of the unit (limited to 5 letters)" }, + ['health:deficit-percent:name'] = { category = 'Health', description = "Displays the health deficit as a percentage and the full name of the unit" }, + ['health:deficit-percent:nostatus'] = { category = 'Health', description = "Displays the health deficit as a percentage, without status" }, + ['health:deficit:shortvalue'] = { category = 'Health', description = "Shortvalue of the health deficit (e.g. -41k instead of -41300)" }, + ['health:deficit'] = { category = 'Health', description = "Displays the health of the unit as a deficit (Total Health - Current Health = -Deficit)" }, + ['health:max:shortvalue'] = { category = 'Health', description = "Shortvalue of the unit's maximum health" }, + ['health:max'] = { category = 'Health', description = "Displays the maximum health of the unit" }, + ['health:percent-nostatus'] = { category = 'Health', description = "Displays the unit's current health as a percentage, without status" }, + ['health:percent'] = { category = 'Health', description = "Displays the current health of the unit as a percentage" }, + ['maxhp'] = { category = 'Health', description = "Displays max HP without decimals" }, + ['missinghp'] = { category = 'Health', description = "Displays the missing health of the unit in whole numbers, when not at full health" }, + ['perhp'] = { category = 'Health', description = "Displays percentage HP without decimals or the % sign. You can display the percent sign by adjusting the tag to [perhp<%]." }, + --Hunter + ['diet'] = { category = 'Hunter', description = "Displays the diet of your pet (Fish, Meat, ...)" }, + ['happiness'] = { category = 'Hunter', description = "Displays the pet happiness in simple text emojis (e.g. :D)" }, + ['happiness:discord'] = { category = 'Hunter', description = "Displays the pet happiness like a Discord emoji" }, + ['happiness:full'] = { category = 'Hunter', description = "Displays the pet happiness as a word (e.g. 'Happy')" }, + ['happiness:icon'] = { category = 'Hunter', description = "Displays the pet happiness like the default Blizzard icon" }, + ['loyalty'] = { category = 'Hunter', description = "Displays the pet loyalty level" }, + -- Level + ['level'] = { category = 'Level', description = "Displays the level of the unit" }, + ['smartlevel'] = { category = 'Level', description = "Only display the unit's level if it is not the same as yours" }, + -- Mana + ['curmana'] = { category = 'Mana', description = "Displays the current mana without decimals" }, + ['mana:current-max-percent'] = { category = 'Mana', description = "Displays the current and max mana of the unit, separated by a dash (% when not full)" }, + ['mana:current-max'] = { category = 'Mana', description = "Displays the unit's current and maximum mana, separated by a dash" }, + ['mana:current-percent'] = { category = 'Mana', description = "Displays the current mana of the unit and % when not full" }, + ['mana:current'] = { category = 'Mana', description = "Displays the unit's current mana" }, + ['mana:deficit'] = { category = 'Mana', description = "Displays the player's mana as a deficit" }, + ['mana:percent'] = { category = 'Mana', description = "Displays the player's mana as a percentage" }, + ['maxmana'] = { category = 'Mana', description = "Displays the max amount of mana the unit can have" }, + ['mana:current-max-percent:shortvalue'] = { category = 'Mana', description = "" }, + ['mana:current-max:shortvalue'] = { category = 'Mana', description = "" }, + ['mana:current-percent:shortvalue'] = { category = 'Mana', description = "" }, + ['mana:current:shortvalue'] = { category = 'Mana', description = "" }, + ['mana:deficit:shortvalue'] = { category = 'Mana', description = "" }, + ['mana:max:shortvalue'] = { category = 'Mana', description = "" }, + -- Miscellaneous + ['race'] = { category = 'Miscellaneous', description = "Displays the race" }, + -- Names + ['name:abbrev:long'] = { category = 'Names', description = "Displays the name of the unit with abbreviation (limited to 20 letters)" }, + ['name:abbrev:medium'] = { category = 'Names', description = "Displays the name of the unit with abbreviation (limited to 15 letters)" }, + ['name:abbrev:short'] = { category = 'Names', description = "Displays the name of the unit with abbreviation (limited to 10 letters)" }, + ['name:abbrev:veryshort'] = { category = 'Names', description = "Displays the name of the unit with abbreviation (limited to 5 letters)" }, + ['name:abbrev'] = { category = 'Names', description = "Displays the name of the unit with abbreviation (e.g. 'Shadowfury Witch Doctor' becomes 'S. W. Doctor')" }, + ['name:last'] = { category = 'Names', description = "Displays the last word of the unit's name" }, + ['name:long:status'] = { category = 'Names', description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 20 letters)" }, + ['name:long:translit'] = { category = 'Names', description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 20 letters)" }, + ['name:long'] = { category = 'Names', description = "Displays the name of the unit (limited to 20 letters)" }, + ['name:medium:status'] = { category = 'Names', description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 15 letters)" }, + ['name:medium:translit'] = { category = 'Names', description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 15 letters)" }, + ['name:medium'] = { category = 'Names', description = "Displays the name of the unit (limited to 15 letters)" }, + ['name:short:status'] = { category = 'Names', description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 10 letters)" }, + ['name:short:translit'] = { category = 'Names', description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 10 letters)" }, + ['name:short'] = { category = 'Names', description = "Displays the name of the unit (limited to 10 letters)" }, + ['name:title'] = { category = 'Names', description = "Displays player name and title" }, + ['name:veryshort:status'] = { category = 'Names', description = "Replace the name of the unit with 'DEAD' or 'OFFLINE' if applicable (limited to 5 letters)" }, + ['name:veryshort:translit'] = { category = 'Names', description = "Displays the name of the unit with transliteration for cyrillic letters (limited to 5 letters)" }, + ['name:veryshort'] = { category = 'Names', description = "Displays the name of the unit (limited to 5 letters)" }, + ['name'] = { category = 'Names', description = "Displays the full name of the unit without any letter limitation" }, + ['name:health'] = { hidden = true, category = 'Names', description = "" }, + ['npctitle:brackets'] = { category = 'Names', description = "Displays the NPC title with brackets (e.g. )" }, + ['npctitle'] = { category = 'Names', description = "Displays the NPC title (e.g. General Goods Vendor)" }, + ['title'] = { category = 'Names', description = "Displays player title" }, + -- Party and Raid + ['group'] = { category = 'Party and Raid', description = "Displays the group number the unit is in (1-8)" }, + ['group:raid'] = { category = 'Party and Raid', description = "Displays the group number the unit is in (1-8): Only while in a raid." }, + ['leader'] = { category = 'Party and Raid', description = "Displays 'L' if the unit is the group/raid leader" }, + ['leaderlong'] = { category = 'Party and Raid', description = "Displays 'Leader' if the unit is the group/raid leader" }, + -- Power + ['curpp'] = { category = 'Power', description = "Displays the unit's current power without decimals" }, + ['maxpp'] = { category = 'Power', description = "Displays the max amount of power of the unit in whole numbers without decimals" }, + ['missingpp'] = { category = 'Power', description = "Displays the missing power of the unit in whole numbers when not at full power" }, + ['perpp'] = { category = 'Power', description = "Displays the unit's percentage power without decimals " }, + ['power:current-max-percent:shortvalue'] = { category = 'Power', description = "Shortvalue of the current power and max power, separated by a dash (% when not full power)" }, + ['power:current-max-percent'] = { category = 'Power', description = "Displays the current power and max power, separated by a dash (% when not full power)" }, + ['power:current-max:shortvalue'] = { category = 'Power', description = "Shortvalue of the current power and max power, separated by a dash" }, + ['power:current-max'] = { category = 'Power', description = "Displays the current power and max power, separated by a dash" }, + ['power:current-percent:shortvalue'] = { category = 'Power', description = "Shortvalue of the current power and power as a percentage, separated by a dash" }, + ['power:current-percent'] = { category = 'Power', description = "Displays the current power and power as a percentage, separated by a dash" }, + ['power:current:shortvalue'] = { category = 'Power', description = "Shortvalue of the unit's current amount of power (e.g. 4k instead of 4000)" }, + ['power:current'] = { category = 'Power', description = "Displays the unit's current amount of power" }, + ['power:deficit:shortvalue'] = { category = 'Power', description = "Shortvalue of the power as a deficit (Total Power - Current Power = -Deficit)" }, + ['power:deficit'] = { category = 'Power', description = "Displays the power as a deficit (Total Power - Current Power = -Deficit)" }, + ['power:max:shortvalue'] = { category = 'Power', description = "Shortvalue of the unit's maximum power" }, + ['power:max'] = { category = 'Power', description = "Displays the unit's maximum power" }, + ['power:percent'] = { category = 'Power', description = "Displays the unit's power as a percentage" }, + --Energy + ["energy:current"] = {category = "Energy", description = "Displays the unit's current amount of energy"}, + ["energy:current-percent"] = {category = "Energy", description = "Displays the current energy and energy as a percentage, separated by a dash"}, + ["energy:current-max"] = {category = "Energy", description = "Displays the current energy and max energy, separated by a dash"}, + ["energy:current-max-percent"] = {category = "Energy", description = "Displays the current energy and max energy, separated by a dash (% when not full energy)"}, + ["energy:percent"] = {category = "Energy", description = "Displays the unit's energy as a percentage"}, + ["energy:max"] = {category = "Energy", description = "Displays the unit's maximum energy"}, + ["energy:deficit"] = {category = "Energy", description = "Displays the energy as a deficit (Total Energy - Current Energy = -Deficit)"}, + --Rage + ["rage:current"] = {category = "Rage", description = "Displays the unit's current amount of rage"}, + ["rage:current-percent"] = {category = "Rage", description = "Displays the current rage and rage as a percentage, separated by a dash"}, + ["rage:current-max"] = {category = "Rage", description = "Displays the current rage and max rage, separated by a dash"}, + ["rage:current-max-percent"] = {category = "Rage", description = "Displays the current rage and max rage, separated by a dash (% when not full rage)"}, + ["rage:percent"] = {category = "Rage", description = "Displays the unit's rage as a percentage"}, + ["rage:max"] = {category = "Rage", description = "Displays the unit's maximum rage"}, + ["rage:deficit"] = {category = "Rage", description = "Displays the rage as a deficit (Total Rage - Current Rage = -Deficit)"}, + -- PvP + ['arena:number'] = { category = 'PvP', description = "Displays the arena number 1-5" }, + ['faction:icon'] = { category = 'PvP', description = "Displays the 'Alliance' or 'Horde' texture" }, + ['faction'] = { category = 'PvP', description = "Displays 'Alliance' or 'Horde'" }, + ['pvp'] = { category = 'PvP', description = "Displays 'PvP' if the unit is pvp flagged" }, + ['pvptimer'] = { category = 'PvP', description = "Displays remaining time on pvp-flagged status" }, + ['pvp:icon'] = { category = 'PvP', description = "Displays player pvp rank icon" }, + ['pvp:rank'] = { category = 'PvP', description = "Displays player pvp rank number" }, + ['pvp:title'] = { category = 'PvP', description = "Displays player pvp title" }, + -- Quest + ['quest:info'] = { category = 'Quest', description = "Displays the quest objectives" }, + ['quest:title'] = { category = 'Quest', description = "Displays the quest title" }, + ['quest:count'] = { category = 'Quest', description = "Displays the quest count" }, + ['quest:full'] = { category = 'Quest', description = "Quest full" }, + ['quest:text'] = { category = 'Quest', description = "Quest text" }, + -- Range + ['range'] = { category = 'Range', description = "Displays the range" }, + ['range:min'] = { category = 'Range', description = "Displays the min range" }, + ['range:max'] = { category = 'Range', description = "Displays the max range" }, + ['distance'] = { category = 'Range', description = "Displays the distance" }, + ['nearbyplayers:4'] = { category = 'Range', description = "Displays all players within 4 yards" }, + ['nearbyplayers:8'] = { category = 'Range', description = "Displays all players within 8 yards" }, + ['nearbyplayers:10'] = { category = 'Range', description = "Displays all players within 10 yards" }, + ['nearbyplayers:15'] = { category = 'Range', description = "Displays all players within 15 yards" }, + ['nearbyplayers:20'] = { category = 'Range', description = "Displays all players within 20 yards" }, + ['nearbyplayers:25'] = { category = 'Range', description = "Displays all players within 25 yards" }, + ['nearbyplayers:30'] = { category = 'Range', description = "Displays all players within 30 yards" }, + ['nearbyplayers:35'] = { category = 'Range', description = "Displays all players within 35 yards" }, + ['nearbyplayers:40'] = { category = 'Range', description = "Displays all players within 40 yards" }, + -- Realm + ['realm:dash:translit'] = { category = 'Realm', description = "Displays the server name with transliteration for cyrillic letters and a dash in front" }, + ['realm:dash'] = { category = 'Realm', description = "Displays the server name with a dash in front (e.g. -Realm)" }, + ['realm:translit'] = { category = 'Realm', description = "Displays the server name with transliteration for cyrillic letters" }, + ['realm'] = { category = 'Realm', description = "Displays the server name" }, + -- Speed + ['speed:percent-moving-raw'] = { category = 'Speed' }, + ['speed:percent-moving'] = { category = 'Speed' }, + ['speed:percent-raw'] = { category = 'Speed' }, + ['speed:percent'] = { category = 'Speed' }, + ['speed:yardspersec-moving-raw'] = { category = 'Speed' }, + ['speed:yardspersec-moving'] = { category = 'Speed' }, + ['speed:yardspersec-raw'] = { category = 'Speed' }, + ['speed:yardspersec'] = { category = 'Speed' }, + -- Status + ['afk'] = { category = 'Status', description = "Displays if the unit is afk" }, + ['dead'] = { category = 'Status', description = "Displays if the unit is dead" }, + ['ElvUI-Users'] = { category = 'Status', description = "Displays current ElvUI users" }, + ['offline'] = { category = 'Status', description = "Displays 'OFFLINE' if the unit is disconnected" }, + ['resting'] = { category = 'Status', description = "Displays 'zzz' if the unit is resting" }, + ['status:icon'] = { category = 'Status', description = "Displays AFK/DND as an orange(afk) / red(dnd) icon" }, + ['status:text'] = { category = 'Status', description = "Displays and " }, + ['status'] = { category = 'Status', description = "Displays zzz, dead, ghost, offline" }, + ['statustimer'] = { category = 'Status', description = "Displays a timer for how long a unit has had the status (e.g 'DEAD - 0:34')" }, + -- Target + ['classcolor:target'] = { category = 'Target', description = "[classcolor] but for the current target of the unit" }, + ['target:long:translit'] = { category = 'Target', description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 20 letters)" }, + ['target:long'] = { category = 'Target', description = "Displays the current target of the unit (limited to 20 letters)" }, + ['target:medium:translit'] = { category = 'Target', description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 15 letters)" }, + ['target:medium'] = { category = 'Target', description = "Displays the current target of the unit (limited to 15 letters)" }, + ['target:short:translit'] = { category = 'Target', description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 10 letters)" }, + ['target:short'] = { category = 'Target', description = "Displays the current target of the unit (limited to 10 letters)" }, + ['target:translit'] = { category = 'Target', description = "Displays the current target of the unit with transliteration for cyrillic letters" }, + ['target:veryshort:translit'] = { category = 'Target', description = "Displays the current target of the unit with transliteration for cyrillic letters (limited to 5 letters)" }, + ['target:veryshort'] = { category = 'Target', description = "Displays the current target of the unit (limited to 5 letters)" }, + ['target'] = { category = 'Target', description = "Displays the current target of the unit" }, + -- Threat + ['threat:current'] = { category = 'Threat', description = "Displays the current threat as a value" }, + ['threat:percent'] = { category = 'Threat', description = "Displays the current threat as a percent" }, + ['threat'] = { category = 'Threat', description = "Displays the current threat situation (Aggro is secure tanking, -- is losing threat and ++ is gaining threat)" }, } -function E:AddTagInfo(tagName, category, description, order) - if order then order = tonumber(order) + 10 end +--[[ + tagName = Tag Name + category = Category that you want it to fall in + description = self explainitory + order = This is optional. It's used for sorting the tags by order and not by name. The +10 is not a rule. I reserve the first 10 slots. +]] - E.TagInfo[tagName] = E.TagInfo[tagName] or {} - E.TagInfo[tagName].category = category or "Miscellaneous" - E.TagInfo[tagName].description = description or "" - E.TagInfo[tagName].order = order or nil +function E:AddTagInfo(tagName, category, description, order, hidden) + if type(order) == 'number' then order = order + 10 else order = nil end + + local info = E.TagInfo[tagName] + if not info then + info = {} + + E.TagInfo[tagName] = info + end + + info.category = category or 'Miscellaneous' + info.description = description or '' + info.order = order or nil + info.hidden = hidden or nil + + return info end + +RefreshNewTags = true diff --git a/ElvUI/Core/Toolkit.lua b/ElvUI/Core/Toolkit.lua index edb862b..bcf6244 100644 --- a/ElvUI/Core/Toolkit.lua +++ b/ElvUI/Core/Toolkit.lua @@ -1,5 +1,6 @@ local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB local LSM = E.Libs.LSM +local NP = E:GetModule('NamePlates') --Lua functions local _G = _G @@ -78,7 +79,7 @@ local function SetInside(obj, anchor, xOffset, yOffset, anchor2) obj:Point("BOTTOMRIGHT", anchor2 or anchor, "BOTTOMRIGHT", -xOffset, yOffset) end -local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement) +local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement) GetTemplate(template, isUnitFrameElement) frame.template = template or "Default" @@ -86,10 +87,25 @@ local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelM if ignoreUpdates then frame.ignoreUpdates = ignoreUpdates end if forcePixelMode then frame.forcePixelMode = forcePixelMode end if isUnitFrameElement then frame.isUnitFrameElement = isUnitFrameElement end + frame.isNamePlateElement = isNamePlateElement local bgFile = glossTex and E.media.glossTex or E.media.blankTex - if template ~= "NoBackdrop" then + if isNamePlateElement then + local bg = frame:CreateTexture(nil, "BACKGROUND") + bg:SetAllPoints() + GetTemplate(template, isUnitFrameElement) + --bg:SetTexture(bgFile) + bg:SetTexture(backdropr, backdropg, backdropb, backdropa) + if not frame.ignoreUpdates then + if frame.isUnitFrameElement then + E.unitFrameElements[frame] = true + else + E.frames[frame] = true + end + end + return + elseif template ~= "NoBackdrop" then frame:SetBackdrop({ bgFile = bgFile, edgeFile = E.media.blankTex, @@ -99,7 +115,8 @@ local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelM frame:SetBackdropColor(backdropr, backdropg, backdropb, backdropa) - if not E.PixelMode and not frame.forcePixelMode then + local notPixelMode = not isUnitFrameElement and not isNamePlateElement and not E.PixelMode + if notPixelMode and not frame.forcePixelMode then if not frame.iborder then local border = CreateFrame("Frame", nil, frame) border:SetInside(frame, E.mult, E.mult) @@ -139,7 +156,7 @@ local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelM end end -local function CreateBackdrop(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement) +local function CreateBackdrop(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement) if not template then template = "Default" end local parent = (frame.IsObjectType and frame:IsObjectType("Texture") and frame:GetParent()) or frame @@ -147,12 +164,13 @@ local function CreateBackdrop(frame, template, glossTex, ignoreUpdates, forcePix if not frame.backdrop then frame.backdrop = backdrop end if frame.forcePixelMode or forcePixelMode then - backdrop:SetOutside(frame, E.mult, E.mult) + backdrop:SetOutside(frame, E.twoPixelsPlease and 2 or 1, E.twoPixelsPlease and 2 or 1) else - backdrop:SetOutside(frame) + local border = isNamePlateElement and NP.BORDER or nil + backdrop:SetOutside(frame, border, border) end - - backdrop:SetTemplate(template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement) + + backdrop:SetTemplate(template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement) local frameLevel = parent.GetFrameLevel and parent:GetFrameLevel() local frameLevelMinusOne = frameLevel and (frameLevel - 1) diff --git a/ElvUI/ElvUI.toc b/ElvUI/ElvUI.toc index 3531b90..9aaf325 100644 --- a/ElvUI/ElvUI.toc +++ b/ElvUI/ElvUI.toc @@ -1,6 +1,6 @@ ## Interface: 30300 ## Author: Elv, Bunny -## Version: 6.18 +## Version: 7.0 ## Title: |cff1784d1E|r|cffe5e3e3lvUI|r ## Notes: User Interface replacement AddOn for World of Warcraft. ## SavedVariables: ElvDB, ElvPrivateDB diff --git a/ElvUI/Init.lua b/ElvUI/Init.lua index 12d185d..7982c04 100644 --- a/ElvUI/Init.lua +++ b/ElvUI/Init.lua @@ -346,7 +346,7 @@ function AddOn:ToggleOptionsUI(msg) if not IsAddOnLoaded("ElvUI_OptionsUI") then noConfig = true end -- version check elvui options if it's actually enabled - if (not noConfig) and GetAddOnMetadata("ElvUI_OptionsUI", "Version") ~= "1.06" then + if (not noConfig) and GetAddOnMetadata("ElvUI_OptionsUI", "Version") ~= "1.10" then self:StaticPopup_Show("CLIENT_UPDATE_REQUEST") end else diff --git a/ElvUI/Libraries/oUF/blizzard.lua b/ElvUI/Libraries/oUF/blizzard.lua index dd46de3..c537ea1 100644 --- a/ElvUI/Libraries/oUF/blizzard.lua +++ b/ElvUI/Libraries/oUF/blizzard.lua @@ -14,6 +14,10 @@ local hiddenParent = CreateFrame('Frame', nil, UIParent) hiddenParent:SetAllPoints() hiddenParent:Hide() +local offScreenParent = CreateFrame('Frame', nil, UIParent) +offScreenParent:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", 0, -128) +offScreenParent:SetFrameLevel(0) + local function handleFrame(baseName) local frame if(type(baseName) == 'string') then @@ -51,6 +55,34 @@ local function handleFrame(baseName) end end +function oUF:DisableBlizzardNamePlate(nameplate) + -- we have to preserve the base frame since the unit frame will attach to it + local blizzElements = {nameplate:GetRegions()} + local healthBar, castBar = nameplate:GetChildren() + tinsert(blizzElements, healthBar) + + nameplate.blizzHighlight = blizzElements[6] + nameplate.HealthBar = healthBar + nameplate.CastBar = castBar + + for _, child in ipairs(blizzElements) do + child:SetParent(hiddenParent) + child:SetAlpha(0) + child:Hide() + if child.SetTexture then + child:SetTexture() + elseif child.SetStatusBarTexture then + child:SetStatusBarTexture(nil) -- this needs nil + end + end + + -- cast bar has to be special because we need onhide / onshow to fire still + castBar:SetParent(offScreenParent) + castBar:SetStatusBarTexture(nil) + castBar:SetAlpha(0) + castBar:Hide() +end + function oUF:DisableBlizzard(unit) if(not unit) then return end diff --git a/ElvUI/Libraries/oUF/colors.lua b/ElvUI/Libraries/oUF/colors.lua index f672759..b8fdfb4 100644 --- a/ElvUI/Libraries/oUF/colors.lua +++ b/ElvUI/Libraries/oUF/colors.lua @@ -31,15 +31,15 @@ local colors = { } for classToken, color in next, RAID_CLASS_COLORS do - colors.class[classToken] = {color.r, color.g, color.b} + colors.class[classToken] = {color.r, color.g, color.b, r = color.r, g = color.g, b = color.b} end for debuffType, color in next, DebuffTypeColor do - colors.debuff[debuffType] = {color.r, color.g, color.b} + colors.debuff[debuffType] = {color.r, color.g, color.b, r = color.r, g = color.g, b = color.b} end for eclass, color in next, FACTION_BAR_COLORS do - colors.reaction[eclass] = {color.r, color.g, color.b} + colors.reaction[eclass] = {color.r, color.g, color.b, r = color.r, g = color.g, b = color.b} end for power, color in next, PowerBarColor do @@ -48,10 +48,10 @@ for power, color in next, PowerBarColor do colors.power[power] = {} for index, color in next, color do - colors.power[power][index] = {color.r, color.g, color.b} + colors.power[power][index] = {color.r, color.g, color.b, r = color.r, g = color.g, b = color.b} end else - colors.power[power] = {color.r, color.g, color.b, atlas = color.atlas} + colors.power[power] = {color.r, color.g, color.b, atlas = color.atlas, r = color.r, g = color.g, b = color.b} end end end diff --git a/ElvUI/Libraries/oUF/elements/auras.lua b/ElvUI/Libraries/oUF/elements/auras.lua index b7472b7..a33bed8 100644 --- a/ElvUI/Libraries/oUF/elements/auras.lua +++ b/ElvUI/Libraries/oUF/elements/auras.lua @@ -356,6 +356,13 @@ local function filterIcons(element, unit, filter, limit, isDebuff, offset, dontH end local function UpdateAuras(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end + if(self.unit ~= unit) then return end local auras = self.Auras @@ -502,6 +509,12 @@ local function UpdateAuras(self, event, unit) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end UpdateAuras(self, event, unit) diff --git a/ElvUI/Libraries/oUF/elements/castbar.lua b/ElvUI/Libraries/oUF/elements/castbar.lua index fea5633..e924f14 100644 --- a/ElvUI/Libraries/oUF/elements/castbar.lua +++ b/ElvUI/Libraries/oUF/elements/castbar.lua @@ -103,6 +103,7 @@ local function resetAttributes(self) self.channeling = nil self.notInterruptible = nil self.spellName = nil -- ElvUI + self.spellID = nil end -- ElvUI block @@ -113,13 +114,19 @@ end -- end block local function CastStart(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Castbar - local name, _, _, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo(unit) + local name, _, _, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID = UnitCastingInfo(unit) event = 'UNIT_SPELLCAST_START' if(not name) then - name, _, _, texture, startTime, endTime, isTradeSkill, notInterruptible = UnitChannelInfo(unit) + name, _, _, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID = UnitChannelInfo(unit) event = 'UNIT_SPELLCAST_CHANNEL_START' end @@ -141,6 +148,7 @@ local function CastStart(self, event, unit) element.notInterruptible = notInterruptible element.holdTime = 0 element.castID = castID + element.spellID = spellID element.spellName = name -- ElvUI if(element.casting) then @@ -205,6 +213,12 @@ local function CastStart(self, event, unit) end local function CastUpdate(self, event, unit, _, _, castID) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Castbar @@ -258,6 +272,12 @@ local function CastUpdate(self, event, unit, _, _, castID) end local function CastStop(self, event, unit, _, _, castID) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Castbar @@ -287,6 +307,12 @@ local function CastStop(self, event, unit, _, _, castID) end local function CastFail(self, event, unit, _, _, castID) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Castbar @@ -324,6 +350,12 @@ local function CastFail(self, event, unit, _, _, castID) end local function CastInterruptible(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Castbar @@ -408,22 +440,43 @@ local function ForceUpdate(element) return Update(element.__owner, 'ForceUpdate', element.__owner.unit) end +local function NamePlateCastBarOnShow(self) + if C_NamePlateManager.IsNamePlateMoving(self) then return end + CastStart(self, "UNIT_SPELLCAST_START", self.unit) +end + +local function NamePlateCastBarOnHide(self) + if C_NamePlateManager.IsNamePlateMoving(self) then return end + CastFail(self, "UNIT_SPELLCAST_FAILED", self.unit, nil, nil, self.Castbar.castID) +end + +local function NamePlateCastBarOnValueChanged(self) + CastUpdate(self, nil, self.unit, nil, nil, self.Castbar.castID) +end + local function Enable(self, unit) local element = self.Castbar if(element and unit and not unit:match('%wtarget$')) then element.__owner = self element.ForceUpdate = ForceUpdate - self:RegisterEvent('UNIT_SPELLCAST_START', CastStart) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) - self:RegisterEvent('UNIT_SPELLCAST_STOP', CastStop) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) - self:RegisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) - self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) - self:RegisterEvent('UNIT_SPELLCAST_FAILED', CastFail) - self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) - self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) - self:RegisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) + if self.isNamePlate then + local castBar = self.nameplateAnchor.CastBar + castBar:SetScript("OnShow", GenerateClosure(NamePlateCastBarOnShow, self)) + castBar:SetScript("OnHide", GenerateClosure(NamePlateCastBarOnHide, self)) + castBar:SetScript("OnValueChanged", GenerateClosure(NamePlateCastBarOnValueChanged, self)) + else + self:RegisterEvent('UNIT_SPELLCAST_START', CastStart) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) + self:RegisterEvent('UNIT_SPELLCAST_STOP', CastStop) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) + self:RegisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) + self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) + self:RegisterEvent('UNIT_SPELLCAST_FAILED', CastFail) + self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) + self:RegisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) + self:RegisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) + end -- ElvUI block self:RegisterEvent('UNIT_SPELLCAST_SENT', UNIT_SPELLCAST_SENT, true) @@ -454,7 +507,7 @@ local function Enable(self, unit) local safeZone = element.SafeZone if(safeZone and safeZone:IsObjectType('Texture') and not safeZone:GetTexture()) then - safeZone:SetColorTexture(1, 0, 0) + safeZone:SetTexture(1, 0, 0) end element:Hide() @@ -468,16 +521,23 @@ local function Disable(self) if(element) then element:Hide() - self:UnregisterEvent('UNIT_SPELLCAST_START', CastStart) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) - self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) - self:UnregisterEvent('UNIT_SPELLCAST_STOP', CastStop) - self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) - self:UnregisterEvent('UNIT_SPELLCAST_FAILED', CastFail) - self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) - self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) - self:UnregisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) + if self.isNamePlate then + local castBar = self.nameplateAnchor.CastBar + castBar:SetScript("OnShow", nil) + castBar:SetScript("OnHide", nil) + castBar:SetScript("OnValueChanged", nil) + else + self:UnregisterEvent('UNIT_SPELLCAST_START', CastStart) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_START', CastStart) + self:UnregisterEvent('UNIT_SPELLCAST_DELAYED', CastUpdate) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_UPDATE', CastUpdate) + self:UnregisterEvent('UNIT_SPELLCAST_STOP', CastStop) + self:UnregisterEvent('UNIT_SPELLCAST_CHANNEL_STOP', CastStop) + self:UnregisterEvent('UNIT_SPELLCAST_FAILED', CastFail) + self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTED', CastFail) + self:UnregisterEvent('UNIT_SPELLCAST_INTERRUPTIBLE', CastInterruptible) + self:UnregisterEvent('UNIT_SPELLCAST_NOT_INTERRUPTIBLE', CastInterruptible) + end element:SetScript('OnUpdate', nil) @@ -501,6 +561,12 @@ end) oUF:AddElement('Castbar', Update, Enable, Disable) function CastingBarFrame_SetUnit(self, unit, showTradeSkills, showShield) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then self.unit = unit self.showTradeSkills = showTradeSkills diff --git a/ElvUI/Libraries/oUF/elements/combopoints.lua b/ElvUI/Libraries/oUF/elements/combopoints.lua index e7c47ac..e99e5a5 100644 --- a/ElvUI/Libraries/oUF/elements/combopoints.lua +++ b/ElvUI/Libraries/oUF/elements/combopoints.lua @@ -37,6 +37,12 @@ local UnitHasVehicleUI = UnitHasVehicleUI local MAX_COMBO_POINTS = MAX_COMBO_POINTS local function Update(self, event, unit) + if self.isNamePlate then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(unit == 'pet') then return end local element = self.ComboPoints diff --git a/ElvUI/Libraries/oUF/elements/happinessindicator.lua b/ElvUI/Libraries/oUF/elements/happinessindicator.lua index f611398..2c553ca 100644 --- a/ElvUI/Libraries/oUF/elements/happinessindicator.lua +++ b/ElvUI/Libraries/oUF/elements/happinessindicator.lua @@ -30,6 +30,12 @@ local GetPetHappiness = GetPetHappiness local HasPetUI = HasPetUI local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(not unit or self.unit ~= unit) then return end local element = self.HappinessIndicator diff --git a/ElvUI/Libraries/oUF/elements/health.lua b/ElvUI/Libraries/oUF/elements/health.lua index 693ce42..e9b0c71 100644 --- a/ElvUI/Libraries/oUF/elements/health.lua +++ b/ElvUI/Libraries/oUF/elements/health.lua @@ -86,6 +86,12 @@ local oUF = ns.oUF local Private = oUF.Private local function UpdateColor(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(not unit or self.unit ~= unit) then return end local element = self.Health @@ -146,7 +152,14 @@ local function ColorPath(self, ...) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(not unit or self.unit ~= unit) then return end + local element = self.Health --[[ Callback: Health:PreUpdate(unit) @@ -200,7 +213,6 @@ local function Path(self, ...) * unit - the unit accompanying the event (string) --]] (self.Health.Override or Update) (self, ...); - ColorPath(self, ...) end @@ -242,6 +254,23 @@ local function SetColorHappiness(element, state) end end +--[[ Health:SetColorSelection(state, isForced) +Used to toggle coloring by the unit's selection. +* self - the Health element +* state - the desired state (boolean) +* isForced - forces the event update even if the state wasn't changed (boolean) +--]] +local function SetColorSelection(element, state, isForced) + if(element.colorSelection ~= state or isForced) then + element.colorSelection = state + if(state) then + element.__owner:RegisterEvent('UNIT_FLAGS', ColorPath) + else + element.__owner:UnregisterEvent('UNIT_FLAGS', ColorPath) + end + end +end + --[[ Health:SetColorTapping(state) Used to toggle coloring if the unit isn't tapped by the player. @@ -320,6 +349,7 @@ local function Enable(self, unit) element.__owner = self element.ForceUpdate = ForceUpdate element.SetColorDisconnected = SetColorDisconnected + element.SetColorSelection = SetColorSelection element.SetColorHappiness = SetColorHappiness element.SetColorTapping = SetColorTapping element.SetColorThreat = SetColorThreat @@ -354,6 +384,16 @@ local function Enable(self, unit) self:RegisterEvent('UNIT_MAXHEALTH', Path) + if self.isNamePlate then + local healthBar = self.nameplateAnchor.HealthBar + healthBar:SetScript("OnValueChanged", function() + Path(self, "UNIT_HEALTH", self.unit) + end) + healthBar:SetScript("OnMinMaxChanged", function() + Path(self, "UNIT_MAXHEALTH", self.unit) + end) + end + if(element:IsObjectType('StatusBar') and not element:GetStatusBarTexture()) then element:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]]) end diff --git a/ElvUI/Libraries/oUF/elements/portrait.lua b/ElvUI/Libraries/oUF/elements/portrait.lua index 2ca381a..4a8410a 100644 --- a/ElvUI/Libraries/oUF/elements/portrait.lua +++ b/ElvUI/Libraries/oUF/elements/portrait.lua @@ -43,6 +43,12 @@ local UnitIsUnit = UnitIsUnit local UnitIsVisible = UnitIsVisible local function Update(self, event, unit) + if self.isNamePlate then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(not unit or not UnitIsUnit(self.unit, unit)) then return end local element = self.Portrait diff --git a/ElvUI/Libraries/oUF/elements/power.lua b/ElvUI/Libraries/oUF/elements/power.lua index 31765e5..85f4050 100644 --- a/ElvUI/Libraries/oUF/elements/power.lua +++ b/ElvUI/Libraries/oUF/elements/power.lua @@ -100,6 +100,12 @@ local UnitPowerType = UnitPowerType local UnitReaction = UnitReaction local function UpdateColor(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Power @@ -170,6 +176,12 @@ local function ColorPath(self, ...) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Power diff --git a/ElvUI/Libraries/oUF/elements/powerenergy.lua b/ElvUI/Libraries/oUF/elements/powerenergy.lua index 2fef6d2..560b242 100644 --- a/ElvUI/Libraries/oUF/elements/powerenergy.lua +++ b/ElvUI/Libraries/oUF/elements/powerenergy.lua @@ -17,6 +17,12 @@ local UnitPowerType = UnitPowerType local UnitReaction = UnitReaction local function UpdateColor(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Energy @@ -84,6 +90,12 @@ local function ColorPath(self, ...) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Energy diff --git a/ElvUI/Libraries/oUF/elements/powerrage.lua b/ElvUI/Libraries/oUF/elements/powerrage.lua index 3a97067..f142752 100644 --- a/ElvUI/Libraries/oUF/elements/powerrage.lua +++ b/ElvUI/Libraries/oUF/elements/powerrage.lua @@ -17,6 +17,12 @@ local UnitPowerType = UnitPowerType local UnitReaction = UnitReaction local function UpdateColor(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Rage @@ -84,6 +90,12 @@ local function ColorPath(self, ...) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(self.unit ~= unit) then return end local element = self.Rage diff --git a/ElvUI/Libraries/oUF/elements/pvpindicator.lua b/ElvUI/Libraries/oUF/elements/pvpindicator.lua index fcf1798..4a76b07 100644 --- a/ElvUI/Libraries/oUF/elements/pvpindicator.lua +++ b/ElvUI/Libraries/oUF/elements/pvpindicator.lua @@ -33,6 +33,12 @@ local FFA_ICON = [[Interface\TargetingFrame\UI-PVP-FFA]] local FACTION_ICON = [[Interface\TargetingFrame\UI-PVP-]] local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if(unit ~= self.unit) then return end local element = self.PvPIndicator diff --git a/ElvUI/Libraries/oUF/elements/threatindicator.lua b/ElvUI/Libraries/oUF/elements/threatindicator.lua index 79234f6..f637f8e 100644 --- a/ElvUI/Libraries/oUF/elements/threatindicator.lua +++ b/ElvUI/Libraries/oUF/elements/threatindicator.lua @@ -36,7 +36,14 @@ local UnitExists = UnitExists local UnitThreatSituation = UnitThreatSituation local function Update(self, event, unit) - if(not unit or self.unit ~= unit) then return end + if not unit then return end + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end + if(self.unit ~= unit) then return end local element = self.ThreatIndicator --[[ Callback: ThreatIndicator:PreUpdate(unit) diff --git a/ElvUI/Libraries/oUF/ouf.lua b/ElvUI/Libraries/oUF/ouf.lua index 03082e6..dcd4981 100644 --- a/ElvUI/Libraries/oUF/ouf.lua +++ b/ElvUI/Libraries/oUF/ouf.lua @@ -311,6 +311,10 @@ local function togglemenu(self, unit) end local function onShow(self) + if self.isNamePlate then + local nameplate = C_NamePlate.GetNamePlateForUnit(self.unit) + if nameplate and C_NamePlateManager.IsNamePlateMoving(nameplate.unitFrame) then return end + end if(not updateActiveUnit(self, 'OnShow')) then return self:UpdateAllElements('OnShow') end @@ -417,7 +421,6 @@ local function initObject(unit, style, styleFunc, header, ...) activeElements[object] = {} -- ElvUI: styleFunc on headers break before this is set when they try to enable elements before it's set. Private.UpdateUnits(object, objectUnit) - styleFunc(object, objectUnit, not header) object:HookScript('OnAttributeChanged', onAttributeChanged) @@ -761,6 +764,116 @@ function oUF:Spawn(unit, overrideName) return object end +--[[ oUF:SpawnNamePlates(prefix, callback, variables) +Used to create nameplates and apply the currently active style to them. + +* self - the global oUF object +* prefix - prefix for the global name of the nameplate. Defaults to an auto-generated prefix (string?) +* callback - function to be called after a nameplate unit or the player's target has changed. The arguments passed to + the callback are the updated nameplate, if any, the event that triggered the update, and the new unit + (function?) +* variables - list of console variable-value pairs to be set when the player logs in (table?) +--]] +local nameplateUnitToFrame = {} +function oUF:SpawnNamePlates(namePrefix, nameplateCallback, nameplateCVars) + argcheck(nameplateCallback, 3, 'function', 'nil') + argcheck(nameplateCVars, 4, 'table', 'nil') + if(not style) then return error('Unable to create frame. No styles have been registered.') end + if(_G.oUF_NamePlateDriver) then return error('oUF nameplate driver has already been initialized.') end + + local style = style + local prefix = namePrefix or generateName() + + local eventHandler = CreateFrame('Frame', 'oUF_NamePlateDriver') + eventHandler:RegisterEvent('NAME_PLATE_UNIT_ADDED') + eventHandler:RegisterEvent('NAME_PLATE_UNIT_REMOVED') + eventHandler:RegisterEvent('PLAYER_TARGET_CHANGED') + eventHandler:RegisterEvent('UNIT_FACTION') + eventHandler:RegisterEvent('UNIT_FLAGS') + + if(IsLoggedIn()) then + if(nameplateCVars) then + for cvar, value in next, nameplateCVars do + SetCVar(cvar, value) + end + end + else + eventHandler:RegisterEvent('PLAYER_LOGIN') + end + + C_NamePlateManager.SetEnableResizeNamePlates(true) + + eventHandler:SetScript('OnEvent', function(_, event, unit) + if(event == 'PLAYER_LOGIN') then + if(nameplateCVars) then + for cvar, value in next, nameplateCVars do + SetCVar(cvar, value) + end + end + elseif(event == 'PLAYER_TARGET_CHANGED') then + local nameplate = C_NamePlate.GetNamePlateForUnit('target') + if(nameplateCallback) then + nameplateCallback(nameplate and nameplate.unitFrame, event, 'target') + end + + -- UAE is called after the callback to reduce the number of + -- ForceUpdate calls layout devs have to do themselves + if(nameplate) then + nameplate.unitFrame:UpdateAllElements(event) + end + elseif(event == 'UNIT_FACTION' and unit) then + local nameplate = C_NamePlate.GetNamePlateForUnit(unit) + if(not nameplate) then return end + + if(nameplateCallback) then + nameplateCallback(nameplate.unitFrame, event, unit) + end + elseif(event == 'NAME_PLATE_UNIT_ADDED' and unit) then + local nameplate = C_NamePlate.GetNamePlateForUnit(unit) + if(not nameplate) then return end + nameplateUnitToFrame[unit] = nameplate + + if(not nameplate.unitFrame) then + self:DisableBlizzardNamePlate(nameplate) + nameplate.style = style + nameplate.isNamePlate = true + + nameplate.unitFrame = CreateFrame('Button', prefix..tostring(nameplate), nameplate) + nameplate.unitFrame:EnableMouse(false) + nameplate.unitFrame.isNamePlate = true + nameplate.unitFrame.nameplateAnchor = nameplate + + Private.UpdateUnits(nameplate.unitFrame, unit) + walkObject(nameplate.unitFrame, unit) + C_NamePlateManager.ApplyFPSIncrease(nameplate.unitFrame) + else + -- for _, child in ipairs(nameplate.blizzElements) do + -- ClearNamePlateElement(child) + -- end + Private.UpdateUnits(nameplate.unitFrame, unit) + end + + nameplate.unitFrame:SetAttribute('unit', unit) + + if(nameplateCallback) then + nameplateCallback(nameplate.unitFrame, event, unit) + end + + -- UAE is called after the callback to reduce the number of + -- ForceUpdate calls layout devs have to do themselves + nameplate.unitFrame:UpdateAllElements(event) + elseif(event == 'NAME_PLATE_UNIT_REMOVED' and unit) then + local nameplate = nameplateUnitToFrame[unit] + if(not nameplate) then return end + nameplateUnitToFrame[unit] = nil + nameplate.unitFrame:SetAttribute('unit', nil) + if(nameplateCallback) then + nameplateCallback(nameplate.unitFrame, event, unit) + end + end + end) +end + --[[ oUF:AddElement(name, update, enable, disable) Used to register an element with oUF. diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_AuraBars/oUF_AuraBars.lua b/ElvUI/Libraries/oUF_Plugins/oUF_AuraBars/oUF_AuraBars.lua index 12e6b86..23c05dd 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_AuraBars/oUF_AuraBars.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_AuraBars/oUF_AuraBars.lua @@ -190,6 +190,12 @@ local function sortByTime(a, b) end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if self.unit ~= unit then return end local element = self.AuraBars diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_AuraWatch/oUF_AuraWatch.lua b/ElvUI/Libraries/oUF_Plugins/oUF_AuraWatch/oUF_AuraWatch.lua index 488c720..4e71f26 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_AuraWatch/oUF_AuraWatch.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_AuraWatch/oUF_AuraWatch.lua @@ -258,6 +258,12 @@ end local found = {} local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if not unit or self.unit ~= unit then return end local guid = UnitGUID(unit) diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_Cutaway/oUF_Cutaway.lua b/ElvUI/Libraries/oUF_Plugins/oUF_Cutaway/oUF_Cutaway.lua index 14b45fa..db1589e 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_Cutaway/oUF_Cutaway.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_Cutaway/oUF_Cutaway.lua @@ -83,6 +83,12 @@ local function Shared_UpdateCheckReturn(self, element, updateType, ...) return (not element.enabled or not self.cur) or element.ready or not maxV elseif (updateType == POST) then local curV, maxV, unit = ... + if unit and element.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = element.unit and UnitIsUnit(element.unit, unit) + if isUnit then + unit = element.unit + end + end return (not element.enabled or not element.cur) or (not element.ready or not curV or not maxV) or element.unit ~= unit else return false diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_DebuffHighlight/oUF_DebuffHighlight.lua b/ElvUI/Libraries/oUF_Plugins/oUF_DebuffHighlight/oUF_DebuffHighlight.lua index d3de78f..120138b 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_DebuffHighlight/oUF_DebuffHighlight.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_DebuffHighlight/oUF_DebuffHighlight.lua @@ -26,6 +26,12 @@ local function GetDebuffType(unit, filterTable) end local function Update(object, event, unit) + if unit and object.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = object.unit and UnitIsUnit(object.unit, unit) + if isUnit then + unit = object.unit + end + end if unit ~= object.unit then return end local debuffType, texture, wasFiltered, style, color = GetDebuffType(unit, object.DebuffHighlightFilterTable) diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_Fader/oUF_Fader.lua b/ElvUI/Libraries/oUF_Plugins/oUF_Fader/oUF_Fader.lua index 91c8553..7845fbd 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_Fader/oUF_Fader.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_Fader/oUF_Fader.lua @@ -62,6 +62,12 @@ local function Update(self, _, unit) end unit = unit or self.unit + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if self.unit ~= unit then return end -- range fader @@ -129,6 +135,7 @@ local function HoverScript(self) end local function TargetScript(self) + if self.isNamePlate and C_NamePlateManager.IsNamePlateMoving(self) then return end if self.Fader and self.Fader.TargetHooked == 1 then if self:IsShown() then self.Fader:ForceUpdate() diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_RaidDebuffs/oUF_RaidDebuffs.lua b/ElvUI/Libraries/oUF_Plugins/oUF_RaidDebuffs/oUF_RaidDebuffs.lua index a424865..91221b7 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_RaidDebuffs/oUF_RaidDebuffs.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_RaidDebuffs/oUF_RaidDebuffs.lua @@ -161,6 +161,12 @@ local function UpdateDebuff(self, name, icon, count, debuffType, duration, endTi end local function Update(self, event, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if unit ~= self.unit then return end local element = self.RaidDebuffs diff --git a/ElvUI/Libraries/oUF_Plugins/oUF_ResComm/oUF_ResComm.lua b/ElvUI/Libraries/oUF_Plugins/oUF_ResComm/oUF_ResComm.lua index 3de1ceb..9da6c59 100644 --- a/ElvUI/Libraries/oUF_Plugins/oUF_ResComm/oUF_ResComm.lua +++ b/ElvUI/Libraries/oUF_Plugins/oUF_ResComm/oUF_ResComm.lua @@ -37,6 +37,12 @@ local UnitName = UnitName local enabledUF, enabled = {} local function Update(self, event, unit, succeeded) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end if not unit or self.unit ~= unit then return end local element = self.ResurrectIndicator diff --git a/ElvUI/Modules/ActionBars/ActionBars.lua b/ElvUI/Modules/ActionBars/ActionBars.lua index 4cc73f1..f7c337c 100644 --- a/ElvUI/Modules/ActionBars/ActionBars.lua +++ b/ElvUI/Modules/ActionBars/ActionBars.lua @@ -539,7 +539,6 @@ function AB:StyleButton(button, noBackdrop, useMasque) if macroText then macroText:ClearAllPoints() - macroText:SetWidth(self.db.fontWidth) macroText:Point("BOTTOM", 0, 1) macroText:FontTemplate(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline) macroText:SetTextColor(color.r, color.g, color.b) @@ -995,4 +994,4 @@ local function InitializeCallback() AB:Initialize() end -E:RegisterModule(AB:GetName(), InitializeCallback) +E:RegisterModule(AB:GetName(), InitializeCallback) \ No newline at end of file diff --git a/ElvUI/Modules/ActionBars/MicroBar.lua b/ElvUI/Modules/ActionBars/MicroBar.lua index cbcbb79..5d05b45 100644 --- a/ElvUI/Modules/ActionBars/MicroBar.lua +++ b/ElvUI/Modules/ActionBars/MicroBar.lua @@ -17,6 +17,7 @@ local MICRO_BUTTONS = { "SocialsMicroButton", "PVPMicroButton", "LFDMicroButton", + "ChallengesMicroButton", "MainMenuMicroButton", "HelpMicroButton" } diff --git a/ElvUI/Modules/Misc/Misc.lua b/ElvUI/Modules/Misc/Misc.lua index 691742e..b8b1411 100644 --- a/ElvUI/Modules/Misc/Misc.lua +++ b/ElvUI/Modules/Misc/Misc.lua @@ -63,7 +63,7 @@ do end do - local interruptMsg = INTERRUPTED.." %s's \124cff71d5ff\124Hspell:%d\124h[%s]\124h\124r!" + local interruptMsg = " "..INTERRUPTED.." %s's \124cff71d5ff\124Hspell:%d\124h[%s]\124h\124r!" function M:ToggleInterruptAnnounce() if E.db.general.interruptAnnounce == "NONE" then diff --git a/ElvUI/Modules/Nameplates/Elements/Auras.lua b/ElvUI/Modules/Nameplates/Elements/Auras.lua index f324cc3..4e22e9f 100644 --- a/ElvUI/Modules/Nameplates/Elements/Auras.lua +++ b/ElvUI/Modules/Nameplates/Elements/Auras.lua @@ -1,463 +1,188 @@ -local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB -local NP = E:GetModule("NamePlates") +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') local LSM = E.Libs.LSM -local LAI = E.Libs.LAI ---Lua functions -local select, unpack, pairs = select, unpack, pairs -local band = bit.band -local tinsert = table.insert -local floor = math.floor -local split = string.split ---WoW API / Variables +local _G = _G +local wipe = wipe +local unpack = unpack local CreateFrame = CreateFrame -local GetSpellInfo = GetSpellInfo -local GetTime = GetTime -local CREATED, VISIBLE, HIDDEN = 2, 1, 0 +function NP:Construct_Auras(nameplate) + local frameName = nameplate:GetName() -local positionValues = { - BOTTOMLEFT = "TOP", - BOTTOMRIGHT = "TOP", - LEFT = "RIGHT", - RIGHT = "LEFT", - TOPLEFT = "BOTTOM", - TOPRIGHT = "BOTTOM" -} + local Buffs = CreateFrame('Frame', frameName..'Buffs', nameplate) + Buffs:Size(1, 1) + Buffs.size = 27 + Buffs.num = 4 + Buffs.spacing = E.Border * 2 + Buffs.onlyShowPlayer = false + Buffs.disableMouse = true + Buffs.isNamePlate = nameplate -local positionValues2 = { - BOTTOMLEFT = "BOTTOM", - BOTTOMRIGHT = "BOTTOM", - LEFT = "LEFT", - RIGHT = "RIGHT", - TOPLEFT = "TOP", - TOPRIGHT = "TOP" -} + Buffs.initialAnchor = 'BOTTOMLEFT' + Buffs.growthX = 'RIGHT' + Buffs.growthY = 'UP' + Buffs.type = 'buffs' + Buffs.forceShow = nameplate == _G.ElvNP_Test + Buffs.tickers = {} -- StyleFilters + Buffs.stacks = {} + Buffs.rows = {} + local Debuffs = CreateFrame('Frame', frameName..'Debuffs', nameplate) + Debuffs:Size(1, 1) + Debuffs.size = 27 + Debuffs.num = 4 + Debuffs.spacing = E.Border * 2 + Debuffs.onlyShowPlayer = false + Debuffs.disableMouse = true + Debuffs.isNamePlate = nameplate + Debuffs.initialAnchor = 'BOTTOMLEFT' + Debuffs.growthX = 'RIGHT' + Debuffs.growthY = 'UP' + Debuffs.type = 'debuffs' + Debuffs.forceShow = nameplate == _G.ElvNP_Test + Debuffs.tickers = {} -- StyleFilters + Debuffs.stacks = {} + Debuffs.rows = {} -local RaidIconBit = { - ["STAR"] = 0x00100000, - ["CIRCLE"] = 0x00200000, - ["DIAMOND"] = 0x00400000, - ["TRIANGLE"] = 0x00800000, - ["MOON"] = 0x01000000, - ["SQUARE"] = 0x02000000, - ["CROSS"] = 0x04000000, - ["SKULL"] = 0x08000000 -} + Buffs.PreUpdate = UF.PreUpdateAura + Buffs.PreSetPosition = UF.SortAuras + Buffs.SetPosition = UF.SetPosition + Buffs.PostCreateIcon = NP.Construct_AuraIcon + Buffs.PostUpdateIcon = UF.PostUpdateAura + Buffs.CustomFilter = UF.AuraFilter -local ByRaidIcon = {} + Debuffs.PreUpdate = UF.PreUpdateAura + Debuffs.PreSetPosition = UF.SortAuras + Debuffs.SetPosition = UF.SetPosition + Debuffs.PostCreateIcon = NP.Construct_AuraIcon + Debuffs.PostUpdateIcon = UF.PostUpdateAura + Debuffs.CustomFilter = UF.AuraFilter -function NP:LibAuraInfo_AURA_APPLIED(event, destGUID) - self:UpdateElement_AurasByGUID(destGUID, event) + nameplate.Buffs_, nameplate.Debuffs_ = Buffs, Debuffs + nameplate.Buffs, nameplate.Debuffs = Buffs, Debuffs end -function NP:LibAuraInfo_AURA_REMOVED(event, destGUID) - self:UpdateElement_AurasByGUID(destGUID, event) -end +function NP:Construct_AuraIcon(button) + if not button then return end -function NP:LibAuraInfo_AURA_REFRESH(event, destGUID) - self:LibAuraInfo_AURA_APPLIED(event, destGUID) -end + button:SetTemplate(nil, nil, nil, nil, nil, true, true) -function NP:LibAuraInfo_AURA_APPLIED_DOSE(event, destGUID) - self:LibAuraInfo_AURA_APPLIED(event, destGUID) -end + button.cd:SetReverse(true) + button.cd:SetInside(button) -function NP:LibAuraInfo_AURA_CLEAR(event, destGUID) - self:UpdateElement_AurasByGUID(destGUID, event) -end + button.icon:SetDrawLayer('ARTWORK') + button.icon:SetInside() -function NP:LibAuraInfo_UNIT_AURA(event, destGUID) - self:UpdateElement_AurasByGUID(destGUID, event) -end + button.count:ClearAllPoints() + button.count:Point('BOTTOMRIGHT', 1, 1) + button.count:SetJustifyH('RIGHT') -function NP:UpdateTime(elapsed) - self.timeLeft = self.timeLeft - elapsed - self:SetValue(self.timeLeft) + button.overlay:SetTexture() + button.stealable:SetTexture() + + button.isNamePlate = true - if self.nextUpdate > 0 then - self.nextUpdate = self.nextUpdate - elapsed - return + E:RegisterCooldown(button.cd, 'nameplates') + + local auras = button:GetParent() + if auras and auras.type then + local db = NP:PlateDB(auras.__owner) + button.db = db[auras.type] end - if self.timeLeft < 0 then - self:SetScript("OnUpdate", nil) - self:Hide() - return - end - - local value, id, nextUpdate, remainder = E:GetTimeInfo(self.timeLeft, self.threshold, self.hhmmThreshold, self.mmssThreshold) - self.nextUpdate = nextUpdate - - local style = E.TimeFormats[id] - if style then - local which = (self.textColors and 2 or 1) + (self.showSeconds and 0 or 2) - if self.textColors then - self.text:SetFormattedText(style[which], value, self.textColors[id], remainder) - else - self.text:SetFormattedText(style[which], value, remainder) - end - end - - local color = self.timeColors[id] - if color then - self.text:SetTextColor(color.r, color.g, color.b) - end + NP:UpdateAuraSettings(button) end -local unstableAffliction = GetSpellInfo(30108) -local vampiricTouch = GetSpellInfo(34914) -function NP:SetAura(frame, guid, index, filter, isDebuff, visible) - local isAura, name, texture, count, debuffType, duration, expiration, caster, spellID, _ = LAI:GUIDAura(guid, index, filter) +function NP:Configure_Auras(nameplate, auras, db) + auras.size = db.size + auras.height = not db.keepSizeRatio and db.height + auras.numAuras = db.numAuras + auras.numRows = db.numRows + auras.onlyShowPlayer = false + auras.spacing = db.spacing + auras.growthY = UF.MatchGrowthY[db.anchorPoint] or db.growthY + auras.growthX = UF.MatchGrowthX[db.anchorPoint] or db.growthX + auras.xOffset = db.xOffset + auras.yOffset = db.yOffset + auras.anchorPoint = db.anchorPoint + auras.auraSort = UF.SortAuraFuncs[db.sortMethod] + auras.initialAnchor = E.InversePoints[db.anchorPoint] + auras.filterList = UF:ConvertFilters(auras, db.priority) + auras.smartPosition, auras.smartFluid = UF:SetSmartPosition(nameplate) + auras.attachTo = UF:GetAuraAnchorFrame(nameplate, db.attachTo) -- keep below SetSmartPosition + auras.num = db.numAuras * db.numRows + auras.db = db -- for auraSort - if frame.forceShow or frame.forceCreate then - spellID = 47540 - name, _, texture = GetSpellInfo(spellID) - if frame.forceShow then - isAura, count, debuffType, duration, expiration = true, 5, "Magic", 0, 0 - end - end - - if isAura then - local position = visible + 1 - local button = frame[position] or NP:Construct_AuraIcon(frame, position) - - button.caster = caster - button.filter = filter - button.isDebuff = isDebuff - - local filterCheck = not frame.forceCreate - if not (frame.forceShow or frame.forceCreate) then - filterCheck = NP:AuraFilter(guid, button, name, texture, count, debuffType, duration, expiration, caster, spellID) + local index = 1 + while auras[index] do + local button = auras[index] + if button then + button.db = db + NP:UpdateAuraSettings(button) + button:SetBackdropBorderColor(unpack(E.media.bordercolor)) end - if filterCheck then - if button.icon then button.icon:SetTexture(texture) end - if button.count then button.count:SetText(count > 1 and count) end - - if duration > 0 and expiration ~= 0 then - local timeLeft = expiration - GetTime() - if timeLeft > 0 then - button.timeLeft = timeLeft - button.nextUpdate = 0 - - button:SetMinMaxValues(0, duration) - button:SetValue(timeLeft) - - button:SetScript("OnUpdate", NP.UpdateTime) --- else --- return HIDDEN - end - else - button.timeLeft = nil - button.text:SetText("") - button:SetScript("OnUpdate", nil) - button:SetMinMaxValues(0, 1) - button:SetValue(0) - end - - button:SetID(index) - button:Show() - - if isDebuff then - local color = (debuffType and DebuffTypeColor[debuffType]) or DebuffTypeColor.none - if button.name and (button.name == unstableAffliction or button.name == vampiricTouch) then - self:StyleFrameColor(button, 0.05, 0.85, 0.94) - else - self:StyleFrameColor(button, color.r * 0.6, color.g * 0.6, color.b * 0.6) - end - end - - return VISIBLE - elseif frame.forceCreate then - button:Hide() - - return CREATED - else - return HIDDEN - end - end -end - -function NP:Update_AurasPosition(frame, db) - local size = db.size + db.spacing - local anchor = E.InversePoints[db.anchorPoint] - local growthx = (db.growthX == "LEFT" and -1) or 1 - local growthy = (db.growthY == "DOWN" and -1) or 1 - local cols = db.perrow - - for i = frame.anchoredIcons + 1, #frame do - local button = frame[i] - if not button then break end - - local col = (i - 1) % cols - local row = floor((i - 1) / cols) - - button:SetSize(db.size, db.size) - button:ClearAllPoints() - button:SetPoint(anchor, frame, anchor, col * size * growthx, row * size * growthy) - - button.count:FontTemplate(LSM:Fetch("font", db.countFont), db.countFontSize, db.countFontOutline) - button.count:ClearAllPoints() - button.count:SetPoint(db.countPosition, db.countXOffset, db.countYOffset) - - button.text:FontTemplate(LSM:Fetch("font", db.durationFont), db.durationFontSize, db.durationFontOutline) - button.text:ClearAllPoints() - button.text:SetPoint(db.durationPosition, db.durationXOffset, db.durationYOffset) - - button:SetOrientation(db.cooldownOrientation) - - button.bg:ClearAllPoints() - if db.cooldownOrientation == "VERTICAL" then - button.bg:SetPoint("TOPLEFT", button) - button.bg:SetPoint("BOTTOMRIGHT", button:GetStatusBarTexture(), "TOPRIGHT") - else - button.bg:SetPoint("TOPRIGHT", button) - button.bg:SetPoint("BOTTOMLEFT", button:GetStatusBarTexture(), "BOTTOMRIGHT") - end - - if db.reverseCooldown then - button:SetStatusBarColor(0, 0, 0, 0.5) - button.bg:SetTexture(0, 0, 0, 0) - else - button:SetStatusBarColor(0, 0, 0, 0) - button.bg:SetTexture(0, 0, 0, 0.5) - end - end -end - -function NP:UpdateElement_AuraIcons(frame, guid, filter, limit, isDebuff) - local index, visible, hidden, created = 1, 0, 0, 0 - - while visible < limit do - local result = NP:SetAura(frame, guid, index, filter, isDebuff, visible) - if not result then - break - elseif result == HIDDEN then - hidden = hidden + 1 - elseif result == VISIBLE then - visible = visible + 1 - elseif result == CREATED then - visible = visible + 1 - created = created + 1 - end index = index + 1 end - visible = visible - created - - for i = visible + 1, #frame do - frame[i].timeLeft = nil - frame[i]:SetScript("OnUpdate", nil) - frame[i]:Hide() - end - return visible -end - -function NP:UpdateElement_Auras(frame) - if not frame.Health:IsShown() then return end - - local guid = frame.guid - - if not guid then - if frame.UnitClass == "HERO" then - local name = frame.UnitName - guid = self.GUIDByName[name] - elseif frame.RaidIcon:IsShown() then - guid = ByRaidIcon[frame.RaidIconType] - end - - if guid then - frame.guid = guid - elseif not frame.Buffs.forceShow and not frame.Debuffs.forceShow then - return - end - end - - local db = NP.db.units[frame.UnitType].buffs - if db.enable then - local buffs = frame.Buffs - buffs.visibleBuffs = NP:UpdateElement_AuraIcons(buffs, guid, buffs.filter or "HELPFUL", db.perrow * db.numrows) - - if #buffs > buffs.anchoredIcons then - self:Update_AurasPosition(buffs, db) - - buffs.anchoredIcons = #buffs - end - end - - db = NP.db.units[frame.UnitType].debuffs - if db.enable then - local debuffs = frame.Debuffs - debuffs.visibleDebuffs = NP:UpdateElement_AuraIcons(debuffs, guid, debuffs.filter or "HARMFUL", db.perrow * db.numrows, true) - - if #debuffs > debuffs.anchoredIcons then - self:Update_AurasPosition(debuffs, db) - - debuffs.anchoredIcons = #debuffs - end - end - - self:StyleFilterUpdate(frame, "UNIT_AURA") -end - -function NP:UpdateElement_AurasByGUID(guid, event) - local destName, destFlags = LAI:GetGUIDInfo(guid) - - if destName then - destName = split("-", destName) - end - - local raidIcon - if destFlags then - for iconName, bitmask in pairs(RaidIconBit) do - if band(destFlags, bitmask) > 0 then - ByRaidIcon[iconName] = guid - raidIcon = iconName - break - end - end - end - - local frame = self:SearchForFrame(guid, raidIcon, destName) - if frame then - frame.guid = guid - self.GUIDByName[destName] = guid - self:UpdateElement_Auras(frame) - end -end - -function NP:Construct_AuraIcon(parent, index) - local db = NP.db.units[parent:GetParent().UnitType][parent.type] - - local button = CreateFrame("StatusBar", "$parentButton"..index, parent) - NP:StyleFrame(button, true) - - button:SetStatusBarTexture(E.media.blankTex) - button:SetStatusBarColor(0, 0, 0, 0) - button:SetOrientation("VERTICAL") - - button.bg = button:CreateTexture() - button.bg:SetTexture(0, 0, 0, 0.5) - - button.bg:SetPoint("TOPLEFT", button) - button.bg:SetPoint("BOTTOMRIGHT", button:GetStatusBarTexture(), "TOPRIGHT") - - button.icon = button:CreateTexture(nil, "BORDER") - button.icon:SetTexCoord(unpack(E.TexCoords)) - button.icon:SetAllPoints() - - button.count = button:CreateFontString(nil, "OVERLAY") - button.count:SetJustifyH("RIGHT") - button.count:FontTemplate(LSM:Fetch("font", db.countFont), db.countFontSize, db.countFontOutline) - - button.text = button:CreateFontString(nil, "OVERLAY") - - -- support cooldown override - if not button.isRegisteredCooldown then - button.CooldownOverride = "nameplates" - button.isRegisteredCooldown = true - button.forceEnabled = true - - if not E.RegisteredCooldowns.nameplates then E.RegisteredCooldowns.nameplates = {} end - tinsert(E.RegisteredCooldowns.nameplates, button) - end - - button.text:FontTemplate(LSM:Fetch("font", db.durationFont), db.durationFontSize, db.durationFontOutline) - - NP:Update_CooldownOptions(button) - - tinsert(parent, button) - - return button -end - -function NP:Update_CooldownOptions(button) - E:Cooldown_Options(button, self.db.cooldown, button) -end - -function NP:Configure_Auras(frame, auraType) - local auras = frame[auraType] - local db = self.db.units[frame.UnitType][auras.type] - - auras:SetWidth(db.perrow * db.size + ((db.perrow - 1) * db.spacing)) - auras:SetHeight(db.numrows * db.size + ((db.numrows - 1) * db.spacing)) auras:ClearAllPoints() - auras:SetPoint(positionValues[db.anchorPoint], db.attachTo == "BUFFS" and frame.Buffs or frame.Health, positionValues2[db.anchorPoint], db.xOffset, db.yOffset) + auras:Point(auras.initialAnchor, auras.attachTo, auras.anchorPoint, auras.xOffset, auras.yOffset) + auras:Size(db.numAuras * db.size + ((db.numAuras - 1) * db.spacing), 1) end -function NP:ConstructElement_Auras(frame, auraType) - local auras = CreateFrame("Frame", "$parent"..auraType, frame) - auras:Show() - auras:SetSize(150, 27) - auras:SetPoint("TOP", 0, 22) - auras.anchoredIcons = 0 - auras.type = string.lower(auraType) +function NP:Update_Auras(nameplate) + local db = NP:PlateDB(nameplate) - return auras -end - -function NP:CheckFilter(name, spellID, isPlayer, allowDuration, noDuration, ...) - for i = 1, select("#", ...) do - local filterName = select(i, ...) - if not filterName then return true end - if G.nameplates.specialFilters[filterName] or E.global.unitframe.aurafilters[filterName] then - local filter = E.global.unitframe.aurafilters[filterName] - if filter then - local filterType = filter.type - local spellList = filter.spells - local spell = spellList and (spellList[spellID] or spellList[name]) - - if filterType and (filterType == "Whitelist") and (spell and spell.enable) and allowDuration then - return true - elseif filterType and (filterType == "Blacklist") and (spell and spell.enable) then - return false - end - elseif filterName == "Personal" and isPlayer and allowDuration then - return true - elseif filterName == "nonPersonal" and (not isPlayer) and allowDuration then - return true - elseif filterName == "blockNoDuration" and noDuration then - return false - elseif filterName == "blockNonPersonal" and (not isPlayer) then - return false - end + if db.debuffs.enable or db.buffs.enable then + if not nameplate:IsElementEnabled('Auras') then + nameplate:EnableElement('Auras') end + + nameplate.Buffs_:ClearAllPoints() + nameplate.Debuffs_:ClearAllPoints() + + if db.debuffs.enable then + nameplate.Debuffs = nameplate.Debuffs_ + NP:Configure_Auras(nameplate, nameplate.Debuffs, db.debuffs) + nameplate.Debuffs:Show() + nameplate.Debuffs:ForceUpdate() + elseif nameplate.Debuffs then + nameplate.Debuffs:Hide() + nameplate.Debuffs = nil + end + + if db.buffs.enable then + nameplate.Buffs = nameplate.Buffs_ + NP:Configure_Auras(nameplate, nameplate.Buffs, db.buffs) + nameplate.Buffs:Show() + nameplate.Buffs:ForceUpdate() + elseif nameplate.Buffs then + nameplate.Buffs:Hide() + nameplate.Buffs = nil + end + elseif nameplate:IsElementEnabled('Auras') then + nameplate:DisableElement('Auras') end end -function NP:AuraFilter(guid, button, name, texture, count, debuffType, duration, expiration, caster, spellID) - local parent = button:GetParent() - local parentType = parent.type - local db = NP.db.units[parent:GetParent().UnitType][parentType] - if not db then return true end - - local isPlayer = caster == E.myguid - - -- keep these same as in `UF:AuraFilter` - button.isPlayer = isPlayer - button.dtype = debuffType - button.duration = duration - button.expiration = expiration - button.stackCount = count - button.name = name - button.spellID = spellID - button.spell = name - button.priority = 0 - - if not db.filters then return true end - - local priority = db.filters.priority - local noDuration = (not duration or duration == 0) - local allowDuration = noDuration or (duration and (duration > 0) and db.filters.maxDuration == 0 or duration <= db.filters.maxDuration) and (db.filters.minDuration == 0 or duration >= db.filters.minDuration) - local filterCheck - - if priority ~= "" then - filterCheck = NP:CheckFilter(name, spellID, isPlayer, allowDuration, noDuration, split(",", priority)) - else - filterCheck = allowDuration and true -- Allow all auras to be shown when the filter list is empty, while obeying duration sliders +function NP:UpdateAuraSettings(button) + local db = button.db + if db then + local point = db.countPosition or 'CENTER' + button.count:ClearAllPoints() + button.count:SetJustifyH(point:find('RIGHT') and 'RIGHT' or 'LEFT') + button.count:Point(point, db.countXOffset, db.countYOffset) + button.count:FontTemplate(LSM:Fetch('font', db.countFont), db.countFontSize, db.countFontOutline) end - return filterCheck -end \ No newline at end of file + if button.auraInfo then + wipe(button.auraInfo) + else + button.auraInfo = {} + end + + button.needsButtonTrim = true + button.needsUpdateCooldownPosition = true +end diff --git a/ElvUI/Modules/Nameplates/Elements/BossMods.lua b/ElvUI/Modules/Nameplates/Elements/BossMods.lua new file mode 100644 index 0000000..6ecafdc --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/BossMods.lua @@ -0,0 +1,299 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') +local AB = E:GetModule('ActionBars') + +local _G = _G +local wipe = wipe +local next = next +local pairs = pairs +local unpack = unpack +local GetTime = GetTime +local UnitGUID = UnitGUID +local CreateFrame = CreateFrame + +NP.BossMods_ActiveUnitGUID = {} +NP.BossMods_TextureCache = {} + +local allowHostile = false +function NP:BossMods_CreateIcon(element) + element.index = not element.index and 1 or (element.index + 1) + + local button = CreateFrame('Button', element:GetName()..'Button'..element.index, element) + button:EnableMouse(false) + button:SetTemplate(nil, nil, nil, nil, nil, true, true) + + local cooldown = CreateFrame('Cooldown', '$parentCooldown', button, 'CooldownFrameTemplate') + cooldown:SetReverse(true) + cooldown:SetInside(button) + E:RegisterCooldown(cooldown, 'nameplates') + + local icon = button:CreateTexture(nil, 'ARTWORK') + icon:SetTexCoord(unpack(E.TexCoords)) + icon:SetInside() + + button.icon = icon + button.cd = cooldown + button.db = element.db + + return button +end + +function NP:BossMods_GetIcon(plate, texture) + local element, unused, avaiableIcon = plate.BossMods + + local activeButton = element.activeIcons[texture] + if not activeButton then + unused, avaiableIcon = next(element.unusedIcons) + if unused then element.unusedIcons[unused] = nil end + end + + local button = activeButton or avaiableIcon or NP:BossMods_CreateIcon(element) + if not activeButton then + element.activeIcons[texture] = button + end + + return button +end + +function NP:BossMods_PositionIcons(element) + if not next(element.activeIcons) then return end + + local index = 1 + local anchor, inversed, growthX, growthY, width, height, cols, point, middle = UF:GetAuraPosition(element) + + element.currentRow = nil -- clear this for a new update + + for _, button in pairs(element.activeIcons) do + UF:SetAuraPosition(element, button, index, anchor, inversed, growthX, growthY, width, height, cols, point, middle) + + button:Size(width, height) + button:Show() + + AB:TrimIcon(button) + + index = index + 1 + end +end + +function NP:BossMods_TrackIcons(track, unitGUID, texture, duration, desaturate, startTime) + if track then + NP.BossMods_TextureCache[texture] = true -- use this to easily populate boss mod style filters + + if not NP.BossMods_ActiveUnitGUID[unitGUID] then + NP.BossMods_ActiveUnitGUID[unitGUID] = {} + end + + local active = NP.BossMods_ActiveUnitGUID[unitGUID] + if not active[texture] then + active[texture] = {} + end + + local activeTexture = active[texture] + activeTexture.duration = duration + activeTexture.desaturate = desaturate + activeTexture.startTime = startTime + else + local active = NP.BossMods_ActiveUnitGUID[unitGUID] + if active then + if active[texture] then + active[texture] = nil + end + + if not next(active) then + NP.BossMods_ActiveUnitGUID[unitGUID] = nil + end + end + end +end + +function NP:BossMods_ClearIcons() + if not next(NP.BossMods_ActiveUnitGUID) then return end + + for unitGUID, textures in pairs(NP.BossMods_ActiveUnitGUID) do + for texture in pairs(textures) do + local plate = NP.PlateGUID[unitGUID] + if plate then + NP:BossMods_ClearIcon(plate, texture) + NP:StyleFilterUpdate(plate, 'FAKE_BossModAuras') + end + end + end + + wipe(NP.BossMods_ActiveUnitGUID) +end + +function NP:BossMods_AddIcon(unitGUID, texture, duration, desaturate, skip) + local active = NP.BossMods_ActiveUnitGUID[unitGUID] + local activeTexture = active and active[texture] + + local pastTime = activeTexture and activeTexture.startTime + local pastDuration = activeTexture and activeTexture.duration + if pastTime and pastDuration and pastDuration ~= duration then + pastTime = nil -- reset the cooldown timer if a new duration is given + end + + local startTime = duration and (pastTime or GetTime()) or nil + NP:BossMods_TrackIcons(true, unitGUID, texture, duration, desaturate, startTime) + + local plate = NP.PlateGUID[unitGUID] + if not plate then return end + + local button = NP:BossMods_GetIcon(plate, texture) + button.icon:SetDesaturated(desaturate) + button.icon:SetTexture(texture) + + if duration then + button.cd:SetCooldown(startTime, duration) + button.cd:Show() + else + button.cd:Hide() + end + + if desaturate then + button:SetBackdropBorderColor(unpack(E.media.bordercolor)) + else + local color = _G.DebuffTypeColor.none + button:SetBackdropBorderColor(color.r * 0.6, color.g * 0.6, color.b * 0.6) + end + + NP:BossMods_PositionIcons(plate.BossMods) + + if not skip then -- this will happen already during PostUpdateAllElements + NP:StyleFilterUpdate(plate, 'FAKE_BossModAuras') + end +end + +function NP:BossMods_RemoveIcon(unitGUID, texture) + NP:BossMods_TrackIcons(false, unitGUID, texture) + + local plate = NP.PlateGUID[unitGUID] + if plate then + NP:BossMods_ClearIcon(plate, texture) + NP:BossMods_PositionIcons(plate.BossMods) + NP:StyleFilterUpdate(plate, 'FAKE_BossModAuras') + end +end + +function NP:BossMods_ClearIcon(plate, texture) + local element = plate.BossMods + local button = element.activeIcons[texture] + if not button then return end + + button:Hide() + + element.activeIcons[texture] = nil + element.unusedIcons[texture] = button +end + +function NP:BossMods_UpdateIcon(plate, removed) + local unitGUID = plate.unitGUID + local active = NP.BossMods_ActiveUnitGUID[unitGUID] + + if not active then + local element = plate.BossMods + if element and next(element.activeIcons) then + for texture in pairs(element.activeIcons) do + NP:BossMods_ClearIcon(plate, texture) + end + end + + return + end + + local enabled = allowHostile and NP.db.bossMods.enable + for texture, info in pairs(active) do + if removed or not enabled then + NP:BossMods_ClearIcon(plate, texture) + elseif enabled then + NP:BossMods_AddIcon(unitGUID, texture, info.duration, info.desaturate, true) + end + end +end + +function NP:BossMods_AddIcon_DBM(isGUID, unit, texture, duration, desaturate) + if not (allowHostile and NP.db.bossMods.enable) then return end + + local unitGUID = (isGUID and unit) or UnitGUID(unit) + NP:BossMods_AddIcon(unitGUID, texture, duration, desaturate) +end + +function NP:BossMods_RemoveIcon_DBM(isGUID, unit, texture) + local unitGUID = (isGUID and unit) or UnitGUID(unit) + NP:BossMods_RemoveIcon(unitGUID, texture) +end + +function NP:BossMods_AddIcon_BW(_, unitGUID, texture, duration, desaturate) + if not (allowHostile and NP.db.bossMods.enable) then return end + + NP:BossMods_AddIcon(unitGUID, texture, duration, desaturate) +end + +function NP:BossMods_RemoveIcon_BW(_, unitGUID, texture) + NP:BossMods_RemoveIcon(unitGUID, texture) +end + +function NP:BossMods_DisableHostile() + NP:BossMods_ClearIcons() + + allowHostile = false +end + +function NP:BossMods_EnableHostile() + allowHostile = true +end + +function NP:DBM_SupportedNPMod() + return _G.DBM.Options.UseNameplateHandoff +end + +function NP:BossMods_RegisterCallbacks() + local DBM = _G.DBM + if DBM and DBM.RegisterCallback and DBM.Nameplate then + DBM.Nameplate.SupportedNPMod = NP.DBM_SupportedNPMod + + DBM:RegisterCallback('BossMod_ShowNameplateAura',NP.BossMods_AddIcon_DBM) + DBM:RegisterCallback('BossMod_HideNameplateAura',NP.BossMods_RemoveIcon_DBM) + DBM:RegisterCallback('BossMod_EnableHostileNameplates',NP.BossMods_EnableHostile) + DBM:RegisterCallback('BossMod_DisableHostileNameplates',NP.BossMods_DisableHostile) + end + + local BWL = _G.BigWigsLoader + if BWL and BWL.RegisterMessage then + BWL.RegisterMessage(NP,'BigWigs_AddNameplateIcon',NP.BossMods_AddIcon_BW) + BWL.RegisterMessage(NP,'BigWigs_RemoveNameplateIcon',NP.BossMods_RemoveIcon_BW) + BWL.RegisterMessage(NP,'BigWigs_EnableHostileNameplates',NP.BossMods_EnableHostile) + BWL.RegisterMessage(NP,'BigWigs_DisableHostileNameplates',NP.BossMods_DisableHostile) + end +end + +function NP:Update_BossMods(plate) + local db = NP.db.bossMods + if not db.enable then return end + + local anchor = db.anchorPoint + local inverse = E.InversePoints[anchor] + + local element = plate.BossMods + element:ClearAllPoints() + element:SetPoint(inverse or 'TOPRIGHT', plate, anchor or 'TOPRIGHT', db.xOffset, db.yOffset) + element:SetSize(plate.width or 150, db.size) + + element.db = db + element.spacing = db.spacing + element.initialAnchor = inverse + element.growthY = UF.MatchGrowthY[anchor] or db.growthY + element.growthX = UF.MatchGrowthX[anchor] or db.growthX + element.size = db.size + (db.spacing or 0) + element.height = not db.keepSizeRatio and db.height + element.rows = {} +end + +function NP:Construct_BossMods(nameplate) + local element = CreateFrame('Frame', '$parentBossMods', nameplate) + + element.activeIcons = {} + element.unusedIcons = {} + + return element +end diff --git a/ElvUI/Modules/Nameplates/Elements/CastBar.lua b/ElvUI/Modules/Nameplates/Elements/CastBar.lua index 85a14a0..ba6ab39 100644 --- a/ElvUI/Modules/Nameplates/Elements/CastBar.lua +++ b/ElvUI/Modules/Nameplates/Elements/CastBar.lua @@ -1,358 +1,254 @@ -local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB -local NP = E:GetModule("NamePlates") +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') +local CH = E:GetModule('Chat') local LSM = E.Libs.LSM ---Lua functions +local _G = _G +local abs = abs +local next = next local unpack = unpack -local abs = math.abs ---WoW API / Variables +local strjoin = strjoin +local strmatch = strmatch local CreateFrame = CreateFrame -local GetTime = GetTime -local UnitCastingInfo = UnitCastingInfo -local UnitChannelInfo = UnitChannelInfo -local FAILED = FAILED +local UnitCanAttack = UnitCanAttack +local UnitName = UnitName + local INTERRUPTED = INTERRUPTED -local function resetAttributes(self) - self.casting = nil - self.channeling = nil - self.notInterruptible = nil - self.spellName = nil +function NP:Castbar_CheckInterrupt(unit) + if unit == 'vehicle' then + unit = 'player' + end + + if self.notInterruptible and UnitCanAttack('player', unit) then + self:SetStatusBarColor(NP.db.colors.castNoInterruptColor.r, NP.db.colors.castNoInterruptColor.g, NP.db.colors.castNoInterruptColor.b) + + if self.Icon and NP.db.colors.castbarDesaturate then + self.Icon:SetDesaturated(true) + end + else + self:SetStatusBarColor(NP.db.colors.castColor.r, NP.db.colors.castColor.g, NP.db.colors.castColor.b) + + if self.Icon then + self.Icon:SetDesaturated(false) + end + end end -function NP:Update_CastBarOnUpdate(elapsed) - if self.casting or self.channeling then - local isCasting = self.casting - if isCasting then - self.value = self.value + elapsed - if self.value >= self.max then - resetAttributes(self) - self:Hide() - NP:StyleFilterUpdate(self:GetParent(), "FAKE_Casting") - return - end - else - self.value = self.value - elapsed - if self.value <= 0 then - resetAttributes(self) - self:Hide() - NP:StyleFilterUpdate(self:GetParent(), "FAKE_Casting") - return - end +function NP:Castbar_CustomDelayText(duration) + if self.channeling then + if self.channelTimeFormat == 'CURRENT' then + self.Time:SetFormattedText('%.1f |cffaf5050%.1f|r', abs(duration - self.max), self.delay) + elseif self.channelTimeFormat == 'CURRENTMAX' then + self.Time:SetFormattedText('%.1f / %.1f |cffaf5050%.1f|r', duration, self.max, self.delay) + elseif self.channelTimeFormat == 'REMAINING' then + self.Time:SetFormattedText('%.1f |cffaf5050%.1f|r', duration, self.delay) + elseif self.channelTimeFormat == 'REMAININGMAX' then + self.Time:SetFormattedText('%.1f / %.1f |cffaf5050%.1f|r', abs(duration - self.max), self.max, self.delay) end + else + if self.castTimeFormat == 'CURRENT' then + self.Time:SetFormattedText('%.1f |cffaf5050%s %.1f|r', duration, '+', self.delay) + elseif self.castTimeFormat == 'CURRENTMAX' then + self.Time:SetFormattedText('%.1f / %.1f |cffaf5050%s %.1f|r', duration, self.max, '+', self.delay) + elseif self.castTimeFormat == 'REMAINING' then + self.Time:SetFormattedText('%.1f |cffaf5050%s %.1f|r', abs(duration - self.max), '+', self.delay) + elseif self.castTimeFormat == 'REMAININGMAX' then + self.Time:SetFormattedText('%.1f / %.1f |cffaf5050%s %.1f|r', abs(duration - self.max), self.max, '+', self.delay) + end + end +end - if self.delay ~= 0 then - if self.channeling then - if self.channelTimeFormat == "CURRENT" then - self.Time:SetFormattedText("%.1f |cffaf5050%.2f|r", abs(self.value - self.max), self.delay) - elseif self.channelTimeFormat == "CURRENTMAX" then - self.Time:SetFormattedText("%.1f / %.2f |cffaf5050%.2f|r", abs(self.value - self.max), self.max, self.delay) - elseif self.channelTimeFormat == "REMAINING" then - self.Time:SetFormattedText("%.1f |cffaf5050%.2f|r", self.value, self.delay) - elseif self.channelTimeFormat == "REMAININGMAX" then - self.Time:SetFormattedText("%.1f / %.2f |cffaf5050%.2f|r", self.value, self.max, self.max, self.delay) +function NP:Castbar_CustomTimeText(duration) + if self.channeling then + if self.channelTimeFormat == 'CURRENT' then + self.Time:SetFormattedText('%.1f', abs(duration - self.max)) + elseif self.channelTimeFormat == 'CURRENTMAX' then + self.Time:SetFormattedText('%.1f / %.1f', abs(duration - self.max), self.max) + elseif self.channelTimeFormat == 'REMAINING' then + self.Time:SetFormattedText('%.1f', duration) + elseif self.channelTimeFormat == 'REMAININGMAX' then + self.Time:SetFormattedText('%.1f / %.1f', duration, self.max) + end + else + if self.castTimeFormat == 'CURRENT' then + self.Time:SetFormattedText('%.1f', duration) + elseif self.castTimeFormat == 'CURRENTMAX' then + self.Time:SetFormattedText('%.1f / %.1f', duration, self.max) + elseif self.castTimeFormat == 'REMAINING' then + self.Time:SetFormattedText('%.1f', abs(duration - self.max)) + elseif self.castTimeFormat == 'REMAININGMAX' then + self.Time:SetFormattedText('%.1f / %.1f', abs(duration - self.max), self.max) + end + end +end + +function NP:Castbar_PostCastStart(unit) + self:CheckInterrupt(unit) + + -- player or NPCs; if used on other players: the cast target doesn't match their target, can be misleading if they mouseover cast + local plate = self.__owner + local db = NP:PlateDB(plate) + if db.castbar and db.castbar.enable then + if db.castbar.displayTarget then + local frameType = plate.frameType + if frameType == 'PLAYER' then + if self.curTarget then + self.Text:SetText(self.spellName..' > '..self.curTarget) end - else - if self.castTimeFormat == "CURRENT" then - self.Time:SetFormattedText("%.1f |cffaf5050%s %.2f|r", self.value, "+", self.delay) - elseif self.castTimeFormat == "CURRENTMAX" then - self.Time:SetFormattedText("%.1f / %.2f |cffaf5050%s %.2f|r", self.value, self.max, "+", self.delay) - elseif self.castTimeFormat == "REMAINING" then - self.Time:SetFormattedText("%.1f |cffaf5050%s %.2f|r", abs(self.value - self.max), "+", self.delay) - elseif self.castTimeFormat == "REMAININGMAX" then - self.Time:SetFormattedText("%.1f / %.2f |cffaf5050%s %.2f|r", abs(self.value - self.max), self.max, "+", self.delay) - end - end - else - if self.channeling then - if self.channelTimeFormat == "CURRENT" then - self.Time:SetFormattedText("%.1f", abs(self.value - self.max)) - elseif self.channelTimeFormat == "CURRENTMAX" then - self.Time:SetFormattedText("%.1f / %.2f", abs(self.value - self.max), self.max) - elseif self.channelTimeFormat == "REMAINING" then - self.Time:SetFormattedText("%.1f", self.value) - elseif self.channelTimeFormat == "REMAININGMAX" then - self.Time:SetFormattedText("%.1f / %.2f", self.value, self.max) - end - else - if self.castTimeFormat == "CURRENT" then - self.Time:SetFormattedText("%.1f", self.value) - elseif self.castTimeFormat == "CURRENTMAX" then - self.Time:SetFormattedText("%.1f / %.2f", self.value, self.max) - elseif self.castTimeFormat == "REMAINING" then - self.Time:SetFormattedText("%.1f", abs(self.value - self.max)) - elseif self.castTimeFormat == "REMAININGMAX" then - self.Time:SetFormattedText("%.1f / %.2f", abs(self.value - self.max), self.max) + elseif frameType == 'ENEMY_NPC' or frameType == 'FRIENDLY_NPC' then + local target = self.curTarget or UnitName(unit..'target') + if target and target ~= '' and target ~= plate.unitName then + self.Text:SetText(self.spellName..' > '..target) end end end - - self:SetValue(self.value) - elseif self.holdTime > 0 then - self.holdTime = self.holdTime - elapsed - else - resetAttributes(self) - self:Hide() - NP:StyleFilterUpdate(self:GetParent(), "FAKE_Casting") + NP:StyleFilterUpdate(plate, 'FAKE_Cast') end end -function NP:Update_CastBar(frame, event, unit) - local castBar = frame.CastBar - if unit then - if not event then - if UnitChannelInfo(unit) then - event = "UNIT_SPELLCAST_CHANNEL_START" - elseif UnitCastingInfo(unit) then - event = "UNIT_SPELLCAST_START" - end - end - elseif castBar:IsShown() then - resetAttributes(castBar) - castBar:Hide() +function NP:Castbar_PostCastFail() + NP:StyleFilterUpdate(self.__owner, 'FAKE_Cast') + self:SetStatusBarColor(NP.db.colors.castInterruptedColor.r, NP.db.colors.castInterruptedColor.g, NP.db.colors.castInterruptedColor.b) +end + +function NP:Castbar_PostCastInterruptible(unit) + self:CheckInterrupt(unit) +end + +function NP:Castbar_PostCastStop() + NP:StyleFilterUpdate(self.__owner, 'FAKE_Cast') +end + +function NP:Construct_Castbar(nameplate) + local castbar = CreateFrame('StatusBar', nameplate:GetName()..'Castbar', nameplate) + castbar:SetParent(nameplate) + castbar:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + castbar:SetStatusBarTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + + NP.StatusBars[castbar] = true + castbar.ModuleStatusBars = NP.StatusBars -- not oUF + + castbar.Button = CreateFrame('Frame', nil, castbar) + castbar.Button:SetTemplate(nil, nil, nil, nil, nil, true, true) + + castbar.Icon = castbar.Button:CreateTexture(nil, 'ARTWORK') + castbar.Icon:SetParent(castbar.Button) + castbar.Icon:SetTexCoord(unpack(E.TexCoords)) + castbar.Icon:SetInside() + + castbar.Time = castbar:CreateFontString(nil, 'OVERLAY') + castbar.Time:FontTemplate(LSM:Fetch('font', NP.db.font), NP.db.fontSize, NP.db.fontOutline) + castbar.Time:Point('RIGHT', castbar, 'RIGHT', -4, 0) + castbar.Time:SetJustifyH('RIGHT') + + castbar.Text = castbar:CreateFontString(nil, 'OVERLAY') + castbar.Text:FontTemplate(LSM:Fetch('font', NP.db.font), NP.db.fontSize, NP.db.fontOutline) + castbar.Text:Point('LEFT', castbar, 'LEFT', 4, 0) + castbar.Text:SetJustifyH('LEFT') + castbar.Text:SetWordWrap(false) + + castbar.CheckInterrupt = NP.Castbar_CheckInterrupt + castbar.CustomDelayText = NP.Castbar_CustomDelayText + castbar.CustomTimeText = NP.Castbar_CustomTimeText + castbar.PostCastStart = NP.Castbar_PostCastStart + castbar.PostCastFail = NP.Castbar_PostCastFail + castbar.PostCastInterruptible = NP.Castbar_PostCastInterruptible + castbar.PostCastStop = NP.Castbar_PostCastStop + + if nameplate == _G.ElvNP_Test then + castbar.Hide = castbar.Show + castbar:Show() + castbar.Text:SetText('Casting') + castbar.Time:SetText('3.1') + castbar.Icon:SetTexture([[Interface\Icons\Achievement_Character_Pandaren_Female]]) + castbar:SetStatusBarColor(NP.db.colors.castColor.r, NP.db.colors.castColor.g, NP.db.colors.castColor.b) end - if self.db.units[frame.UnitType].castbar.enable ~= true then return end - if self.db.units[frame.UnitType].health.enable ~= true and not (frame.isTarget and self.db.alwaysShowTargetHealth) then return end --Bug + return castbar +end - if event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START" then - local name, _, _, texture, startTime, endTime, _, _, notInterruptible = UnitCastingInfo(unit) - event = "UNIT_SPELLCAST_START" - if not name then - name, _, _, texture, startTime, endTime, _, notInterruptible = UnitChannelInfo(unit) - event = "UNIT_SPELLCAST_CHANNEL_START" +function NP:CASTBAR_COMBAT_LOG_EVENT_UNFILTERED(event, sourceGUID, sourceName, targetGUID) + if (event == 'SPELL_INTERRUPT' or event == 'SPELL_PERIODIC_INTERRUPT') and targetGUID and (sourceName and sourceName ~= '') then + local plate, classColor = NP.PlateGUID[targetGUID] + if plate and plate.Castbar then + local db = NP:PlateDB(plate) + if db.castbar and db.castbar.enable and db.castbar.sourceInterrupt and (db.castbar.timeToHold > 0) then + local name = strmatch(sourceName, '([^%-]+).*') + if db.castbar.sourceInterruptClassColor then + local data = CH:GetPlayerInfoByGUID(sourceGUID) + if data and data.classColor then + classColor = data.classColor.colorStr + end + + plate.Castbar.Text:SetFormattedText('%s > %s', INTERRUPTED, classColor and strjoin('', '|c', classColor, name) or name) + else + plate.Castbar.Text:SetFormattedText('%s > %s', INTERRUPTED, name) + end + end + end + end +end + +function NP:Update_Castbar(nameplate) + local frameDB = NP:PlateDB(nameplate) + local db = frameDB.castbar + + local castbar = nameplate.Castbar + if nameplate == _G.ElvNP_Test then + castbar:SetAlpha((not frameDB.nameOnly and db.enable) and 1 or 0) + end + + if db.enable then + if not nameplate:IsElementEnabled('Castbar') then + nameplate:EnableElement('Castbar') end - if not name then - resetAttributes(castBar) - castBar:Hide() - return - end + castbar.timeToHold = db.timeToHold + castbar.castTimeFormat = db.castTimeFormat + castbar.channelTimeFormat = db.channelTimeFormat - endTime = endTime / 1000 - startTime = startTime / 1000 + castbar:Size(db.width, db.height) + castbar:Point('CENTER', nameplate, 'CENTER', db.xOffset, db.yOffset) - castBar.max = endTime - startTime - castBar.startTime = startTime - castBar.delay = 0 - castBar.casting = event == "UNIT_SPELLCAST_START" - castBar.channeling = event == "UNIT_SPELLCAST_CHANNEL_START" - castBar.notInterruptible = notInterruptible - castBar.holdTime = 0 - castBar.interrupted = nil - castBar.spellName = name - - if castBar.casting then - castBar.value = GetTime() - startTime + if db.showIcon then + castbar.Button:ClearAllPoints() + castbar.Button:Point(db.iconPosition == 'RIGHT' and 'BOTTOMLEFT' or 'BOTTOMRIGHT', castbar, db.iconPosition == 'RIGHT' and 'BOTTOMRIGHT' or 'BOTTOMLEFT', db.iconOffsetX, db.iconOffsetY) + castbar.Button:Size(db.iconSize, db.iconSize) + castbar.Button:Show() else - castBar.value = endTime - GetTime() + castbar.Button:Hide() end - castBar:SetMinMaxValues(0, castBar.max) - castBar:SetValue(castBar.value) + castbar.Time:ClearAllPoints() + castbar.Text:ClearAllPoints() - castBar.Icon.texture:SetTexture(texture) - castBar.Spark:Show() - castBar.Name:SetText(name) - castBar.Time:SetText() - - castBar:Show() - elseif event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP" then - if castBar:IsShown() then - resetAttributes(castBar) - end - elseif event == "UNIT_SPELLCAST_FAILED" or event == "UNIT_SPELLCAST_INTERRUPTED" then - if castBar:IsShown() then - castBar.Spark:Hide() - castBar.Name:SetText(event == "UNIT_SPELLCAST_FAILED" and FAILED or INTERRUPTED) - - castBar.holdTime = self.db.units[frame.UnitType].castbar.timeToHold --How long the castbar should stay visible after being interrupted, in seconds - castBar.interrupted = true - - resetAttributes(castBar) - castBar:SetValue(castBar.max) - end - elseif event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then - if frame:IsShown() then - local name, startTime, endTime, _ - if event == "UNIT_SPELLCAST_DELAYED" then - name, _, _, _, startTime, endTime = UnitCastingInfo(unit) - else - name, _, _, _, startTime, endTime = UnitChannelInfo(unit) - end - - if not name then - resetAttributes(castBar) - castBar:Hide() - return - end - - endTime = endTime / 1000 - startTime = startTime / 1000 - - local delta - if castBar.casting then - delta = startTime - castBar.startTime - castBar.value = GetTime() - startTime - else - delta = castBar.startTime - startTime - castBar.value = endTime - GetTime() - end - - if delta < 0 then - delta = 0 - end - - castBar.Name:SetText(name) - castBar.max = endTime - startTime - castBar.startTime = startTime - castBar.delay = castBar.delay + delta - castBar:SetMinMaxValues(0, castBar.max) - castBar:SetValue(castBar.value) - end - elseif event == "UNIT_SPELLCAST_INTERRUPTIBLE" or event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" then - castBar.notInterruptible = event == "UNIT_SPELLCAST_NOT_INTERRUPTIBLE" - end - - if not castBar.notInterruptible then - if castBar.interrupted then - castBar:SetStatusBarColor(self.db.colors.castInterruptedColor.r, self.db.colors.castInterruptedColor.g, self.db.colors.castInterruptedColor.b) + if db.textPosition == 'BELOW' then + castbar.Time:Point('TOPRIGHT', castbar, 'BOTTOMRIGHT', db.timeXOffset, db.timeYOffset) + castbar.Text:Point('TOPLEFT', castbar, 'BOTTOMLEFT', db.textXOffset, db.textYOffset) + elseif db.textPosition == 'ABOVE' then + castbar.Time:Point('BOTTOMRIGHT', castbar, 'TOPRIGHT', db.timeXOffset, db.timeYOffset) + castbar.Text:Point('BOTTOMLEFT', castbar, 'TOPLEFT', db.textXOffset, db.textYOffset) else - castBar:SetStatusBarColor(self.db.colors.castColor.r, self.db.colors.castColor.g, self.db.colors.castColor.b) - end - castBar.Icon.texture:SetDesaturated(false) - else - castBar:SetStatusBarColor(self.db.colors.castNoInterruptColor.r, self.db.colors.castNoInterruptColor.g, self.db.colors.castNoInterruptColor.b) - - if self.db.colors.castbarDesaturate then - castBar.Icon.texture:SetDesaturated(true) - end - end - - self:StyleFilterUpdate(frame, "FAKE_Casting") -end - -function NP:Configure_CastBarScale(frame, scale, noPlayAnimation) - if frame.currentScale == scale then return end - local db = self.db.units[frame.UnitType].castbar - if not db.enable then return end - - local castBar = frame.CastBar - - if noPlayAnimation then - castBar:SetSize(db.width * scale, db.height * scale) - castBar.Icon:SetSize(db.iconSize * scale, db.iconSize * scale) - else - if castBar.scale:IsPlaying() or castBar.Icon.scale:IsPlaying() then - castBar.scale:Stop() - castBar.Icon.scale:Stop() + castbar.Time:Point('RIGHT', castbar, 'RIGHT', db.timeXOffset, db.timeYOffset) + castbar.Text:Point('LEFT', castbar, 'LEFT', db.textXOffset, db.textYOffset) end - castBar.scale.width:SetChange(db.width * scale) - castBar.scale.height:SetChange(db.height * scale) - castBar.scale:Play() + if db.hideTime then + castbar.Time:Hide() + else + castbar.Time:FontTemplate(LSM:Fetch('font', db.font), db.fontSize, db.fontOutline) + castbar.Time:Show() + end - castBar.Icon.scale.width:SetChange(db.iconSize * scale) - castBar.Icon.scale.height:SetChange(db.iconSize * scale) - castBar.Icon.scale:Play() + if db.hideSpellName then + castbar.Text:Hide() + else + castbar.Text:FontTemplate(LSM:Fetch('font', db.font), db.fontSize, db.fontOutline) + castbar.Text:Show() + end + elseif nameplate:IsElementEnabled('Castbar') then + nameplate:DisableElement('Castbar') end end - -function NP:Configure_CastBar(frame, configuring) - local db = self.db.units[frame.UnitType].castbar - local castBar = frame.CastBar - - castBar:SetPoint("TOP", frame.Health, "BOTTOM", db.xOffset, db.yOffset) - - if db.showIcon then - castBar.Icon:ClearAllPoints() - castBar.Icon:SetPoint(db.iconPosition == "RIGHT" and "BOTTOMLEFT" or "BOTTOMRIGHT", castBar, db.iconPosition == "RIGHT" and "BOTTOMRIGHT" or "BOTTOMLEFT", db.iconOffsetX, db.iconOffsetY) - castBar.Icon:Show() - else - castBar.Icon:Hide() - end - - castBar.Time:ClearAllPoints() - castBar.Name:ClearAllPoints() - - castBar.Spark:SetPoint("CENTER", castBar:GetStatusBarTexture(), "RIGHT", 0, 0) - castBar.Spark:SetHeight(db.height * 2) - - if db.textPosition == "BELOW" then - castBar.Time:SetPoint("TOPRIGHT", castBar, "BOTTOMRIGHT") - castBar.Name:SetPoint("TOPLEFT", castBar, "BOTTOMLEFT") - elseif db.textPosition == "ABOVE" then - castBar.Time:SetPoint("BOTTOMRIGHT", castBar, "TOPRIGHT") - castBar.Name:SetPoint("BOTTOMLEFT", castBar, "TOPLEFT") - else - castBar.Time:SetPoint("RIGHT", castBar, "RIGHT", -4, 0) - castBar.Name:SetPoint("LEFT", castBar, "LEFT", 4, 0) - end - - if configuring then - self:Configure_CastBarScale(frame, frame.currentScale or 1, configuring) - end - - castBar.Name:FontTemplate(LSM:Fetch("font", db.font), db.fontSize, db.fontOutline) - castBar.Time:FontTemplate(LSM:Fetch("font", db.font), db.fontSize, db.fontOutline) - - if db.hideSpellName then - castBar.Name:Hide() - else - castBar.Name:Show() - end - if db.hideTime then - castBar.Time:Hide() - else - castBar.Time:Show() - end - - castBar:SetStatusBarTexture(LSM:Fetch("statusbar", self.db.statusbar)) - - castBar.castTimeFormat = db.castTimeFormat - castBar.channelTimeFormat = db.channelTimeFormat -end - -function NP:Construct_CastBar(parent) - local frame = CreateFrame("StatusBar", "$parentCastBar", parent) - NP:StyleFrame(frame) - frame:SetScript("OnUpdate", NP.Update_CastBarOnUpdate) - - frame.Icon = CreateFrame("Frame", nil, frame) - frame.Icon.texture = frame.Icon:CreateTexture(nil, "BORDER") - frame.Icon.texture:SetAllPoints() - frame.Icon.texture:SetTexCoord(unpack(E.TexCoords)) - NP:StyleFrame(frame.Icon) - - frame.Time = frame:CreateFontString(nil, "OVERLAY") - frame.Time:SetJustifyH("RIGHT") - frame.Time:SetWordWrap(false) - - frame.Name = frame:CreateFontString(nil, "OVERLAY") - frame.Name:SetJustifyH("LEFT") - frame.Name:SetWordWrap(false) - - frame.Spark = frame:CreateTexture(nil, "OVERLAY") - frame.Spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]]) - frame.Spark:SetBlendMode("ADD") - frame.Spark:SetSize(15, 15) - - frame.holdTime = 0 - frame.interrupted = nil - - frame.scale = CreateAnimationGroup(frame) - frame.scale.width = frame.scale:CreateAnimation("Width") - frame.scale.width:SetDuration(0.2) - frame.scale.height = frame.scale:CreateAnimation("Height") - frame.scale.height:SetDuration(0.2) - - frame.Icon.scale = CreateAnimationGroup(frame.Icon) - frame.Icon.scale.width = frame.Icon.scale:CreateAnimation("Width") - frame.Icon.scale.width:SetDuration(0.2) - frame.Icon.scale.height = frame.Icon.scale:CreateAnimation("Height") - frame.Icon.scale.height:SetDuration(0.2) - - frame:Hide() - - return frame -end \ No newline at end of file diff --git a/ElvUI/Modules/Nameplates/Elements/ClassPower.lua b/ElvUI/Modules/Nameplates/Elements/ClassPower.lua new file mode 100644 index 0000000..a36b2ad --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/ClassPower.lua @@ -0,0 +1,310 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') +local LSM = E.Libs.LSM + +local _G = _G +local max, next, ipairs = max, next, ipairs + +local CreateFrame = CreateFrame +local UnitHasVehicleUI = UnitHasVehicleUI +local MAX_COMBO_POINTS = MAX_COMBO_POINTS + +local MAX_POINTS = { -- match to UF.classMaxResourceBar + DEATHKNIGHT = max(6, MAX_COMBO_POINTS), + ROGUE = max(7, MAX_COMBO_POINTS), + HERO = max(5, MAX_COMBO_POINTS), + DRUID = max(5, MAX_COMBO_POINTS) +} + +function NP:ClassPower_SetBarColor(bar, r, g, b) + bar:SetStatusBarColor(r, g, b) + + if bar.bg then + bar.bg:SetVertexColor(r * NP.multiplier, g * NP.multiplier, b * NP.multiplier) + end +end + +function NP:ClassPower_UpdateColor(powerType, rune) + local isRunes = powerType == 'RUNES' + + local classPower = self.classColor + local colors = NP.db.colors.classResources + local fallback = NP.db.colors.power[powerType] + + if isRunes and NP.db.colors.chargingRunes then + NP:Runes_UpdateCharged(self, rune) + elseif isRunes and rune and not classPower then + local color = colors.DEATHKNIGHT[rune.runeType or 0] + NP:ClassPower_SetBarColor(rune, color.r, color.g, color.b) + else + local classColor = not classPower and ((isRunes and colors.DEATHKNIGHT) or (powerType == 'COMBO_POINTS' and colors.comboPoints) or (powerType == 'ESSENCE' and colors.EVOKER) or (powerType == 'CHI' and colors.MONK)) + for i, bar in ipairs(self) do + local color = classPower or (isRunes and classColor[bar.runeType or 0]) or (classColor and classColor[i]) or colors[E.myclass] or fallback + NP:ClassPower_SetBarColor(bar, color.r, color.g, color.b) + end + end +end + +function NP:ClassPower_PostUpdate(Cur, _, needUpdate, powerType, chargedPoints) + if Cur and Cur > 0 then + self:Show() + else + self:Hide() + end + + if needUpdate then + NP:Update_ClassPower(self.__owner) + end + + if powerType == 'COMBO_POINTS' and E.myclass == 'ROGUE' then + NP.ClassPower_UpdateColor(self, powerType) + + if chargedPoints then + local color = NP.db.colors.classResources.chargedComboPoint + for _, chargedIndex in next, chargedPoints do + self[chargedIndex]:SetStatusBarColor(color.r, color.g, color.b) + self[chargedIndex].bg:SetVertexColor(color.r * NP.multiplier, color.g * NP.multiplier, color.b * NP.multiplier) + end + end + end +end + +function NP:Construct_ClassPower(nameplate) + local frameName = nameplate:GetName() + local ClassPower = CreateFrame('Frame', frameName..'ClassPower', nameplate) + ClassPower:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + ClassPower:Hide() + + local texture = LSM:Fetch('statusbar', NP.db.statusbar) + local total = MAX_POINTS[E.myclass] or 0 + + for i = 1, total do + local bar = CreateFrame('StatusBar', frameName..'ClassPower'..i, ClassPower) + bar:SetStatusBarTexture(texture) + NP.StatusBars[bar] = true + + bar.bg = ClassPower:CreateTexture(frameName..'ClassPower'..i..'bg', 'BORDER') + bar.bg:SetTexture(texture) + bar.bg:SetAllPoints() + + if nameplate == _G.ElvNP_Test then + local combo = NP.db.colors.classResources.comboPoints[i] + bar.bg:SetVertexColor(combo.r, combo.g, combo.b) + end + + ClassPower[i] = bar + end + + if nameplate == _G.ElvNP_Test then + ClassPower.Hide = ClassPower.Show + ClassPower:Show() + end + + ClassPower.UpdateColor = NP.ClassPower_UpdateColor + ClassPower.PostUpdate = NP.ClassPower_PostUpdate + + return ClassPower +end + +function NP:Update_ClassPower(nameplate) + local db = NP:PlateDB(nameplate) + + if nameplate == _G.ElvNP_Test then + if not db.nameOnly and db.classpower and db.classpower.enable then + NP.ClassPower_UpdateColor(nameplate.ClassPower, 'COMBO_POINTS') + nameplate.ClassPower:SetAlpha(1) + else + nameplate.ClassPower:SetAlpha(0) + end + end + + local target = nameplate.frameType == 'TARGET' + if (target or nameplate.frameType == 'PLAYER') and db.classpower and db.classpower.enable then + if not nameplate:IsElementEnabled('ClassPower') then + nameplate:EnableElement('ClassPower') + end + + local anchor = target and NP:GetClassAnchor() + nameplate.ClassPower:ClearAllPoints() + nameplate.ClassPower:Point('CENTER', anchor or nameplate, 'CENTER', db.classpower.xOffset, db.classpower.yOffset) + nameplate.ClassPower:Size(db.classpower.width, db.classpower.height) + + nameplate.ClassPower.classColor = db.classpower.classColor and RAID_CLASS_COLORS[E.myclass] + + for i = 1, #nameplate.ClassPower do + nameplate.ClassPower[i]:Hide() + nameplate.ClassPower[i].bg:Hide() + end + + local maxButtons = nameplate.ClassPower.__max + if maxButtons > 0 then + local Width = db.classpower.width / maxButtons + for i = 1, maxButtons do + local button = nameplate.ClassPower[i] + button:Show() + button.bg:Show() + button:ClearAllPoints() + + if i == 1 then + local width = Width - (maxButtons == 6 and 2 or 0) + button:Point('LEFT', nameplate.ClassPower, 'LEFT', 0, 0) + button:Size(width, db.classpower.height) + else + button:Point('LEFT', nameplate.ClassPower[i - 1], 'RIGHT', 1, 0) + button:Size(Width - 1, db.classpower.height) + + if i == maxButtons then + button:Point('RIGHT', nameplate.ClassPower) + end + end + end + end + else + if nameplate:IsElementEnabled('ClassPower') then + nameplate:DisableElement('ClassPower') + end + + nameplate.ClassPower:Hide() + end +end + +function NP:Runes_UpdateCharged(runes, rune) + local colors = NP.db.colors.classResources.DEATHKNIGHT + local classColor = (runes and runes.classColor) or (rune and rune.__owner and rune.__owner.classColor) + + if rune then + NP:ClassPower_SetBarColor(rune, UF:Runes_GetColor(rune, colors, classColor)) + elseif runes then + for _, bar in ipairs(runes) do + NP:ClassPower_SetBarColor(bar, UF:Runes_GetColor(bar, colors, classColor)) + end + end +end + +function NP:Runes_PostUpdate() + self:SetShown(not UnitHasVehicleUI('player')) + + if NP.db.colors.chargingRunes then + NP:Runes_UpdateCharged(self) + end +end + +function NP:Runes_UpdateChargedColor() + if NP.db.colors.chargingRunes then + NP:Runes_UpdateCharged(nil, self) + end +end + +function NP:Runes_PostUpdateColor(r, g, b, color, rune) + NP.ClassPower_UpdateColor(self, 'RUNES', rune) +end + +function NP:Construct_Runes(nameplate) + local frameName = nameplate:GetName() + local Runes = CreateFrame('Frame', frameName..'Runes', nameplate) + Runes:SetFrameStrata("BACKGROUND") + Runes:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + Runes:Hide() + + Runes.PostUpdate = NP.Runes_PostUpdate + Runes.PostUpdateColor = NP.Runes_PostUpdateColor + + local texture = LSM:Fetch('statusbar', NP.db.statusbar) + local color = NP.db.colors.classResources.DEATHKNIGHT[0] + + for i = 1, 6 do + local rune = CreateFrame('StatusBar', frameName..'Runes'..i, Runes) + rune:SetStatusBarTexture(texture) + rune:SetStatusBarColor(color.r, color.g, color.b) + rune.PostUpdateColor = NP.Runes_UpdateChargedColor + rune.__owner = Runes + NP.StatusBars[rune] = true + + rune.bg = rune:CreateTexture(frameName..'Runes'..i..'bg', 'BORDER') + rune.bg:SetVertexColor(color.r * NP.multiplier, color.g * NP.multiplier, color.b * NP.multiplier) + rune.bg:SetTexture(texture) + rune.bg:SetAllPoints() + rune.bg.multiplier = 0.35 + + Runes[i] = rune + end + + return Runes +end + +function NP:Update_Runes(nameplate) + local db = NP:PlateDB(nameplate) + + local target = nameplate.frameType == 'TARGET' + if (target or nameplate.frameType == 'PLAYER') and db.classpower and db.classpower.enable then + if not nameplate:IsElementEnabled('Runes') then + nameplate:EnableElement('Runes') + end + + local anchor = target and NP:GetClassAnchor() + nameplate.Runes:ClearAllPoints() + nameplate.Runes:Point('CENTER', anchor or nameplate, 'CENTER', db.classpower.xOffset, db.classpower.yOffset) + nameplate.Runes:Show() + + nameplate.Runes.classColor = db.classpower.classColor and RAID_CLASS_COLORS[E.myclass] + nameplate.Runes.sortOrder = (db.classpower.sortDirection ~= 'NONE') and db.classpower.sortDirection + nameplate.Runes.colorSpec = NP.db.colors.runeBySpec + + local width = db.classpower.width / 6 + nameplate.Runes:Size(db.classpower.width, db.classpower.height) + + for i = 1, 6 do + local rune = nameplate.Runes[i] + if i == 1 then + rune:Size(width, db.classpower.height) + rune:ClearAllPoints() + rune:Point('LEFT', nameplate.Runes, 'LEFT', 0, 0) + else + rune:Size(width - 1, db.classpower.height) + rune:ClearAllPoints() + rune:Point('LEFT', nameplate.Runes[i-1], 'RIGHT', 1, 0) + + if i == 6 then + rune:Point('RIGHT', nameplate.Runes) + end + end + end + else + if nameplate:IsElementEnabled('Runes') then + nameplate:DisableElement('Runes') + end + + nameplate.Runes:Hide() + end +end + +function NP:Construct_Stagger(nameplate) + local Stagger = CreateFrame('StatusBar', nameplate:GetName()..'Stagger', nameplate) + Stagger:SetStatusBarTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + Stagger:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + Stagger:Hide() + + NP.StatusBars[Stagger] = true + + return Stagger +end + +function NP:Update_Stagger(nameplate) + local db = NP:PlateDB(nameplate) + + local target = nameplate.frameType == 'TARGET' + if (target or nameplate.frameType == 'PLAYER') and db.classpower and db.classpower.enable then + if not nameplate:IsElementEnabled('Stagger') then + nameplate:EnableElement('Stagger') + end + + local anchor = target and NP:GetClassAnchor() + nameplate.Stagger:ClearAllPoints() + nameplate.Stagger:Point('CENTER', anchor or nameplate, 'CENTER', db.classpower.xOffset, db.classpower.yOffset) + + nameplate.Stagger:Size(db.classpower.width, db.classpower.height) + elseif nameplate:IsElementEnabled('Stagger') then + nameplate:DisableElement('Stagger') + end +end diff --git a/ElvUI/Modules/Nameplates/Elements/Health.lua b/ElvUI/Modules/Nameplates/Elements/Health.lua new file mode 100644 index 0000000..12da215 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Health.lua @@ -0,0 +1,302 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') +local LSM = E.Libs.LSM + +local ipairs = ipairs +local unpack = unpack +local UnitPlayerControlled = UnitPlayerControlled +local UnitClass = UnitClass +local UnitReaction = UnitReaction +local UnitIsConnected = UnitIsConnected +local CreateFrame = CreateFrame + +function NP:Health_UpdateColor(_, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end + if not unit or self.unit ~= unit then return end + local element = self.Health + + local r, g, b, t + if element.colorDisconnected and not UnitIsConnected(unit) then + t = self.colors.disconnected + elseif (element.colorClass and self.isPlayer) or (element.colorClassNPC and not self.isPlayer) or (element.colorClassPet and UnitPlayerControlled(unit) and not self.isPlayer) then + local _, class = UnitClass(unit) + t = self.colors.class[class] + elseif element.colorReaction and UnitReaction(unit, 'player') then + local reaction = UnitReaction(unit, 'player') + t = NP.db.colors.reactions[reaction == 4 and 'neutral' or reaction <= 3 and 'bad' or 'good'] + elseif element.colorSmooth then + r, g, b = self:ColorGradient(element.cur or 1, element.max or 1, unpack(element.smoothGradient or self.colors.smooth)) + elseif element.colorHealth then + t = NP.db.colors.health + end + + if t then + r, g, b = t.r, t.g, t.b + element.r, element.g, element.b = r, g, b -- save these for the style filter to switch back + end + + local sf = NP:StyleFilterChanges(self) + if sf.HealthColor then + r, g, b = sf.HealthColor.r, sf.HealthColor.g, sf.HealthColor.b + end + + if b then + element:SetStatusBarColor(r, g, b) + + if element.bg then + element.bg:SetVertexColor(r * NP.multiplier, g * NP.multiplier, b * NP.multiplier) + end + end + + if element.PostUpdateColor then + element:PostUpdateColor(unit, r, g, b) + end +end + +function NP:Construct_Health(nameplate) + local Health = CreateFrame('StatusBar', nameplate:GetName()..'Health', nameplate) + Health:SetParent(nameplate) + Health:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + Health:SetStatusBarTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + Health.considerSelectionInCombatHostile = true + Health.UpdateColor = NP.Health_UpdateColor + + NP.StatusBars[Health] = true + + local statusBarTexture = Health:GetStatusBarTexture() + local healthFlashTexture = Health:CreateTexture(nameplate:GetName()..'FlashTexture', 'OVERLAY') + healthFlashTexture:SetTexture(LSM:Fetch('background', 'ElvUI Blank')) + healthFlashTexture:Point('BOTTOMLEFT', statusBarTexture, 'BOTTOMLEFT') + healthFlashTexture:Point('TOPRIGHT', statusBarTexture, 'TOPRIGHT') + healthFlashTexture:Hide() + nameplate.HealthFlashTexture = healthFlashTexture + + local clipFrame = CreateFrame("Frame", nil, Health) + clipFrame:SetScript("OnUpdate", UF.HealthClipFrame_OnUpdate) + clipFrame:SetAllPoints() + clipFrame:EnableMouse(false) + clipFrame.__frame = Health + Health.ClipFrame = clipFrame + + return Health +end + +function NP:Health_SetColors(nameplate, threatColors) + if threatColors then -- managed by ThreatIndicator_PostUpdate + nameplate.Health:SetColorTapping(nil) + nameplate.Health:SetColorSelection(nil) + nameplate.Health.colorReaction = nil + nameplate.Health.colorClass = nil + else + local db = NP:PlateDB(nameplate) + nameplate.Health:SetColorTapping(true) + nameplate.Health:SetColorSelection(true) + nameplate.Health.colorReaction = true + nameplate.Health.colorClass = db.health and db.health.useClassColor + end +end + +function NP:Update_Health(nameplate, skipUpdate) + local db = NP:PlateDB(nameplate) + + NP:Health_SetColors(nameplate) + + if skipUpdate then return end + + if db.health.enable then + if not nameplate:IsElementEnabled('Health') then + nameplate:EnableElement('Health') + end + + nameplate.Health:Point('CENTER') + nameplate.Health:Point('LEFT') + nameplate.Health:Point('RIGHT') + + E:SetSmoothing(nameplate.Health, NP.db.smoothbars) + elseif nameplate:IsElementEnabled('Health') then + nameplate:DisableElement('Health') + end + + nameplate.Health.width = db.health.width + nameplate.Health.height = db.health.height + nameplate.Health:Height(db.health.height) +end + +function NP:Update_HealComm(nameplate) + local db = NP:PlateDB(nameplate) + + if db.health.enable and db.health.healPrediction then + if not nameplate:IsElementEnabled('HealCommBar') then + nameplate:EnableElement('HealCommBar') + end + + nameplate.HealCommBar.myBar:SetStatusBarColor(NP.db.colors.healPrediction.personal.r, NP.db.colors.healPrediction.personal.g, NP.db.colors.healPrediction.personal.b) + nameplate.HealCommBar.otherBar:SetStatusBarColor(NP.db.colors.healPrediction.others.r, NP.db.colors.healPrediction.others.g, NP.db.colors.healPrediction.others.b) + elseif nameplate:IsElementEnabled('HealCommBar') then + nameplate:DisableElement('HealCommBar') + end +end + +function NP.HealthClipFrame_HealComm(frame) + local pred = frame.HealCommBar + if pred then + NP:SetAlpha_HealComm(pred, true) + NP:SetVisibility_HealComm(pred) + end +end + +function NP:SetAlpha_HealComm(obj, show) + obj.myBar:SetAlpha(show and 1 or 0) + obj.otherBar:SetAlpha(show and 1 or 0) +end + +function NP:SetVisibility_HealComm(obj) + -- the first update is from `HealthClipFrame_HealComm` + -- we set this variable to allow `Configure_HealComm` to + -- update the elements overflow lock later on by option + if not obj.allowClippingUpdate then + obj.allowClippingUpdate = true + end + + if obj.maxOverflow > 1 then + obj.myBar:SetParent(obj.health) + obj.otherBar:SetParent(obj.health) + else + obj.myBar:SetParent(obj.parent) + obj.otherBar:SetParent(obj.parent) + end +end + +function NP:Construct_HealComm(frame) + local health = frame.Health + local parent = health.ClipFrame + + local myBar = CreateFrame("StatusBar", nil, parent) + local otherBar = CreateFrame("StatusBar", nil, parent) + + myBar:SetFrameLevel(health:GetFrameLevel()+1) + otherBar:SetFrameLevel(health:GetFrameLevel()+1) + + NP.StatusBars[myBar] = true + NP.StatusBars[otherBar] = true + + local healPrediction = { + myBar = myBar, + otherBar = otherBar, + PostUpdate = NP.UpdateHealComm, + maxOverflow = 1, + health = health, + parent = parent, + frame = frame + } + + NP:SetAlpha_HealComm(healPrediction) + + return healPrediction +end + +function NP:Configure_HealComm(frame) + local db = NP:PlateDB(frame) + + if db.health.enable and db.health.healPrediction then + if not frame:IsElementEnabled('HealComm4') then + frame:EnableElement('HealComm4') + end + + local healPrediction = frame.HealCommBar + local c = db.colors.healPrediction + healPrediction.maxOverflow = 1 + (c.maxOverflow or 0) + + if healPrediction.allowClippingUpdate then + NP:SetVisibility_HealComm(healPrediction) + end + + local health = frame.Health + local orientation = health:GetOrientation() + + local myBar = healPrediction.myBar + local otherBar = healPrediction.otherBar + + myBar:SetOrientation(orientation) + otherBar:SetOrientation(orientation) + + if orientation == "HORIZONTAL" then + local width = health:GetWidth() + width = (width > 0 and width) or health.WIDTH + local healthTexture = health:GetStatusBarTexture() + + myBar:Size(width, 0) + myBar:ClearAllPoints() + myBar:Point("TOP", health, "TOP") + myBar:Point("BOTTOM", health, "BOTTOM") + myBar:Point("LEFT", healthTexture, "RIGHT") + + otherBar:Size(width, 0) + otherBar:ClearAllPoints() + otherBar:Point("TOP", health, "TOP") + otherBar:Point("BOTTOM", health, "BOTTOM") + otherBar:Point("LEFT", myBar:GetStatusBarTexture(), "RIGHT") + else + local height = health:GetHeight() + height = (height > 0 and height) or health.HEIGHT + local healthTexture = health:GetStatusBarTexture() + + myBar:Size(0, height) + myBar:ClearAllPoints() + myBar:Point("LEFT", health, "LEFT") + myBar:Point("RIGHT", health, "RIGHT") + myBar:Point("BOTTOM", healthTexture, "TOP") + + otherBar:Size(0, height) + otherBar:ClearAllPoints() + otherBar:Point("LEFT", health, "LEFT") + otherBar:Point("RIGHT", health, "RIGHT") + otherBar:Point("BOTTOM", myBar:GetStatusBarTexture(), "TOP") + end + + frame.HealCommBar.myBar:SetStatusBarColor(NP.db.colors.healPrediction.personal.r, NP.db.colors.healPrediction.personal.g, NP.db.colors.healPrediction.personal.b) + frame.HealCommBar.otherBar:SetStatusBarColor(NP.db.colors.healPrediction.others.r, NP.db.colors.healPrediction.others.g, NP.db.colors.healPrediction.others.b) + elseif frame:IsElementEnabled('HealComm4') then + frame:DisableElement('HealComm4') + end +end + +local function UpdateFillBar(frame, previousTexture, bar, amount) + if amount == 0 then + bar:Hide() + return previousTexture + end + + local orientation = frame:GetOrientation() + bar:ClearAllPoints() + if orientation == "HORIZONTAL" then + bar:SetPoint("TOPLEFT", previousTexture, "TOPRIGHT") + bar:SetPoint("BOTTOMLEFT", previousTexture, "BOTTOMRIGHT") + else + bar:SetPoint("BOTTOMRIGHT", previousTexture, "TOPRIGHT") + bar:SetPoint("BOTTOMLEFT", previousTexture, "TOPLEFT") + end + + local totalWidth, totalHeight = frame:GetSize() + if orientation == "HORIZONTAL" then + bar:Width(totalWidth) + else + bar:Height(totalHeight) + end + + return bar:GetStatusBarTexture() +end + +function NP:UpdateHealComm(_, myIncomingHeal, allIncomingHeal) + local health = self.health + local previousTexture = health:GetStatusBarTexture() + + previousTexture = UpdateFillBar(health, previousTexture, self.myBar, myIncomingHeal) + UpdateFillBar(health, previousTexture, self.otherBar, allIncomingHeal) +end diff --git a/ElvUI/Modules/Nameplates/Elements/Plugins.lua b/ElvUI/Modules/Nameplates/Elements/Plugins.lua new file mode 100644 index 0000000..99206e8 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Plugins.lua @@ -0,0 +1,313 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local LSM = E.Libs.LSM + +local strfind = strfind +local ipairs, unpack = ipairs, unpack +local CreateFrame = CreateFrame + +local targetIndicators = {'Spark', 'TopIndicator', 'LeftIndicator', 'RightIndicator'} + +function NP:Construct_QuestIcons(nameplate) + local QuestIcons = CreateFrame('Frame', nameplate:GetName() .. 'QuestIcons', nameplate) + QuestIcons:Size(20) + QuestIcons:Hide() + + for _, object in ipairs(NP.QuestIcons.iconTypes) do + local icon = QuestIcons:CreateTexture(nil, 'BORDER', nil, 1) + icon.Text = QuestIcons:CreateFontString(nil, 'OVERLAY') + icon.Text:FontTemplate() + icon:Hide() + + QuestIcons[object] = icon + end + + QuestIcons.Item:SetTexCoord(unpack(E.TexCoords)) + QuestIcons.Chat:SetTexture([[Interface\WorldMap\ChatBubble_64.PNG]]) + QuestIcons.Chat:SetTexCoord(0, 0.5, 0.5, 1) + + return QuestIcons +end + +function NP:Update_QuestIcons(nameplate) + local plateDB = NP:PlateDB(nameplate) + local db = plateDB.questIcon + + if db and db.enable and (nameplate.frameType == 'FRIENDLY_NPC' or nameplate.frameType == 'ENEMY_NPC') then + if not nameplate:IsElementEnabled('QuestIcons') then + nameplate:EnableElement('QuestIcons') + end + + nameplate.QuestIcons:ClearAllPoints() + nameplate.QuestIcons:Point(E.InversePoints[db.position], nameplate, db.position, db.xOffset, db.yOffset) + + for _, object in ipairs(NP.QuestIcons.iconTypes) do + local icon = nameplate.QuestIcons[object] + icon:Size(db.size, db.size) + icon:SetAlpha(db.hideIcon and 0 or 1) + + local xoffset = strfind(db.textPosition, 'LEFT') and -2 or 2 + local yoffset = strfind(db.textPosition, 'BOTTOM') and 2 or -2 + icon.Text:ClearAllPoints() + icon.Text:Point('CENTER', icon, db.textPosition, xoffset, yoffset) + icon.Text:FontTemplate(LSM:Fetch('font', db.font), db.fontSize, db.fontOutline) + icon.Text:SetJustifyH('CENTER') + + icon.size, icon.position = db.size, db.position + end + elseif nameplate:IsElementEnabled('QuestIcons') then + nameplate:DisableElement('QuestIcons') + end +end + +function NP:Construct_ClassificationIndicator(nameplate) + return nameplate:CreateTexture(nameplate:GetName() .. 'ClassificationIndicator', 'OVERLAY') +end + +function NP:Update_ClassificationIndicator(nameplate) + local plateDB = NP:PlateDB(nameplate) + local db = plateDB.eliteIcon + + if db and db.enable and (nameplate.frameType == 'FRIENDLY_NPC' or nameplate.frameType == 'ENEMY_NPC') then + if not nameplate:IsElementEnabled('ClassificationIndicator') then + nameplate:EnableElement('ClassificationIndicator') + end + + nameplate.ClassificationIndicator:ClearAllPoints() + nameplate.ClassificationIndicator:Size(db.size, db.size) + nameplate.ClassificationIndicator:Point(E.InversePoints[db.position], nameplate, db.position, db.xOffset, db.yOffset) + elseif nameplate:IsElementEnabled('ClassificationIndicator') then + nameplate:DisableElement('ClassificationIndicator') + end +end + +function NP:Construct_TargetIndicator(nameplate) + local TargetIndicator = CreateFrame('Frame', nameplate:GetName() .. 'TargetIndicator', nameplate) + TargetIndicator:SetFrameLevel(nameplate:GetFrameLevel()-1) + + TargetIndicator.Shadow = CreateFrame('Frame', nil, TargetIndicator) + TargetIndicator.Shadow:Hide() + + for _, object in ipairs(targetIndicators) do + local indicator = TargetIndicator:CreateTexture(nil, 'BACKGROUND') + indicator:Hide() + + TargetIndicator[object] = indicator + end + + return TargetIndicator +end + +function NP:Update_TargetIndicator(nameplate) + local enabled = nameplate:IsElementEnabled('TargetIndicator') + if nameplate.frameType == 'PLAYER' then + if enabled then + nameplate:DisableElement('TargetIndicator') + end + + return + elseif not enabled then + nameplate:EnableElement('TargetIndicator') + end + + local tdb = NP.db.units.TARGET + local indicator = nameplate.TargetIndicator + indicator.arrow = E.Media.Textures[NP.db.units.TARGET.arrow] or E.Media.Textures.TopIndicator + indicator.lowHealthThreshold = NP.db.lowHealthThreshold + indicator.preferGlowColor = NP.db.colors.preferGlowColor + indicator.style = tdb.glowStyle + + if indicator.style ~= 'none' then + local style, color, size, spacing = tdb.glowStyle, NP.db.colors.glowColor, tdb.arrowSize, tdb.arrowSpacing + local r, g, b, a = color.r, color.g, color.b, color.a + local db = NP:PlateDB(nameplate) + + -- background glow is 2, 6, and 8; 2 is background glow only + if not db.health.enable and (style ~= 'style2' and style ~= 'style6' and style ~= 'style8') then + style = 'style2' + indicator.style = style + end + + -- top arrow is 3, 5, 6 + if indicator.TopIndicator and (style == 'style3' or style == 'style5' or style == 'style6') then + indicator.TopIndicator:Point('BOTTOM', nameplate.Health, 'TOP', 0, spacing) + indicator.TopIndicator:SetVertexColor(r, g, b, a) + indicator.TopIndicator:SetSize(size, size) + end + + -- side arrows are 4, 7, 8 + if indicator.LeftIndicator and indicator.RightIndicator and (style == 'style4' or style == 'style7' or style == 'style8') then + indicator.LeftIndicator:Point('LEFT', nameplate.Health, 'RIGHT', spacing, 0) + indicator.RightIndicator:Point('RIGHT', nameplate.Health, 'LEFT', -spacing, 0) + indicator.LeftIndicator:SetVertexColor(r, g, b, a) + indicator.RightIndicator:SetVertexColor(r, g, b, a) + indicator.LeftIndicator:SetSize(size, size) + indicator.RightIndicator:SetSize(size, size) + end + + -- border glow is 1, 5, 7 + if indicator.Shadow and (style == 'style1' or style == 'style5' or style == 'style7') then + indicator.Shadow:SetOutside(nameplate.Health, E.PixelMode and 6 or 8, E.PixelMode and 6 or 8, nil, true) + indicator.Shadow:SetBackdropBorderColor(r, g, b) + indicator.Shadow:SetAlpha(a) + end + + -- background glow is 2, 6, and 8 + if indicator.Spark and (style == 'style2' or style == 'style6' or style == 'style8') then + local size + if db.health.enable and not (db.nameOnly or nameOnlySF) then + parent = nameplate.Health + size = E.Border + 14 + else + parent = nameplate + size = -(E.Border + 4) + end + indicator.Spark:Point('TOPLEFT', parent, 'TOPLEFT', -(size * 2), size) + indicator.Spark:Point('BOTTOMRIGHT', parent, 'BOTTOMRIGHT', (size * 2), -size) + indicator.Spark:SetVertexColor(r, g, b, a) + end + end +end + +function NP:Construct_Highlight(nameplate) + Highlight = nameplate.nameplateAnchor.blizzHighlight + return Highlight +end + +function NP:Update_Highlight(nameplate, nameOnlySF) + local db = NP:PlateDB(nameplate) + + if NP.db.highlight and db.enable then + if not nameplate:IsElementEnabled('Highlight') then + nameplate:EnableElement('Highlight') + end + + local highlight = nameplate.Highlight + if highlight:GetParent() ~= nameplate then + highlight:SetParent(nameplate) + end + + if highlight:GetDrawLayer() ~= "OVERLAY" then + Highlight:SetDrawLayer("OVERLAY") + end + + highlight:SetTexture(E.Media.Textures.Spark) + highlight:SetAllPoints(nameplate) + + if db.health.enable and not (db.nameOnly or nameOnlySF) then + highlight:SetAlpha(0.75) + else + highlight:SetAlpha(0.50) + end + elseif nameplate:IsElementEnabled('Highlight') then + nameplate:DisableElement('Highlight') + end +end + +function NP:Construct_PVPRole(nameplate) + local texture = nameplate:CreateTexture(nameplate:GetName() .. 'PVPRole', 'OVERLAY', nil, 1) + texture:Size(40) + texture.HealerTexture = E.Media.Textures.Healer + texture.TankTexture = E.Media.Textures.Tank + texture:SetTexture(texture.HealerTexture) + + texture:Hide() + + return texture +end + +function NP:Update_PVPRole(nameplate) + local db = NP:PlateDB(nameplate) + + if (nameplate.frameType == 'FRIENDLY_PLAYER' or nameplate.frameType == 'ENEMY_PLAYER') and (db.markHealers or db.markTanks) then + if not nameplate:IsElementEnabled('PVPRole') then + nameplate:EnableElement('PVPRole') + end + + nameplate.PVPRole.ShowHealers = db.markHealers + nameplate.PVPRole.ShowTanks = db.markTanks + + nameplate.PVPRole:Point('RIGHT', nameplate.Health, 'LEFT', -6, 0) + elseif nameplate:IsElementEnabled('PVPRole') then + nameplate:DisableElement('PVPRole') + end +end + +function NP:Update_Fader(nameplate) + local db = NP:PlateDB(nameplate) + local vis = db.visibility + + if not vis or vis.showAlways then + if nameplate:IsElementEnabled('Fader') then + nameplate:DisableElement('Fader') + + NP:PlateFade(nameplate, 0.5, nameplate:GetAlpha(), 1) + end + elseif db.enable then + if not nameplate.Fader then + nameplate.Fader = {} + end + + if not nameplate:IsElementEnabled('Fader') then + nameplate:EnableElement('Fader') + + nameplate.Fader:SetOption('MinAlpha', 0) + nameplate.Fader:SetOption('Smooth', 0.3) + nameplate.Fader:SetOption('Hover', true) + --nameplate.Fader:SetOption('Power', true) + nameplate.Fader:SetOption('Health', true) + nameplate.Fader:SetOption('Casting', true) + end + + nameplate.Fader:SetOption('Combat', vis.showInCombat) + nameplate.Fader:SetOption('PlayerTarget', vis.showWithTarget) + nameplate.Fader:SetOption('DelayAlpha', (vis.alphaDelay > 0 and vis.alphaDelay) or nil) + nameplate.Fader:SetOption('Delay', (vis.hideDelay > 0 and vis.hideDelay) or nil) + + nameplate.Fader:ForceUpdate() + end +end + +function NP:Construct_Cutaway(nameplate) + local frameName = nameplate:GetName() + local Cutaway = {} + + Cutaway.Health = nameplate.Health.ClipFrame:CreateTexture(frameName .. 'CutawayHealth') + local healthTexture = nameplate.Health:GetStatusBarTexture() + Cutaway.Health:Point('TOPLEFT', healthTexture, 'TOPRIGHT') + Cutaway.Health:Point('BOTTOMLEFT', healthTexture, 'BOTTOMRIGHT') + + -- Cutaway.Power = nameplate.Power.ClipFrame:CreateTexture(frameName .. 'CutawayPower') + -- local powerTexture = nameplate.Power:GetStatusBarTexture() + -- Cutaway.Power:Point('TOPLEFT', powerTexture, 'TOPRIGHT') + -- Cutaway.Power:Point('BOTTOMLEFT', powerTexture, 'BOTTOMRIGHT') + + return Cutaway +end + +function NP:Update_Cutaway(nameplate) + local eitherEnabled = NP.db.cutaway.health.enabled or NP.db.cutaway.power.enabled + if not eitherEnabled then + if nameplate:IsElementEnabled('Cutaway') then + nameplate:DisableElement('Cutaway') + end + else + if not nameplate:IsElementEnabled('Cutaway') then + nameplate:EnableElement('Cutaway') + end + + nameplate.Cutaway:UpdateConfigurationValues(NP.db.cutaway) + + if NP.db.cutaway.health.forceBlankTexture then + nameplate.Cutaway.Health:SetTexture(E.media.blankTex) + else + nameplate.Cutaway.Health:SetTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + end + + -- if NP.db.cutaway.power.forceBlankTexture then + -- nameplate.Cutaway.Power:SetTexture(E.media.blankTex) + -- else + -- nameplate.Cutaway.Power:SetTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + -- end + end +end diff --git a/ElvUI/Modules/Nameplates/Elements/Portraits.lua b/ElvUI/Modules/Nameplates/Elements/Portraits.lua new file mode 100644 index 0000000..4d2be48 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Portraits.lua @@ -0,0 +1,66 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') + +local _G = _G +local unpack = unpack +local hooksecurefunc = hooksecurefunc + +function NP:Portrait_PostUpdate() + local nameplate = self.__owner + local db = NP:PlateDB(nameplate) + local sf = NP:StyleFilterChanges(nameplate) + + if sf.Portrait or (db.portrait and db.portrait.enable) then + if db.portrait.classicon and nameplate.isPlayer then + self:SetTexture([[Interface\WorldStateFrame\Icons-Classes]]) + self:SetTexCoord(unpack(_G.CLASS_ICON_TCOORDS[nameplate.classFile])) + self.backdrop:Hide() + else + self:SetTexCoord(.18, .82, .18, .82) + self.backdrop:Show() + end + else + self.backdrop:Hide() + end +end + +function NP:Update_PortraitBackdrop() + if self.backdrop then + self.backdrop:SetShown(self:IsShown()) + end +end + +function NP:Construct_Portrait(nameplate) + local Portrait = nameplate:CreateTexture(nameplate:GetName() .. 'Portrait', 'OVERLAY', nil, 2) + Portrait:SetTexCoord(.18, .82, .18, .82) + Portrait:CreateBackdrop(nil, nil, nil, nil, nil, true, true) + Portrait:Hide() + + Portrait.PostUpdate = NP.Portrait_PostUpdate + hooksecurefunc(Portrait, 'Hide', NP.Update_PortraitBackdrop) + hooksecurefunc(Portrait, 'Show', NP.Update_PortraitBackdrop) + + return Portrait +end + +function NP:Update_Portrait(nameplate) + local db = NP:PlateDB(nameplate) + local sf = NP:StyleFilterChanges(nameplate) + + if sf.Portrait or (db.portrait and db.portrait.enable) then + if not nameplate:IsElementEnabled('Portrait') then + nameplate:EnableElement('Portrait') + nameplate.Portrait:ForceUpdate() + end + + nameplate.Portrait:Size(db.portrait.width, db.portrait.height) + + -- These values are forced in name only mode inside of DisablePlate + if not (db.nameOnly or sf.NameOnly) then + nameplate.Portrait:ClearAllPoints() + nameplate.Portrait:Point(E.InversePoints[db.portrait.position], nameplate, db.portrait.position, db.portrait.xOffset, db.portrait.yOffset) + end + elseif nameplate:IsElementEnabled('Portrait') then + nameplate:DisableElement('Portrait') + end +end diff --git a/ElvUI/Modules/Nameplates/Elements/Power.lua b/ElvUI/Modules/Nameplates/Elements/Power.lua new file mode 100644 index 0000000..db39774 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Power.lua @@ -0,0 +1,148 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local UF = E:GetModule('UnitFrames') +local LSM = E.Libs.LSM + +local unpack = unpack +local UnitPlayerControlled = UnitPlayerControlled +local UnitClass = UnitClass +local UnitReaction = UnitReaction +local UnitIsConnected = UnitIsConnected +local CreateFrame = CreateFrame +local UnitPowerType = UnitPowerType +local POWERTYPE_ALTERNATE = 10 + +function NP:Power_UpdateColor(_, unit) + if unit and self.isNamePlate and unit:sub(1, 9) ~= "nameplate" then + local isUnit = self.unit and UnitIsUnit(self.unit, unit) + if isUnit then + unit = self.unit + end + end + if self.unit ~= unit then return end + + local element = self.Power + local ptype, ptoken, altR, altG, altB = UnitPowerType(unit) + element.token = ptoken + + local sf = NP:StyleFilterChanges(self) + if sf.PowerColor then return end + + local Selection = element.colorSelection and NP:UnitSelectionType(unit, element.considerSelectionInCombatHostile) + + local r, g, b, t, atlas + if element.colorDisconnected and not UnitIsConnected(unit) then + t = self.colors.disconnected + elseif element.colorPower then + if element.displayType ~= POWERTYPE_ALTERNATE then + t = NP.db.colors.power[ptoken or ptype] + if not t then + if element.GetAlternativeColor then + r, g, b = element:GetAlternativeColor(unit, ptype, ptoken, altR, altG, altB) + elseif altR then + r, g, b = altR, altG, altB + if r > 1 or g > 1 or b > 1 then -- BUG: As of 7.0.3, altR, altG, altB may be in 0-1 or 0-255 range. + r, g, b = r / 255, g / 255, b / 255 + end + end + end + else + t = NP.db.colors.power.ALT_POWER + end + + if element.useAtlas and t and t.atlas then + atlas = t.atlas + end + elseif (element.colorClass and self.isPlayer) or (element.colorClassNPC and not self.isPlayer) or (element.colorClassPet and UnitPlayerControlled(unit) and not self.isPlayer) then + local _, class = UnitClass(unit) + t = self.colors.class[class] + elseif Selection then + t = NP.db.colors.selection[Selection] + elseif element.colorReaction and UnitReaction(unit, 'player') then + local reaction = UnitReaction(unit, 'player') + t = NP.db.colors.reactions[reaction == 4 and 'neutral' or reaction <= 3 and 'bad' or 'good'] + elseif element.colorSmooth then + local adjust = 0 - (element.min or 0) + r, g, b = self:ColorGradient((element.cur or 1) + adjust, (element.max or 1) + adjust, unpack(element.smoothGradient or self.colors.smooth)) + end + + if t then + r, g, b = t[1] or t.r, t[2] or t.g, t[3] or t.b + end + + if atlas then + element:SetStatusBarTexture(atlas) + element:SetStatusBarColor(1, 1, 1) + elseif b then + element:SetStatusBarColor(r, g, b) + end + + if element.bg and b then element.bg:SetVertexColor(r * NP.multiplier, g * NP.multiplier, b * NP.multiplier) end + + if element.PostUpdateColor then + element:PostUpdateColor(unit, r, g, b) + end +end + +function NP:Power_PostUpdate(_, cur) --unit, cur, min, max + local db = NP:PlateDB(self.__owner) + if not db.enable then return end + + if self.__owner.frameType ~= 'PLAYER' and db.power.displayAltPower and not self.displayType then + self:Hide() + return + end + + if db.power and db.power.enable and db.power.hideWhenEmpty and (cur == 0) then + self:Hide() + else + self:Show() + end +end + +function NP:Construct_Power(nameplate) + local Power = CreateFrame('StatusBar', nameplate:GetName()..'Power', nameplate) + Power:CreateBackdrop('Transparent', nil, nil, nil, nil, true, true) + + NP.StatusBars[Power] = true + + --Power.frequentUpdates = true + Power.colorTapping = false + Power.colorClass = false + + Power.PostUpdate = NP.Power_PostUpdate + Power.UpdateColor = NP.Power_UpdateColor + + local clipFrame = CreateFrame('Frame', nil, Power) + clipFrame:SetAllPoints() + clipFrame:EnableMouse(false) + clipFrame.__frame = Power + Power.ClipFrame = clipFrame + + return Power +end + +function NP:Update_Power(nameplate) + local db = NP:PlateDB(nameplate) + + if db.power.enable then + if not nameplate:IsElementEnabled('Power') then + nameplate:EnableElement('Power') + end + + nameplate.Power:SetStatusBarTexture(LSM:Fetch('statusbar', NP.db.statusbar)) + nameplate.Power:Point('CENTER', nameplate, 'CENTER', db.power.xOffset, db.power.yOffset) + + E:SetSmoothing(nameplate.Power, NP.db.smoothbars) + elseif nameplate:IsElementEnabled('Power') then + nameplate:DisableElement('Power') + end + + nameplate.Power.displayAltPower = db.power.displayAltPower + nameplate.Power.useAtlas = db.power.useAtlas + nameplate.Power.colorClass = db.power.useClassColor + nameplate.Power.colorPower = not db.power.useClassColor + nameplate.Power.width = db.power.width + nameplate.Power.height = db.power.height + nameplate.Power:Size(db.power.width, db.power.height) +end diff --git a/ElvUI/Modules/Nameplates/Elements/PvP.lua b/ElvUI/Modules/Nameplates/Elements/PvP.lua new file mode 100644 index 0000000..844bd8d --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/PvP.lua @@ -0,0 +1,64 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') + +local strlower = strlower + +function NP:PvPIndicator_PostUpdate(_, status) + if status and status ~= 'FFA' and (not self.Badge or not self.Badge:IsShown()) then + self:SetAtlas('bfa-landingbutton-'..strlower(status)..'-up') + self:SetTexCoord(0, 1, 0, 1) + end +end + +function NP:Construct_PvPIndicator(nameplate) + local PvPIndicator = nameplate:CreateTexture(nil, 'OVERLAY') + PvPIndicator.Badge_ = nameplate:CreateTexture(nil, 'ARTWORK') + PvPIndicator.PostUpdate = NP.PvPIndicator_PostUpdate + + return PvPIndicator +end + +function NP:Update_PvPIndicator(nameplate) + local db = NP:PlateDB(nameplate) + + if db.pvpindicator and db.pvpindicator.enable then + if not nameplate:IsElementEnabled('PvPIndicator') then + nameplate:EnableElement('PvPIndicator') + end + + nameplate.PvPIndicator:Size(db.pvpindicator.size, db.pvpindicator.size) + nameplate.PvPIndicator.Badge_:Size(db.pvpindicator.size + 14, db.pvpindicator.size + 16) + + nameplate.PvPIndicator.Badge = nil + + if db.pvpindicator.showBadge then + nameplate.PvPIndicator.Badge = nameplate.PvPIndicator.Badge_ + end + + nameplate.PvPIndicator:ClearAllPoints() + nameplate.PvPIndicator:Point(E.InversePoints[db.pvpindicator.position], nameplate, db.pvpindicator.position, db.pvpindicator.xOffset, db.pvpindicator.yOffset) + elseif nameplate:IsElementEnabled('PvPIndicator') then + nameplate:DisableElement('PvPIndicator') + end +end + +function NP:Construct_PvPClassificationIndicator(nameplate) + local PvPClassificationIndicator = nameplate:CreateTexture(nil, 'OVERLAY') + return PvPClassificationIndicator +end + +function NP:Update_PvPClassificationIndicator(nameplate) + local db = NP:PlateDB(nameplate) + + if (nameplate.frameType == 'ENEMY_PLAYER' or nameplate.frameType == 'FRIENDLY_PLAYER' or nameplate.frameType == 'PLAYER') and db.pvpclassificationindicator and db.pvpclassificationindicator.enable then + if not nameplate:IsElementEnabled('PvPClassificationIndicator') then + nameplate:EnableElement('PvPClassificationIndicator') + end + + nameplate.PvPClassificationIndicator:ClearAllPoints() + nameplate.PvPClassificationIndicator:Point(E.InversePoints[db.pvpclassificationindicator.position], nameplate, db.pvpclassificationindicator.position, db.pvpclassificationindicator.xOffset, db.pvpclassificationindicator.yOffset) + nameplate.PvPClassificationIndicator:Size(db.pvpclassificationindicator.size) + elseif nameplate:IsElementEnabled('PvPClassificationIndicator') then + nameplate:DisableElement('PvPClassificationIndicator') + end +end diff --git a/ElvUI/Modules/Nameplates/Elements/RaidTargetIndicator.lua b/ElvUI/Modules/Nameplates/Elements/RaidTargetIndicator.lua new file mode 100644 index 0000000..f9a6de9 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/RaidTargetIndicator.lua @@ -0,0 +1,41 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') + +local GetRaidTargetIndex = GetRaidTargetIndex +local SetRaidTargetIconTexture = SetRaidTargetIconTexture + +function NP:RaidTargetIndicator_Override() + local element = self.RaidTargetIndicator + local index = self.unit and GetRaidTargetIndex(self.unit) + + if index then + SetRaidTargetIconTexture(element, index) + element:Show() + else + element:Hide() + end +end + +function NP:Construct_RaidTargetIndicator(nameplate) + local RaidTargetIndicator = nameplate:CreateTexture(nil, 'OVERLAY', nil, 7) + RaidTargetIndicator.Override = NP.RaidTargetIndicator_Override + RaidTargetIndicator:Hide() + + return RaidTargetIndicator +end + +function NP:Update_RaidTargetIndicator(nameplate) + local db = NP:PlateDB(nameplate) + + if db.raidTargetIndicator and db.raidTargetIndicator.enable then + if not nameplate:IsElementEnabled('RaidTargetIndicator') then + nameplate:EnableElement('RaidTargetIndicator') + end + + nameplate.RaidTargetIndicator:ClearAllPoints() + nameplate.RaidTargetIndicator:Point(E.InversePoints[db.raidTargetIndicator.position], nameplate, db.raidTargetIndicator.position, db.raidTargetIndicator.xOffset, db.raidTargetIndicator.yOffset) + nameplate.RaidTargetIndicator:Size(db.raidTargetIndicator.size) + elseif nameplate:IsElementEnabled('RaidTargetIndicator') then + nameplate:DisableElement('RaidTargetIndicator') + end +end diff --git a/ElvUI/Modules/Nameplates/Elements/Tags.lua b/ElvUI/Modules/Nameplates/Elements/Tags.lua new file mode 100644 index 0000000..b8ece83 --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Tags.lua @@ -0,0 +1,38 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') +local LSM = E.Libs.LSM + +function NP:Construct_TagText(nameplate) + local Text = nameplate:CreateFontString(nil, 'OVERLAY') + Text:FontTemplate(E.LSM:Fetch('font', NP.db.font), NP.db.fontSize, NP.db.fontOutline) + + return Text +end + +function NP:Update_TagText(nameplate, element, db, hide) + if not db then return end + + if db.enable and not hide then + nameplate:Tag(element, db.format or '') + element:FontTemplate(LSM:Fetch('font', db.font), db.fontSize, db.fontOutline) + element:UpdateTag() + + element:ClearAllPoints() + element:Point(E.InversePoints[db.position], db.parent == 'Nameplate' and nameplate or nameplate[db.parent], db.position, db.xOffset, db.yOffset) + element:Show() + else + nameplate:Untag(element) + element:Hide() + end +end + +function NP:Update_Tags(nameplate, nameOnlySF) + local db = NP:PlateDB(nameplate) + local hide = db.nameOnly or nameOnlySF + + NP:Update_TagText(nameplate, nameplate.Name, db.name) + NP:Update_TagText(nameplate, nameplate.Title, db.title) + NP:Update_TagText(nameplate, nameplate.Level, db.level, hide) + NP:Update_TagText(nameplate, nameplate.Health.Text, db.health and db.health.text, hide) + --NP:Update_TagText(nameplate, nameplate.Power.Text, db.power and db.power.text, hide) +end diff --git a/ElvUI/Modules/Nameplates/Elements/Threat.lua b/ElvUI/Modules/Nameplates/Elements/Threat.lua new file mode 100644 index 0000000..29e79fd --- /dev/null +++ b/ElvUI/Modules/Nameplates/Elements/Threat.lua @@ -0,0 +1,96 @@ +local E, L, V, P, G = unpack(ElvUI) +local NP = E:GetModule('NamePlates') + +local UnitName = UnitName +local UnitExists = UnitExists +local UnitIsUnit = UnitIsUnit + +NP.ThreatPets = { +} + +function NP:ThreatIndicator_PreUpdate(unit, pass) + local nameplate, db, unitTarget, imTank = self.__owner, NP.db.threat, unit..'target', E.myrole == 'TANK' or NP.GroupRoles[E.myname] == 'TANK' + local unitRole = NP.IsInGroup and (UnitExists(unitTarget) and not UnitIsUnit(unitTarget, 'player')) and NP.GroupRoles[UnitName(unitTarget)] or 'NONE' + local unitTank = unitRole == 'TANK' or (db.beingTankedByPet and NP.ThreatPets[NP:UnitNPCID(unitTarget)]) + local isTank, offTank, feedbackUnit = unitTank or imTank, db.beingTankedByTank and (unitTank and imTank) or false, (unitTank and unitTarget) or 'player' + + nameplate.ThreatScale = nil + + if pass then + return isTank, offTank, feedbackUnit + else + self.feedbackUnit = feedbackUnit + self.offTank = offTank + self.isTank = isTank + end +end + +function NP:ThreatIndicator_PostUpdate(unit, status) + local nameplate, colors, db = self.__owner, NP.db.colors.threat, NP.db.threat + local sf = NP:StyleFilterChanges(nameplate) + if not status and not sf.Scale then + nameplate.ThreatScale = 1 + NP:ScalePlate(nameplate, 1) + elseif status and db.enable and db.useThreatColor then + NP:Health_SetColors(nameplate, true) + nameplate.ThreatStatus = status + + local Color, Scale + if status == 3 then -- securely tanking + Color = self.offTank and colors.offTankColor or self.isTank and colors.goodColor or colors.badColor + Scale = self.isTank and db.goodScale or db.badScale + elseif status == 2 then -- insecurely tanking + Color = self.offTank and colors.offTankColorBadTransition or self.isTank and colors.badTransition or colors.goodTransition + Scale = 1 + elseif status == 1 then -- not tanking but threat higher than tank + Color = self.offTank and colors.offTankColorGoodTransition or self.isTank and colors.goodTransition or colors.badTransition + Scale = 1 + else -- not tanking at all + Color = self.isTank and colors.badColor or colors.goodColor + Scale = self.isTank and db.badScale or db.goodScale + end + + if sf.HealthColor then + self.r, self.g, self.b = Color.r, Color.g, Color.b + else + nameplate.Health:SetStatusBarColor(Color.r, Color.g, Color.b) + end + + if Scale then + nameplate.ThreatScale = Scale + + if not sf.Scale then + NP:ScalePlate(nameplate, Scale) + end + end + end +end + +function NP:Construct_ThreatIndicator(nameplate) + local ThreatIndicator = nameplate:CreateTexture(nil, 'OVERLAY') + ThreatIndicator:Size(16, 16) + ThreatIndicator:Hide() + ThreatIndicator:Point('CENTER', nameplate, 'TOPRIGHT') + + ThreatIndicator.PreUpdate = NP.ThreatIndicator_PreUpdate + ThreatIndicator.PostUpdate = NP.ThreatIndicator_PostUpdate + + return ThreatIndicator +end + +function NP:Update_ThreatIndicator(nameplate) + local db = NP.db.threat + if nameplate.frameType == 'ENEMY_NPC' and db.enable then + if not nameplate:IsElementEnabled('ThreatIndicator') then + nameplate:EnableElement('ThreatIndicator') + end + + if db.indicator then + nameplate.ThreatIndicator:SetAlpha(1) + else + nameplate.ThreatIndicator:SetAlpha(0) + end + elseif nameplate:IsElementEnabled('ThreatIndicator') then + nameplate:DisableElement('ThreatIndicator') + end +end diff --git a/ElvUI/Modules/Nameplates/Load_Nameplates.xml b/ElvUI/Modules/Nameplates/Load_Nameplates.xml index b9ed7f9..12a2d4f 100644 --- a/ElvUI/Modules/Nameplates/Load_Nameplates.xml +++ b/ElvUI/Modules/Nameplates/Load_Nameplates.xml @@ -1,17 +1,19 @@ - -