diff --git a/ClearFont.ttf b/ClearFont.ttf new file mode 100644 index 0000000..e955207 Binary files /dev/null and b/ClearFont.ttf differ diff --git a/Injector.lua b/Injector.lua new file mode 100644 index 0000000..db3104c --- /dev/null +++ b/Injector.lua @@ -0,0 +1,1256 @@ +Injector = CreateFrame("Frame","Injector",UIParent) + +Injector:SetScript("OnEvent", function(self, event, ...) + self[event](self, event, ...) +end) + +InjectorConfig.SetupIndicators = InjectorConfig.SetupIndicators or {} + +InjectorConfig.unitGap = InjectorConfig.unitGap or 10 +InjectorConfig.groupGap = InjectorConfig.groupGap or 10 +InjectorConfig.unitGrowth = InjectorConfig.unitGrowth or "RIGHT" +InjectorConfig.groupGrowth = InjectorConfig.groupGrowth or "TOP" +InjectorConfig.resize = InjectorConfig.resize or { after = 27, to = 0.8 } -- = if number of players in raid exeeds 27 then resize to 0.8 +InjectorConfig.anchorpoint = InjectorConfig.anchorpoint or "BOTTOMLEFT" +InjectorConfig.outOfRangeAlpha = InjectorConfig.outOfRangeAlpha or 0.4 +InjectorConfig.incomingHealTimeframe = InjectorConfig.incomingHealTimeframe or 1.5 +InjectorConfig.IndicatorAuras = InjectorConfig.IndicatorAuras or {} +local InjectorUnitInRange = InjectorConfig.UnitInRangeFunc or UnitInRange + +local auras = InjectorConfig.IndicatorAuras +local dtypes = InjectorConfig.DebuffTypes +local traceheals = InjectorConfig.TraceHeals +local colors = setmetatable(InjectorConfig.Colors or {},{ __index = function(t,k) return RAID_CLASS_COLORS[k] end }) +local OORUnits = setmetatable({},{__mode = 'k'}) +local inCL = setmetatable({},{__index = function (t,k) return 0 end}) +local buffer = {} +local loaded = {} + +if not ClickCastFrames then + ClickCastFrames = {} + end +local InjectorString = "|cffff7777Injector: |r" +local Roster = {} +local guidMap = {} +local group_headers = {} + +local UnitHealth = UnitHealth +local UnitHealthMax = UnitHealthMax +local UnitPower = UnitPower +local UnitPowerMax = UnitPowerMax +local UnitAura = UnitAura +local UnitAffectingCombat = UnitAffectingCombat +local bit_band = bit.band +local _, helpers = ... +local utf8sub = helpers.utf8sub +local reverse = helpers.Reverse +local UIFrameScale = helpers.UIFrameScale +local UIFrameScaleOut = helpers.UIFrameScaleOut +local InjectorDB = {} + +local QuickHealth +local HealComm + +Injector:RegisterEvent("ADDON_LOADED") +function Injector.ADDON_LOADED(self,event,arg1) + if arg1 == "Injector" then + + InjectorDB_Global = InjectorDB_Global or {} + InjectorDB_Char = InjectorDB_Char or {} + InjectorDB_Global.charspec = InjectorDB_Global.charspec or {} + user = UnitName("player").."@"..GetRealmName() + if InjectorDB_Global.charspec[user] then + setmetatable(InjectorDB,{ __index = function(t,k) return InjectorDB_Char[k] end, __newindex = function(t,k,v) rawset(InjectorDB_Char,k,v) end}) + else + setmetatable(InjectorDB,{ __index = function(t,k) return InjectorDB_Global[k] end, __newindex = function(t,k,v) rawset(InjectorDB_Global,k,v) end}) + end + + InjectorDB.pos = InjectorDB.pos or {} + InjectorDB.pos.x = InjectorDB.pos.x or 0 + InjectorDB.pos.y = InjectorDB.pos.y or 0 + InjectorDB.pos.point = InjectorDB.pos.point or "CENTER" + + InjectorDB.pet_pos = InjectorDB.pet_pos or {} + InjectorDB.pet_pos.x = InjectorDB.pet_pos.x or 0 + InjectorDB.pet_pos.y = InjectorDB.pet_pos.y or 0 + InjectorDB.pet_pos.point = InjectorDB.pet_pos.point or "CENTER" + + InjectorDB.scale = InjectorDB.scale or 1 + + if InjectorConfig.disableBlizzardParty then + helpers.DisableBlizzParty() + end + + if InjectorConfig.useHealComm then + HealComm = LibStub:GetLibrary("LibHealComm-4.0",true); + if HealComm then + if InjectorConfig.incomingHealIgnoreHots then + HealComm.InjectorHealType = HealComm.CASTED_HEALS + else + HealComm.InjectorHealType = HealComm.ALL_HEALS + HealComm.RegisterCallback(self, "HealComm_HealUpdated", "HealUpdated"); -- hots + end + HealComm.RegisterCallback(self, "HealComm_HealStarted", "HealUpdated"); + HealComm.RegisterCallback(self, "HealComm_HealStopped", "HealUpdated"); + end + end + if InjectorConfig.useQuickHealth then + QuickHealth = LibStub and LibStub("LibQuickHealth-2.0", true) + if QuickHealth then + UnitHealth = QuickHealth.UnitHealth + Injector.UnitHealthUpdated = function(self, event, unit, h, hm) + if Roster[unit] then + self:UNIT_HEALTH(nil, unit) + end + end + QuickHealth.RegisterCallback(self, "UnitHealthUpdated") + end + end + self:RegisterEvent("UNIT_HEALTH") + self:RegisterEvent("UNIT_MAXHEALTH") + Injector.UNIT_MAXHEALTH = Injector.UNIT_HEALTH + + if not InjectorConfig.disableManaBar then + self:RegisterEvent("UNIT_MANA") + self:RegisterEvent("UNIT_MAXMANA") + self:RegisterEvent("UNIT_DISPLAYPOWER") + Injector.UNIT_MAXMANA = Injector.UNIT_MANA + end + if InjectorConfig.AggroStatus then + self:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE") + end + if InjectorConfig.ReadyCheck then + self:RegisterEvent("READY_CHECK") + self:RegisterEvent("READY_CHECK_CONFIRM") + self:RegisterEvent("READY_CHECK_FINISHED") + end + if InjectorConfig.TargetStatus then + self.previousTarget = "player" + self:RegisterEvent("PLAYER_TARGET_CHANGED") + end + if InjectorConfig.MainTankStatus then + self:RegisterEvent("PLAYER_ROLES_ASSIGNED") + self:RegisterEvent("PARTY_MEMBERS_CHANGED") + self.PLAYER_ROLES_ASSIGNED = self.UpdateMainTanks + self.PARTY_MEMBERS_CHANGED = self.UpdateMainTanks + end + + self:RegisterEvent("UNIT_AURA") + + if InjectorConfig.TraceHeals then + self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") + end + + self:RegisterEvent("RAID_ROSTER_UPDATE") + + if not InjectorConfig.raidIcons or not InjectorConfig.SetupIcons.raidicon then + InjectorConfig.SetupIcons.raidicon = nil + else + self:RegisterEvent("RAID_TARGET_UPDATE") + end + + self:RegisterEvent("UNIT_ENTERED_VEHICLE") + self:RegisterEvent("UNIT_EXITED_VEHICLE") + + + self.anchor = self:CreateAnchor("pos") + + InjectorConfig.maxgroups = InjectorConfig.maxgroups or 8 + + + local arrangeHeaders = function(prv_group, notreverse) + local p1, p2 + local xgap = 0 + local ygap = InjectorConfig.groupGap + local point, direction = reverse(InjectorConfig.unitGrowth) + local grgrowth = notreverse and reverse(InjectorConfig.groupGrowth) or InjectorConfig.groupGrowth + if grgrowth == "TOP" then + if direction == "VERTICAL" then point = "" end + p1 = "BOTTOM"..point; p2 = "TOP"..point; + elseif grgrowth == "BOTTOM" then + if direction == "VERTICAL" then point = "" end + p2 = "BOTTOM"..point; p1 = "TOP"..point + ygap = -ygap + elseif grgrowth == "RIGHT" then + if direction == "HORIZONTAL" then point = "" end + p1 = point.."LEFT"; p2 = point.."RIGHT" + xgap, ygap = ygap, xgap + elseif grgrowth == "LEFT" then + if direction == "HORIZONTAL" then point = "" end + p2 = point.."LEFT"; p1 = point.."RIGHT" + xgap, ygap = -ygap, xgap + end + return p1, prv_group, p2, xgap, ygap + end + + local i = 1 + while (i <= InjectorConfig.maxgroups) do + local f = Injector:CreateHeader(i) + + + group_headers[i] = f + if i == 1 then + f:SetPoint(InjectorConfig.anchorpoint, self.anchor, reverse(InjectorConfig.anchorpoint), 0, 0) +--~ f:SetPoint(InjectorConfig.anchorpoint, self.backdrop, InjectorConfig.anchorpoint, 0, 0) + f:SetAttribute("showParty", true) + f:SetAttribute("showSolo", InjectorConfig.showSolo) + f:SetAttribute("showPlayer", true) + else + f:SetPoint(arrangeHeaders(group_headers[i-1])) + end + f:SetScale(InjectorDB.scale) + f:Show() + i = i + 1 + end + + --pet module + if InjectorPet then + local pets = InjectorPet:Init() + if InjectorConfig.petFramesSeparation then + self.petanchor = self:CreateAnchor("pet_pos") + pets:SetPoint(InjectorConfig.anchorpoint, self.petanchor, reverse(InjectorConfig.anchorpoint), 0, 0) + else + -- damn pets + pets:SetPoint(arrangeHeaders(group_headers[1], true)) + end + end + + Injector:SetScript("OnUpdate",Injector.OnRangeUpdate) + Injector:Show() + + SLASH_INJECTOR1= "/injector" + SLASH_INJECTOR2= "/inj" + SlashCmdList["INJECTOR"] = Injector.SlashCmd + end +end + +-- Incoming heal functions for HealComm +function Injector.HealUpdated(self, event, casterGUID, spellID, healType, endTime, ...) + for i=1,select('#', ...) do + local targetGUID = select(i, ...) + local unit = guidMap[targetGUID] + if unit then Injector.UpdateIncomingHeals(unit, targetGUID) end + end +end +local HealTextStatus = { name = "IncHealText", priority = 15 } +function Injector.UpdateIncomingHeals(unit, guid) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + local heal = HealComm:GetHealAmount(guid, HealComm.InjectorHealType, GetTime()+InjectorConfig.incomingHealTimeframe) + self.incoming:SetValue( heal and self.hp:GetValue()+(heal/UnitHealthMax(unit)*100) or 0) + if InjectorConfig.incomingHealDisplayAmount then + if heal then + self.text2.jobs[HealTextStatus.name] = HealTextStatus + else + self.text2.jobs[HealTextStatus.name] = nil + end + Injector.UpdateStatus(self.text2, "text", heal and ("%.1fk"):format( heal / 1e3) ) + end + if InjectorConfig.IncomingHealStatus then + if heal then + Injector.UpdateAura(unit, InjectorConfig.IncomingHealStatus, true) + else + Injector.UpdateAura(unit, InjectorConfig.IncomingHealStatus, false) + end + end + end +end + +--Health Text string updates +function Injector.UpdateHealthText(self, h, hm) + if hm - h > 1000 then + self.text:SetText(("%.1fk"):format( (h-hm) / 1e3)) + else + self.text:SetText(self.name) + end +end + +local DeadStatus = { name = "DEAD", text = "DEAD", priority = 20} +local GhostStatus = { name = "GHOST", text = "GHOST", priority = 22} +local OfflineStatus = { name = "OFFLINE", text = "OFFLINE", priority = 30} +function Injector.UNIT_HEALTH(self, event, unit) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + local h,hm = UnitHealth(unit), UnitHealthMax(unit) + Injector.UpdateHealthText(self, h, hm) + self.hp:SetValue(h/hm*100) + + if event then -- quickhealth calls this function without event + if UnitIsDeadOrGhost(unit) then + self.hp.bg:Hide() + Injector.UpdateAura(unit, InjectorConfig.AggroStatus, false) + local opts = UnitIsGhost(unit) and GhostStatus or DeadStatus + self.text:SetText(self.name) + self.text2.jobs[opts.name] = opts + else + if not self.hp.bg:IsVisible() then + self.hp.bg:Show() + Injector.ScanAuras(unit) + Injector.UpdateHealthText(self, h, hm) + self.text2.jobs[DeadStatus.name] = nil + self.text2.jobs[GhostStatus.name] = nil + end + end + if not UnitIsConnected(unit) then + self.text2.jobs[OfflineStatus.name] = OfflineStatus + else + self.text2.jobs[OfflineStatus.name] = nil + end + Injector.UpdateStatus(self.text2, "text") + end + + end +end + +function Injector.UNIT_MANA(self, event, unit) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + if not self.mb:IsVisible() then return end + self.mb:SetValue(UnitPower(unit)/UnitPowerMax(unit)*100) + end +end + +local name, rank, icon, count, debuffType, duration, expirationTime, caster, isStealable +function Injector.ScanAuras(unit) + for auraname,opts in pairs(auras) do + name, rank, icon, count, debuffType, duration, expirationTime, caster = UnitAura(unit, auraname, nil, opts.type) + if name then + if opts.stackcolor then + opts.color = opts.stackcolor[count] + end + if opts.foreigncolor then + opts.isforeign = (caster ~= "player") + end + opts.start = expirationTime - duration + opts.duration = duration + opts.texture = opts.texture or icon + opts.stacks = count + Injector.UpdateAura(unit, opts, true) + else + Injector.UpdateAura(unit, opts, false) + end + end +end +function Injector.ScanDispels(unit) + if dtypes then + if UnitAura(unit, 1, "HARMFUL|RAID") then + for _,opts in pairs(dtypes) do + opts.gotone = false + end + for i = 1, 100 do + name, rank, icon, count, debuffType, duration, expirationTime, caster = UnitAura(unit, i, "HARMFUL|RAID") + if not name then break end + if dtypes[debuffType] then + local opts = dtypes[debuffType] + opts.gotone = true + opts.start = expirationTime - duration + opts.duration = duration + opts.stacks = count + opts.texture = icon + end + end + for _,opts in pairs(dtypes) do + Injector.UpdateAura(unit, opts, opts.gotone) + end + else + for _,opts in pairs(dtypes) do + Injector.UpdateAura(unit, opts, false) + end + end + end +end + +function Injector.UNIT_AURA(self, event, unit) + if not Roster[unit] then return end + Injector.ScanAuras(unit) + Injector.ScanDispels(unit) +end + +--~ local function VehicleHack(self, time) +--~ self.OnUpdateCounter = (self.OnUpdateCounter or 0) + time +--~ if self.OnUpdateCounter < 0.5 then return end +--~ self.OnUpdateCounter = 0 +--~ self.count = (self.count or 0) +1 +--~ +--~ if( self.count >= 6 or not UnitHasVehicleUI(self.unitOwner) ) then +--~ if self.unit ~= self.unitOwner then +--~ Roster[self.unitOwner] = Roster[self.unit] +--~ Injector:Colorize(nil, self.unitOwner) +--~ Roster[self.unit] = nil +--~ self.unit = self.unitOwner +--~ self.unitOwner = nil +--~ Injector:UNIT_HEALTH(nil,self.unit) +--~ self:SetScript("OnUpdate", nil) +--~ end +--~ Injector:UNIT_MANA(nil,self.unit) +--~ elseif( UnitIsConnected(self.unit) or UnitHealthMax(self.unit) > 0 ) then +--~ if self.unit ~= self.unitOwner then +--~ Injector:Colorize(nil, self.unitOwner) +--~ Roster[self.unit] = Roster[self.unitOwner] +--~ Roster[self.unitOwner] = nil +--~ Injector:UNIT_HEALTH(nil,self.unit) +--~ self:SetScript("OnUpdate", nil) +--~ end +--~ end +--~ end + +--~ local function TranslateModifiedUnit(unit) +--~ if unit == "player" then return "pet" end +--~ return gsub("raid1","(%w+)(%d+)","%1pet%2") +--~ end + +function Injector.UNIT_ENTERED_VEHICLE(self, event, unit) + if not Roster[unit] then return end + Injector:Colorize(nil, unit) +--~ for self in pairs(Roster[unit]) do +--~ self.unitOwner = unit +--~ self.unit = SecureButton_GetModifiedUnit(self) +--~ self:SetScript("OnUpdate",VehicleHack) +--~ end +end +function Injector.UNIT_EXITED_VEHICLE(self, event, unit) +--~ local modunit = TranslateModifiedUnit(unit) + if not Roster[unit] then return end + Injector:Colorize(nil, unit) +end +--Range check +Injector.OnRangeUpdate = function (self, time) + self.OnUpdateCounter = (self.OnUpdateCounter or 0) + time + if self.OnUpdateCounter < 0.5 then return end + self.OnUpdateCounter = 0 + + for unit, frames in pairs(Roster) do + for frame in pairs(frames) do +--~ local unit1 = frame.unit + if InjectorUnitInRange(unit) then + frame:SetAlpha(1) + OORUnits[unit] = nil + else + frame:SetAlpha(InjectorConfig.outOfRangeAlpha) + OORUnits[unit] = true + end + end + end +end + +--Aggro +function Injector.UNIT_THREAT_SITUATION_UPDATE(self, event, unit) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + local sit = UnitThreatSituation(unit) + if sit and sit > 1 then + Injector.UpdateAura(unit, InjectorConfig.AggroStatus, true) + else + Injector.UpdateAura(unit, InjectorConfig.AggroStatus, false) + end + end +end + +-- maintanks, resize +function Injector.UpdateMainTanks( self ) + if InjectorConfig.MainTankStatus then + for unit in pairs(Roster) do + if UnitExists(unit) and (GetPartyAssignment("MAINTANK", unit) or UnitGroupRolesAssigned(unit)) then + Injector.UpdateAura(unit, InjectorConfig.MainTankStatus, true) + else + Injector.UpdateAura(unit, InjectorConfig.MainTankStatus, false) + end + end + end +end +function Injector.RAID_ROSTER_UPDATE(self,event,arg1) + if not InCombatLockdown() then + if InjectorConfig.resize then + if GetNumRaidMembers() > InjectorConfig.resize.after then + for i = 1, InjectorConfig.maxgroups do + group_headers[i]:SetScale(InjectorConfig.resize.to) + end + else + for i = 1, InjectorConfig.maxgroups do + group_headers[i]:SetScale(InjectorDB.scale) + end + end + end + end + self:UpdateMainTanks() +end + +--raid icons +function Injector.RAID_TARGET_UPDATE(self, event) + for unit, frames in pairs(Roster) do + for self in pairs(frames) do + local index = GetRaidTargetIndex(unit) + local icon = self.icons.raidicon + if index then + SetRaidTargetIconTexture(icon.texture, index) + icon:Show() + else + icon:Hide() + end + end + end +end + +function Injector.PLAYER_TARGET_CHANGED(self, event) + local newTargetUnit = guidMap[UnitGUID("target")] + if newTargetUnit and Roster[newTargetUnit] then + Injector.UpdateAura(Injector.previousTarget, InjectorConfig.TargetStatus, false) + Injector.UpdateAura(newTargetUnit, InjectorConfig.TargetStatus, true) + Injector.previousTarget = newTargetUnit + else + Injector.UpdateAura(Injector.previousTarget, InjectorConfig.TargetStatus, false) + end +end + +-- readycheck +function Injector.READY_CHECK(self, event) + for unit in pairs(Roster) do + self:READY_CHECK_CONFIRM(event, unit) + end +end +function Injector.READY_CHECK_CONFIRM(self, event, unit) + local rci = InjectorConfig.ReadyCheck + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + local status = GetReadyCheckStatus(unit) + if not status or not rci.stackcolor[status] then return end + rci.color = rci.stackcolor[status] + Injector.UpdateAura(unit, rci, true) + end +end +function Injector.READY_CHECK_FINISHED(self, event) + for unit in pairs(Roster) do + Injector.UpdateAura(unit, InjectorConfig.ReadyCheck, false) + end +end + +--power type changed +function Injector.UNIT_DISPLAYPOWER(self, event, unit) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + local x,y,p1,p2 + if UnitPowerType(unit) == 0 then + self.mb:Show() + self.mb.bg:Show() + + self.hp:ClearAllPoints() + self.hp:SetPoint(InjectorConfig.mbst.p1,self,InjectorConfig.mbst.p1, -InjectorConfig.mbst.x, InjectorConfig.mbst.y) + self.hp:SetPoint(InjectorConfig.mbst.p3,self,InjectorConfig.mbst.p3, 0, 0) + self.hp.bg:ClearAllPoints() + self.hp.bg:SetAllPoints(self.hp) + self.text:SetPoint("CENTER",InjectorConfig.mbst.x/2,0) + else + + self.hp:ClearAllPoints() + self.hp:SetPoint("TOPRIGHT",self,"TOPRIGHT",0,0) + self.hp:SetPoint("BOTTOMLEFT",self,"BOTTOMLEFT",0,0) + self.hp.bg:ClearAllPoints() + self.hp.bg:SetAllPoints(self.hp) + self.text:SetPoint("CENTER",0,0) + + self.mb:Hide() + self.mb.bg:Hide() + end + end +end + +--applying UnitButton color +function Injector.Colorize(self, event, unit) + if not Roster[unit] then return end + for self in pairs(Roster[unit]) do + if UnitHasVehicleUI(unit) then + local color = colors["VEHICLE"] or { r = 0, g = 1, b = 0 } + self:SetColor(color.r,color.g,color.b) + else + local _,class = UnitClass(unit) + if class then + local color = colors[class] -- or { r = 1, g = 1, b = 0} + self:SetColor(color.r,color.g,color.b) + end + end + end +end + +--UnitButton initialization +local OnAttributeChanged = function(self, name, unit) + if name ~= "unit" then return end + + for unit, frames in pairs(Roster) do + if frames[self] and self:GetAttribute("unit") ~= unit then + frames[self] = nil + end + end + + if not unit then return end + + local name, realm = UnitName(unit) + self.name = utf8sub(name,1,InjectorConfig.cropNamesLen) + + self.guid = UnitGUID(unit) + self.unit = unit + Roster[unit] = Roster[unit] or {} + Roster[unit][self] = true + + guidMap[UnitGUID(unit)] = unit + for guid, gunit in pairs(guidMap) do + if not Roster[gunit] or guid ~= UnitGUID(gunit) then guidMap[guid] = nil end + end + + Injector:Colorize(nil, unit) + Injector.ScanAuras(unit) + Injector:UNIT_HEALTH("ONATTR", unit) + if not InjectorConfig.disableManaBar then + Injector:UNIT_DISPLAYPOWER(nil, unit) + Injector:UNIT_MANA(nil, unit) + end + + Injector:UNIT_THREAT_SITUATION_UPDATE(nil, unit) + if InjectorConfig.SetupIcons.raidicon then + Injector:RAID_TARGET_UPDATE() + end + Injector:UpdateMainTanks() + if InjectorConfig.useHealComm then Injector.UpdateIncomingHeals(unit, UnitGUID(unit)) end +end + +--building header, frame, anchor +function Injector.CreateHeader(self,group) + local frameName = "NR"..group + local xgap = InjectorConfig.unitGap + local ygap = InjectorConfig.unitGap + local unitgr = reverse(InjectorConfig.unitGrowth) + + local f = CreateFrame("Button",frameName, UIParent, "SecureGroupHeaderTemplate") + + f:SetAttribute("template", "SecureUnitButtonTemplate") + f:SetAttribute("templateType", "Button") + if unitgr == "RIGHT" then + xgap = -xgap + elseif unitgr == "TOP" then + ygap = -ygap + end + f:SetAttribute("point", unitgr) + f:SetAttribute("groupFilter", group) + f:SetAttribute("showRaid", true) + f:SetAttribute("xOffset", xgap) + f:SetAttribute("yOffset", ygap) + f.initialConfigFunction = Injector.CreateFrame + + return f +end +function Injector.CreateFrame(f) + local texture = InjectorConfig.texture + local font = InjectorConfig.font + local fontsize = InjectorConfig.fontsize + local manabar_width = InjectorConfig.manabarwidth + + f:SetAttribute("initial-width", InjectorConfig.width) + f:SetAttribute("initial-height", InjectorConfig.height) + f:SetAttribute("initial-scale", InjectorConfig.scale) + f:SetAttribute("toggleForVehicle", true) + + ClickCastFrames[f] = true + + f:SetAttribute("*type1", "target") + + local backdrop = { + bgFile = "Interface\\Addons\\Injector\\white", tile = true, tileSize = 0, + insets = {left = -2, right = -2, top = -2, bottom = -2}, + } + f:SetBackdrop(backdrop) + f:SetBackdropColor(0, 0, 0, 1) + + local hpi = CreateFrame("StatusBar", nil, f) + hpi:SetAllPoints(f) + hpi:SetOrientation(InjectorConfig.orientation) + hpi:SetStatusBarTexture("Interface\\Tooltips\\UI-Tooltip-Background") + hpi:SetStatusBarColor(0,0,0,0.3) + hpi:SetMinMaxValues(0,100) + hpi:SetValue(0) + f.incoming = hpi + + + local hp = CreateFrame("StatusBar", nil, f) + hp:SetAllPoints(f) + hp:SetOrientation(InjectorConfig.orientation) + hp:SetStatusBarTexture(texture) + + hp:SetStatusBarColor(1,1,1,1) + hp:SetMinMaxValues(0,100) + hp:SetValue(0) + + local hpbg = f:CreateTexture() + hpbg:SetAllPoints(hp) + hpbg:SetTexture(texture) + + hp.bg = hpbg + f.SetColor = function(self,r,g,b) + if not InjectorConfig.invertColor then + self.hp:SetStatusBarColor(0,0,0,0.8) + self.hp.bg:SetVertexColor(r,g,b,1) + self.text:SetTextColor(r,g,b) + else + self.hp:SetStatusBarColor(r,g,b,1) + self.hp.bg:SetVertexColor(r,g,b,0.2) + self.text:SetTextColor(r*0.75,g*0.75,b*0.75) + end + end + + + f.hp = hp + + --==< HEALTH BAR TEXT >==-- + local text = hp:CreateFontString(nil, "OVERLAY", "GameFontNormal") + text:SetPoint("CENTER",0,0) + text:SetJustifyH"CENTER" + text:SetFont(font, fontsize) + text:SetTextColor(1, 1, 1) + f.text = text + + --==< HEALTH BAR TEXT - SECOND LINE >==-- + local text2 = hp:CreateFontString(nil, "OVERLAY", "GameFontNormal") + text2:SetPoint("TOP",text,"BOTTOM",0,0) + text2:SetJustifyH"CENTER" + text2:SetFont(font, fontsize-3) + text2:SetTextColor(0.2, 1, 0.2) + text2.jobs = {} + f.text2 = text2 + + + --- mana bar + if not InjectorConfig.disableManaBar then + + local mb = CreateFrame("StatusBar",nil, f) + + if InjectorConfig.orientation == "VERTICAL" then + InjectorConfig.mbst = { + x = manabar_width, + y = 0, + p1 = "TOPRIGHT", + p2 = "BOTTOMRIGHT", + p3 = "BOTTOMLEFT", + } + else + InjectorConfig.mbst = { + x = 0, + y = manabar_width, + p1 = "BOTTOMLEFT", + p2 = "BOTTOMRIGHT", + p3 = "TOPRIGHT", + } + end + + mb:SetPoint(InjectorConfig.mbst.p1,f,InjectorConfig.mbst.p1,0,0) + mb:SetPoint(InjectorConfig.mbst.p3,f,InjectorConfig.mbst.p2, -InjectorConfig.mbst.x , InjectorConfig.mbst.y) +--~ mb:SetWidth(manabar_width) + mb:SetOrientation(InjectorConfig.orientation) + mb:SetStatusBarTexture(texture) + mb:SetStatusBarColor(0,0,0,0.7) + mb:SetMinMaxValues(0,100) + mb:SetValue(100) +--~ mb:SetFrameLevel(2) + local mbbg = f:CreateTexture() + mbbg:SetAllPoints(mb) + mbbg:SetTexture(texture) + mbbg:SetVertexColor(0.2, 0.45, 0.75) + + hp:ClearAllPoints() + hp:SetPoint("TOPRIGHT",f,"TOPRIGHT",-manabar_width,0) + hp:SetPoint("BOTTOMLEFT",f,"BOTTOMLEFT",0,0) + hp.bg:ClearAllPoints() + hp.bg:SetAllPoints(hp) + + + mb.bg = mbbg + mb.width = manabar_width + f.mb = mb + + + f.mb = mb + end + +--~ f:EnableMouse(true) -- taint + f:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight","ADD") + + if InjectorConfig.mouseoverTooltip and InjectorConfig.mouseoverTooltip ~= "disabled" then + if InjectorConfig.mouseoverTooltip == "always" then UnitAffectingCombat = function() return false end end + f:SetScript("OnEnter", function(self) + if UnitAffectingCombat("player") then return end + UnitFrame_OnEnter(self) + self:SetScript("OnUpdate", UnitFrame_OnUpdate) + end) + f:SetScript("OnLeave", function(self) + UnitFrame_OnLeave(self) + self:SetScript("OnUpdate", nil) + end) + end + + f:SetScript("OnAttributeChanged", OnAttributeChanged) + + f.indicators = {} + for name, opts in pairs(InjectorConfig.SetupIndicators) do + f.indicators[name] = Injector.CreateIndicator(f, name, opts) + f.indicators[name].jobs = {} + end + + f.icons = {} + for name, opts in pairs(InjectorConfig.SetupIcons) do + f.icons[name] = Injector.CreateIcon(f, name, opts) + if name == "raidicon" then f.icons[name].texture:SetTexture[[Interface\TargetingFrame\UI-RaidTargetingIcons]] end + f.icons[name].jobs = {} + end +end +function Injector.CreateAnchor(self, tbl) + local f = CreateFrame("Frame",nil,UIParent) + f:SetHeight(20) + f:SetWidth(20) + f.cols = cols + f.filter = filter + + f:RegisterForDrag("LeftButton") + f:EnableMouse(true) + f:SetMovable(true) + f:SetFrameStrata("HIGH") + f:SetFrameLevel(2) + if InjectorConfig.lockedOnStartUp then + f:Hide() + else + f:Show() + end + + local t = f:CreateTexture(nil,"BACKGROUND") + t:SetTexture("Interface\\Buttons\\UI-RadioButton") + t:SetTexCoord(0,0.25,0,1) + t:SetAllPoints(f) + + t = f:CreateTexture(nil,"BACKGROUND") + t:SetTexture("Interface\\Buttons\\UI-RadioButton") + t:SetTexCoord(0.25,0.49,0,1) + t:SetVertexColor(1, 0, 0) + t:SetAllPoints(f) + + f:SetScript("OnDragStart",function(self) self:StartMoving() end) + f:SetScript("OnDragStop",function(self) + self:StopMovingOrSizing(); + _,_, InjectorDB[tbl].point, InjectorDB[tbl].x, InjectorDB[tbl].y = self:GetPoint(1) + end) + + f.SetPos = function(self,point, x, y ) + InjectorDB[tbl].point = point + InjectorDB[tbl].x = x + InjectorDB[tbl].y = y + self:ClearAllPoints() + self:SetPoint(point, UIParent, point, x, y) + end + + f:SetPos(InjectorDB[tbl].point, InjectorDB[tbl].x, InjectorDB[tbl].y) + + return f +end + +function Injector.CreateIcon(f,name,opts) + local icon = CreateFrame("Frame", f:GetName()..name, f) + icon:SetPoint(opts.point, f, opts.point, opts.xOffset or 0, opts.yOffset or 0 ) + icon:SetWidth(opts.size) + icon:SetHeight(opts.size) + icon:SetAlpha(opts.alpha or 1) + icon:SetFrameLevel(10) + local texture = f:CreateTexture(f:GetName()..name,"ARTWORK")--CreateFrame("Frame", nil,f) + texture:SetAllPoints(icon) + texture:SetParent(icon) + texture:SetTexCoord(.07, .93, .07, .93) + + local cd = CreateFrame("Cooldown",nil,icon) + if not opts.omnicc then + cd.noCooldownCount = true -- for OmniCC + end + cd:SetReverse(true) + cd:SetAllPoints(icon) + + icon.texture = texture + icon.cd = cd + + if opts.stacktext then + local stacktext = icon:CreateFontString(nil, "OVERLAY") + if type(opts.stacktext) ~= "table" then opts.stacktext = {} end + local sfont = opts.stacktext.font or InjectorConfig.font + local ssize = opts.stacktext.size or InjectorConfig.fontsize - 2 + local sflags = opts.stacktext.flags or "OUTLINE" + stacktext:SetFont(sfont, ssize, sflags) + stacktext:SetWidth(icon:GetWidth()) + stacktext:SetJustifyH("RIGHT") + local color = opts.stacktext.color or {1,1,1} + stacktext:SetTextColor(unpack(color)) + stacktext:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT",0,0) + stacktext:SetPoint("BOTTOMLEFT", icon, "BOTTOMLEFT",0,0) + icon.stacktext = stacktext + end + + icon:Hide() + + return icon +end +--tonumber(string.sub(UnitGUID("target"),9,12),16) +function Injector.CreateIndicator(f, name, opts) + local ind = CreateFrame("Frame", f:GetName()..name, f) + if not opts.nobackdrop then + ind:SetBackdrop{ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, + insets = {left = -2, right = -2, top = -2, bottom = -2}, + } + ind:SetBackdropColor(0, 0, 0, 1) + end + ind:SetPoint(opts.point, f, opts.point, opts.xOffset or 0, opts.yOffset or 0 ) + if opts.size then + ind:SetWidth(opts.size) + ind:SetHeight(opts.size) + else + ind:SetWidth(opts.width) + ind:SetHeight(opts.height) + end + ind:SetFrameLevel(10) + + local it = ind:CreateTexture(nil,"ARTWORK") + it:SetAllPoints(ind) + it:SetTexture("Interface\\Addons\\Injector\\white") + ind.color = it + + local cd = CreateFrame("Cooldown",nil,ind) + if not opts.omnicc then + cd.noCooldownCount = true -- for OmniCC + end + cd:SetReverse(true) + cd:SetAllPoints(ind) + ind.cd = cd + + ind.Pulse = function(frame) + UIFrameScale(frame,{ + mode = "IN", + timeToScale = 0.2, + finishedFunc = function(frame) frame.shining = false; UIFrameScaleOut(frame, 0.8, 2, 1); end, + finishedArg1 = frame, + startScale = 1, + endScale = 2, + }) + frame.shining = true + end + +--~ if opts.stacktext then +--~ local stacktext = icon:CreateFontString(nil, "OVERLAY") +--~ stacktext:SetFont(InjectorConfig.font, opts.size) +--~ stacktext:SetAllPoints(ind) +--~ stacktext:SetJustifyH("CENTER") +--~ ind.stacktext = stacktext +--~ end + + ind:Hide() + + return ind +end + +function Injector.UpdateAura(unit, opts, status) + if not Roster[unit] then return end + for frame in pairs(Roster[unit]) do + if opts.indicator then + for _, iname in ipairs(opts.indicator) do + local self = frame.indicators[iname] + if self then + if opts.isMissing then status = not status end + if status then + local n = opts.name + if opts.fade then n = n.."Fade" end + self.jobs[n] = opts + else + self.jobs[opts.name] = nil + end + Injector.UpdateStatus(self, "indicator") + end + end + end + if opts.icon then + local self = frame.icons[opts.icon] + if self then + self.jobs[opts.name] = status and opts or nil + Injector.UpdateStatus(self, "icon") + end + end +--~ if opts.textind then +--~ local self = frame.text2 +--~ if self then +--~ self.jobs[opts.name] = status and opts or nil +--~ Injector.UpdateStatus(self, "textind") +--~ end +--~ end + end +end + +function Injector.UpdateStatus(self, statustype, arg1) + if next(self.jobs) then -- if not empty + local max + --if self.currentJob and self.jobs[self.currentJob] then max = self.currentJob end + local max_priority = 0--max and self.jobs[max].priority + for name, opts in pairs(self.jobs) do + if not opts.priority then opts.priority = 80 end + if max_priority < opts.priority then + max_priority = opts.priority + max = name + end + end + local job = self.jobs[max] + if statustype == "indicator" then + local color + if job.foreigncolor and job.isforeign then + color = job.foreigncolor + else + color = job.color or { 1,1,1,1 } + end + self.color:SetVertexColor(color[1],color[2],color[3],color[4] or 1) + self:SetBackdropColor(0,0,0,color[4] or 1) + if job.pulse and self.currentPriority and job.priority > self.currentPriority then + self:Pulse() + end + self.currentPriority = job.priority + end + if statustype == "icon" then + local texture = job.texture + self.texture:SetTexture(texture) + if self.stacktext and job.stacks then self.stacktext:SetText(job.stacks > 1 and job.stacks or "") end + end + if statustype == "text" then + local text = arg1 or job.text + if text then + self:SetText(text) + self:Show() + end + return + end + + self:Show() + if job.fade and not self.fading then + self:SetAlpha(1); + self.fading = true + + UIFrameFade(self, { + mode = "OUT", + timeToFade = job.fade, + finishedFunc = function(self, name) + --print("hiding") + self:Hide(); + self:SetAlpha(1); + self.jobs[name] = nil; + self.fading = false; + Injector.UpdateStatus(self, "indicator"); + end, + finishedArg1 = self, + finishedArg2 = max, + }); + end + + if job.showDuration and job.start then + self.cd:SetCooldown(job.start, job.duration) + self.cd:Show() + else + self.cd:Hide() + end + else + self.currentPriority = 0 + self:Hide() + end +end + +local ParseOpts = function(str) + local fields = {} + for opt,args in string.gmatch(str,"(%w*)%s*=%s*([%w%,%-%_%.%:%\\%']+)") do + fields[opt:lower()] = tonumber(args) or args + end + return fields +end +function Injector.SlashCmd(msg) + k,v = string.match(msg, "([%w%+%-%=]+) ?(.*)") + if not k or k == "help" then print([[Usage: + |cff00ff00/injector lock|r + |cff00ff00/injector unlock|r + |cff00ff00/injector reset|r + |cff00ff00/injector scale <0-2+>|r + |cff00ff00/injector setpos |r + |cff00ff00/injector load + |cff00ff00/injector charspec|r + |cff00ff00/injector toggle | show | hide + |cff00ff00/injector togglegroup <1-8>]] + )end + if k == "unlock" then + Injector.anchor:Show() + if InjectorPet and Injector.petanchor then Injector.petanchor:Show() end + end + if k == "lock" then + Injector.anchor:Hide() + if InjectorPet and Injector.petanchor then Injector.petanchor:Hide() end + end + if k == "reset" then + Injector.anchor:SetPos("CENTER", 0, 0) + if InjectorPet then + Injector.petanchor:ClearAllPoints() + Injector.petanchor:SetPoint("CENTER",UIParent,"CENTER",0,0) + end + end + if k == "scale" then + local s = tonumber(v) + if not s then + print(InjectorString.."Current scale = "..InjectorDB.scale) + return + end + InjectorDB.scale = s + for i = 1, InjectorConfig.maxgroups do + group_headers[i]:SetScale(s) + end + end + if k == "togglegroup" then + local group = tonumber(v) + if group then + local hdr = group_headers[group] + if hdr:IsVisible() then + hdr:Hide() + else + hdr:Show() + end + end + end + if k == "toggle" then + if group_headers[1]:IsVisible() then k = "hide" else k = "show" end + end + if k == "show" then + for i=1,InjectorConfig.maxgroups do + group_headers[i]:Show() + end + end + if k == "hide" then + for i=1,InjectorConfig.maxgroups do + group_headers[i]:Hide() + end + end + if k == "load" then + local add = InjectorConfig.LoadableDebuffs[v] + if v == "" then + print("Spell sets:") + for k,v in pairs(InjectorConfig.LoadableDebuffs) do + print(k) + end return + end + if add then + if loaded[v] then return end + add() + print(InjectorString..v.." loaded.") + loaded[v] = true + else + print(InjectorString..v.." doesn't exist") + end + end + if k == "setpos" then + local fields = ParseOpts(v) + if not next(fields) then print("Usage: /inj setpos point=center x=0 y=0") return end + Injector.anchor:SetPos(string.upper(fields['point'] or "CENTER"), fields['x'] or 0, fields['y'] or 0) + end + if k == "charspec" then + local user = UnitName("player").."@"..GetRealmName() + if InjectorDB_Global.charspec[user] then InjectorDB_Global.charspec[user] = nil + else InjectorDB_Global.charspec[user] = true + end + print (InjectorString..(InjectorDB_Global.charspec[user] and "Enabled" or "Disabled").." character specific options for this toon. Will take effect after ui reload",0.7,1,0.7) + end +end + +if InjectorConfig.enableTraceHeals then + +--~ local TraceHealEvents = {} +--~ for name, opts in pairs(traceheals) do +--~ TraceHealEvents[opts.type] = true +--~ end + +function Injector.COMBAT_LOG_EVENT_UNFILTERED( self, event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, amount, overhealing, absorbed, critical) + if (bit_band(srcFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) == COMBATLOG_OBJECT_AFFILIATION_MINE) then + local opts = traceheals[spellName] + if opts and eventType == opts.type then + if guidMap[dstGUID] then + Injector.UpdateAura(guidMap[dstGUID],opts,true) + end + end + end +end + +end + +--raid/pvp debuffs loading +local loader = CreateFrame("Frame") +loader:RegisterEvent("ZONE_CHANGED_NEW_AREA") +loader:RegisterEvent("PLAYER_ENTERING_WORLD") +local mapIDs = { + [609] = "Ruby Sanctum", + [605] = "Icecrown Citadel", + [544] = "Trial of the Crusader", + [530] = "Ulduar", + [536] = "Naxxramas", +} +loader:SetScript("OnEvent",function (self,event) + local instance + local _, instanceType = GetInstanceInfo() + if instanceType == "arena" or instanceType == "pvp" then + instance = "PvP" + else + instance = mapIDs[GetCurrentMapAreaID()] + end + if not instance then return end + local add = InjectorConfig.LoadableDebuffs[instance] + if add and not loaded[instance] then + add() + print (InjectorString..instance.." debuffs loaded.") + loaded[instance] = true + end +end) + + +if InjectorConfig.useCombatLogFiltering then + +local timer = CreateFrame("Frame") +timer.OnUpdateCounter = 0 +timer:SetScript("OnUpdate",function(self, time) + self.OnUpdateCounter = self.OnUpdateCounter + time + if self.OnUpdateCounter < 1 then return end + self.OnUpdateCounter = 0 + + for unit in pairs(buffer) do + Injector.ScanAuras(unit) + buffer[unit] = nil + end +end) + +function Injector.UNIT_AURA(self, event, unit) + if not Roster[unit] then return end + Injector.ScanDispels(unit) + if OORUnits[unit] and inCL[unit] +5 < GetTime() then + buffer[unit] = true + end +end + +local auraUpdateEvents = { + ["SPELL_AURA_REFRESH"] = true, + ["SPELL_AURA_APPLIED"] = true, + ["SPELL_AURA_APPLIED_DOSE"] = true, + ["SPELL_AURA_REMOVED"] = true, + ["SPELL_AURA_REMOVED_DOSE"] = true, +} +local cleuEvent = CreateFrame("Frame") +cleuEvent:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") +cleuEvent:SetScript("OnEvent", +function( self, event, timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellID, spellName, spellSchool, auraType, amount) + if auras[spellName] then + if auraUpdateEvents[eventType] then + local unit = guidMap[dstGUID] + if unit then + buffer[unit] = nil + inCL[unit] = GetTime() + Injector.ScanAuras(unit) + end + end + end +end) + +end \ No newline at end of file diff --git a/Injector.toc b/Injector.toc new file mode 100644 index 0000000..ebc4200 --- /dev/null +++ b/Injector.toc @@ -0,0 +1,12 @@ +## Interface: 30300 +## Title: Injector +## Notes: raid frames +## Author: d87 +## SavedVariablesPerCharacter: InjectorDB_Char +## SavedVariables: InjectorDB_Global +libs.xml +helpers.lua +config.lua +loadables.lua +InjectorPet.lua +Injector.lua \ No newline at end of file diff --git a/InjectorPet.lua b/InjectorPet.lua new file mode 100644 index 0000000..cfab31e --- /dev/null +++ b/InjectorPet.lua @@ -0,0 +1,171 @@ +if InjectorConfig.petFrames then + +local _, helpers = ... +local reverse = helpers.Reverse + +InjectorPet = CreateFrame("Frame",nil,UIParent) +InjectorPet.Roster = {} + +function InjectorPet.OnEvent(self, event, unit) -- only UNIT_HEALTH (=MAXHEALTH) + if not InjectorPet.Roster[unit] then return end + for self in pairs(InjectorPet.Roster[unit]) do + local h,hm = UnitHealth(unit), UnitHealthMax(unit) + Injector.UpdateHealthText(self, h, hm) + self.hp:SetValue(h/hm*100) + +--~ if UnitIsDeadOrGhost(unit) then +--~ self.hp.bg:Hide() +--~ self.text:SetText(self.name) +--~ else +--~ if not self.hp.bg:IsVisible() then +--~ self.hp.bg:Show() +--~ Injector.UpdateHealthText(self, h, hm) +--~ end +--~ end + end +end + +local PetOnAttributeChanged = function(self, name, unit) +--~ print("pet attr changed: "..(name or "nil")..' '..(unit or "nil")) + if name ~= "unit" then return end + + for unit, frames in pairs(InjectorPet.Roster) do + if frames[self] and self:GetAttribute("unit") ~= unit then + frames[self] = nil + end + end + + if not unit then return end + + local name, realm = UnitName(unit) + self.name = helpers.utf8sub(name,1,InjectorConfig.cropNamesLen) +--~ self.text:SetText(self.name) + local ownerunit = string.gsub(unit,"pet","") + if ownerunit == "" then ownerunit = "player" end + local _, class = UnitClass(ownerunit) -- owner class + + if class then + local color = RAID_CLASS_COLORS[class] -- or { r = 1, g = 1, b = 0} + self.hp.bg:SetVertexColor(color.r,color.g,color.b) + self.text:SetTextColor(color.r,color.g,color.b) + end + + self.guid = UnitGUID(unit) + self.unit = unit + InjectorPet.Roster[unit] = InjectorPet.Roster[unit] or {} + InjectorPet.Roster[unit][self] = true + +--~ for guid, unit in pairs(guid_roster) do +--~ if guid ~= UnitGUID(unit) then +--~ guid_roster[guid] = nil +--~ end +--~ end +--~ guid_roster[UnitGUID(unit)] = unit +--~ InjectorPet:UNIT_MAXHEALTH(nil, unit) + InjectorPet:OnEvent(nil, unit) +end + +function InjectorPet.ConfigFunc(f) + local texture = InjectorConfig.texture + local font = InjectorConfig.font + local fontsize = InjectorConfig.fontsize + + + f:SetAttribute("initial-width", InjectorConfig.width) + f:SetAttribute("initial-height", InjectorConfig.height) + f:SetAttribute("initial-scale", InjectorConfig.petScale or InjectorConfig.scale*0.7 ) + + ClickCastFrames[f] = true + + f:SetAttribute("*type1", "target") + + local backdrop = { +--~ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 0, + bgFile = "Interface\\Addons\\Injector\\white", tile = true, tileSize = 0, +--~ edgeFile = "Interface\\Addons\\Injector\\white", edgeSize = 2, + insets = {left = -2, right = -2, top = -2, bottom = -2}, + } + f:SetBackdrop(backdrop) + f:SetBackdropColor(0, 0, 0, 1) + + local hp = CreateFrame("StatusBar", nil, f) + hp:SetAllPoints(f) + hp:SetOrientation(InjectorConfig.orientation) + hp:SetStatusBarTexture(texture) + hp:SetStatusBarColor(0,0,0,0.8) + hp:SetMinMaxValues(0,100) + hp:SetValue(0) + + local hpbg = f:CreateTexture() + hpbg:SetAllPoints(hp) + hpbg:SetTexture(texture) + + + hp.bg = hpbg + f.hp = hp + + --==< HEALTH BAR TEXT >==-- + local text = hp:CreateFontString(nil, "OVERLAY", "GameFontNormal") + text:SetPoint("CENTER",0,0) + text:SetJustifyH"CENTER" + text:SetFont(font, fontsize) + text:SetTextColor(1, 1, 1) + f.text = text + +--~ f:EnableMouse(true) + f:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight","ADD") + + f:SetScript("OnEnter", function(self) + if UnitAffectingCombat("player") then return end + UnitFrame_OnEnter(self) + self:SetScript("OnUpdate", UnitFrame_OnUpdate) + end) + f:SetScript("OnLeave", function(self) + UnitFrame_OnLeave(self) + self:SetScript("OnUpdate", nil) + end) + f:SetScript("OnAttributeChanged", PetOnAttributeChanged) +end + +function InjectorPet.Init(self) + local xgap = InjectorConfig.unitGap + local ygap = InjectorConfig.unitGap + local unitgr = InjectorConfig.petFramesSeparation and reverse(InjectorConfig.unitGrowth) or InjectorConfig.unitGrowth + + local f = CreateFrame("Button","InjectorPet", UIParent, "SecureGroupPetHeaderTemplate") + + f:SetAttribute("showParty", true) + f:SetAttribute("showSolo", true) + f:SetAttribute("showPlayer", true) + + f:SetAttribute("template", "SecureUnitButtonTemplate") + f:SetAttribute("templateType", "Button") + + if unitgr == "RIGHT" then + xgap = -xgap + elseif unitgr == "TOP" then + ygap = -ygap + end + f:SetAttribute("point", unitgr) +--~ f:SetAttribute("groupFilter", group) + f:SetAttribute("showRaid", true) + f:SetAttribute("xOffset", xgap) + f:SetAttribute("yOffset", ygap) + + f:SetAttribute("unitsPerColumn",5) + f.initialConfigFunction = InjectorPet.ConfigFunc +--~ f:SetPoint("BOTTOMRIGHT",UIParent,"BOTTOMRIGHT",0,0) + + + InjectorPet:RegisterEvent("UNIT_HEALTH") + InjectorPet:RegisterEvent("UNIT_MAXHEALTH") + InjectorPet:SetScript("OnEvent",InjectorPet.OnEvent) + + InjectorPet.header = f + + f:Show() + + return f +end + +end \ No newline at end of file diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..bc69364 --- /dev/null +++ b/config.lua @@ -0,0 +1,237 @@ +local _, helpers = ... +local _, playerClass = UnitClass("player") +local isHealer = (playerClass == "PRIEST" or playerClass == "PALADIN" or playerClass == "SHAMAN" or playerClass == "DRUID") +local A = helpers.AddAura +local DT = helpers.AddDispellType +local Trace = helpers.AddTrace +InjectorConfig = {} + +-- size +InjectorConfig.width = 50 +InjectorConfig.height = 50 +InjectorConfig.scale = 1 + +-- frame config +InjectorConfig.texture = [[Interface\AddOns\Injector\gradient]] +InjectorConfig.font = [[Interface\AddOns\Injector\ClearFont.ttf]] +InjectorConfig.fontsize = 12 + +InjectorConfig.cropNamesLen = 7 -- maximum amount of characters in unit name +InjectorConfig.manabarwidth = 6 +InjectorConfig.orientation = "VERTICAL" -- HORIZONTAL / VERTICAL +InjectorConfig.outOfRangeAlpha = 0.4 +InjectorConfig.disableManaBar = false +InjectorConfig.invertColor = false -- if true hp lost becomes dark, current hp becomes bright +InjectorConfig.incomingHealTimeframe = 1.5 -- incoming in next 1.5 seconds heals are displayed, including hot ticks +InjectorConfig.incomingHealDisplayAmount = true -- on second line +InjectorConfig.incomingHealIgnoreHots = true +InjectorConfig.raidIcons = true +InjectorConfig.enableTraceHeals = true +InjectorConfig.mouseoverTooltip = "outofcombat" -- always / outofcombat / disabled + +-- layout +InjectorConfig.maxgroups = 8 +InjectorConfig.showSolo = true +InjectorConfig.unitGap = 10 -- gap between units +InjectorConfig.groupGap = 10 +InjectorConfig.unitGrowth = "RIGHT" -- direction for adding new players in group. LEFT / RIGHT / TOP / BOTTOM +InjectorConfig.groupGrowth = "TOP" -- new groups direction. LEFT / RIGHT / TOP / BOTTOM +InjectorConfig.resize = { after = 27, to = 0.8 } -- = if number of players in raid exeeds 27 then resize to 0.8. "InjectorConfig.resize = nil" disables it +InjectorConfig.anchorpoint = "BOTTOMLEFT" -- anchor position relative to 1st unit of 1st group. if you want to grow frames to TOP and RIGHT it better be BOTTOMLEFT. +InjectorConfig.lockedOnStartUp = true +InjectorConfig.disableBlizzardParty = true + +-- pets +-- petframes suck in this addon, i know. It's more like outdated plugin. +InjectorConfig.petFrames = false +InjectorConfig.petScale = 1 +InjectorConfig.petFramesSeparation = false + + +-- bells and whistles +InjectorConfig.useCombatLogFiltering = true +-- useCombatLogFiltering provides a huge perfomance boost over default behavior, which would be to listen only to UNIT_AURA event. +-- UNIT_AURA doesn't tell what exactly changed and every time addon had to scan current buffs/debuffs, +-- in raid combat unit_aura sometimes fired up to 8 times per second for each member with all the stacking trinkets and procs. +-- useCombatLogFiltering option moves this process mainly to combat log, where we can see what spell was updated. +-- Only if it's one of OUR spells from indicators it will update buff data for this unit. +-- The drawback is that it only works in combat log range, but it's big enough, and there's a fallback on throttled unit_aura (updates every 5s) for out of range units. +-- On lich king there was an issue, and maybe it's still present, that necrotic plague removal event didn't appear in combat log +-- and that caused glitches with boss debuff indicator. But that's a rare blizzard side bug. +-- Dispel idicators still work from unit_aura, so you'll see plague regardless as disease if you can dispel it. Necrotic plague removed from default loadables.lua setup. + +-- libs +InjectorConfig.useHealComm = true -- incoming heal library +InjectorConfig.useQuickHealth = isHealer -- combat log event faster than UNIT_HEALTH event. And that's what this lib does, allows you to see health updates sooner. + +InjectorConfig.SetupIndicators = { + ["topleft"] = { point = "TOPLEFT", size = 5, }, + ["topleft2"] = { point = "TOPLEFT", size = 5, xOffset = 7}, + ["topleft3"] = { point = "TOPLEFT", size = 5, yOffset = -7}, + ["topright"] = { point = "TOPRIGHT", size = 7 }, + ["bottomright"] = { point = "BOTTOMRIGHT", size = 8, }, + ["bottomleft"] = { point = "BOTTOMLEFT", size = 4, }, + ["bottom"] = { point = "BOTTOM", size = 7, }, + ["top"] = { point = "TOP", size = 10, }, + ["left"] = { point = "LEFT", size = 10, }, + + ["border_right"] = { point = "RIGHT", width = 2, height = InjectorConfig.height+8, xOffset = 4 , nobackdrop = true}, + ["border_left"] = { point = "LEFT", width = 2, height = InjectorConfig.height+8, xOffset = -4 , nobackdrop = true}, + ["border_top"] = { point = "TOP", width = InjectorConfig.width+4, height = 2, yOffset = 4 , nobackdrop = true}, + ["border_bottom"] = { point = "BOTTOM", width = InjectorConfig.width+4, height = 2, yOffset = -4 , nobackdrop = true }, +} +-- so border actually is built from 4 indicators, you can use them separately +local BORDER = { "border_left", "border_right", "border_top", "border_bottom" } -- shortcut, e.g. indicator = BORDER + +InjectorConfig.SetupIcons = { + ["raidicon"] = { point = "BOTTOMLEFT", size = 24, xOffset = -9, yOffset = -9, alpha = 0.6 }, --special icon for raid targets + ["center"] = { point = "CENTER", size = 24, alpha = 0.6, omnicc = false, stacktext = true }, +} +--customizing stack label: stacktext = { font = [[Interface\AddOns\Injector\ClearFont.ttf]], size = 10, flags = "OUTLINE", color = {1,0,0} }, + +--InjectorConfig.TargetStatus = { name = "Target", type = "HELPFUL", indicator = BORDER, color = {1,0.7,0.7}, priority = 65 } +InjectorConfig.IncomingHealStatus = nil --{ name = "IncomingHeal", type = "HELPFUL", indicator = { "bottomleft" }, color = { 0, 1, 0}, priority = 60 } +InjectorConfig.AggroStatus = { name = "Aggro", type = "HARMFUL", indicator = { "bottomleft" }, color = { 0.7, 0, 0} } -- InjectorConfig.AggroStatus = nil will disable aggro monitoring at all +InjectorConfig.ReadyCheck = { name = "Readycheck", type = "HELPFUL", priority = 90, indicator = { "top" }, stackcolor = { + ['ready'] = { 0, 1, 0}, + ['notready'] = { 1, 0, 0}, + ['waiting'] = { 1, 1, 0}, + }} +InjectorConfig.MainTankStatus = { name = "MainTank", type = "HELPFUL", priority = 60, indicator = BORDER, color = {0.6,0.6,0.6} } + + +InjectorConfig.Colors = { + --["PRIEST"] = { r = 1, g = 1, b = 1 }, + ["VEHICLE"] = { r = 1, g = 0.5, b = 0.5 }, +} + +--[[ +Spell parameters +================ + id - spell id to query localized spell name. + name - spell name + type - HELPFUL/HARMFUL for buffs/debuffs + priority - if multiple spells assigned to same indicator, the one with highest priority will be displayed. Default is 80. + showDuration - enables cooldownlike duration circle + isMine - only your spells +]] + +if playerClass == "PRIEST" then + -- long buffs + A{ id = 1243, type = "HELPFUL", indicator = { "topleft" }, pulse = true, color = { 1, 1, 1} } --Power Word: Fortitude + A{ id = 21562, type = "HELPFUL", indicator = { "topleft" }, color = { 0.8, 1, 0.8} } --Prayer of Fortitude + A{ id = 14752, type = "HELPFUL", indicator = { "topleft2" }, color = { .6 , .6, 1} } --Divine Spirit + A{ id = 27681, type = "HELPFUL", indicator = { "topleft2" }, color = { .6 , .6, 1} } --Prayer of Spirit + A{ id = 976, type = "HELPFUL", indicator = { "topleft3" }, color = { 102/255 , 0, 187/255 } } --Shadow Protection + A{ id = 27683, type = "HELPFUL", indicator = { "topleft3" }, color = { 102/255 , 0, 187/255 } } --Prayer of Shadow Protection + + --A{ id = 1706, type = "HELPFUL", indicator = { "topright" }, color = { 1, 1, 1}, priority = 60, showDuration = true } --Levitate + --A{ id = 552, type = "HELPFUL", indicator = { "topleft3" }, priority = 82, color = { 0, 1, 0 } } --Abolish Disease + + A{ id = 139, type = "HELPFUL", indicator = { "bottomright" }, pulse = true, color = { 0, 1, 0}, showDuration = true, isMine = true } --Renew + A{ id = 17, type = "HELPFUL", indicator = { "top" }, color = { 1, 1, 0}, showDuration = true } --Power Word: Shield + A{ id = 6788, type = "HARMFUL", indicator = { "top" }, color = { 0.6, 0, 0}, showDuration = true, priority = 40 } --Weakened Soul + A{ id = 33076, type = "HELPFUL", indicator = { "topright" }, stackcolor = { + [1] = { 0.4, 0, 0}, + [2] = { 0.7, 0, 0}, + [3] = { 1, 0, 0}, + [4] = { 1, 0.3, 0.3}, + [5] = { 1, 0.6, 0.6}, + [6] = { 1, 0.9, 0.9}, -- Tier7 set bonus + }} --Prayer of Mending + + Trace{id = 34861, type = "HEAL", indicator = { "topright" }, color = { 1, 1, 0}, fade = 0.7, priority = 96 } -- Circle of Healing + Trace{id = 33076, type = "HEAL", indicator = { "topright" }, color = { 1, 0.6, 0.6}, fade = 1.5, priority = 97 } -- PoM Trace + + --InjectorConfig.UnitInRangeFunc = function(unit) return (IsSpellInRange(GetSpellInfo(2061),unit) == 1) end + --// Use Flash Heal for range check. Usual UnitInRange is about 38yd, not 41, tho it's probably good to have that margin. Disabled by default. + + DT("Magic", { indicator = { "bottom" }, color = { 0.2, 0.6, 1}, priority = 81 }) + DT("Disease", { indicator = { "bottom" }, color = { 0.6, 0.4, 0} }) +end + +if playerClass == "WARLOCK" then + A{ id = 20707, type = "HELPFUL", indicator = { "topleft" }, color = { 180/255, 0, 1 }, priority = 81 } --Soulstone Resurrection +--~ A{ id = 6307, type = "HELPFUL", indicator = { "topleft" }, color = { 1, 0, 0 }, priority = 81 } --Blood Pact +--~ A{ id = 54424, type = "HELPFUL", indicator = { "topleft" }, color = { .6 , .6, 1 } } --Fel Intelligence +end +if playerClass == "PALADIN" then + --A{ id = 20217, type = "HELPFUL", indicator = { "topleft" }, color = { .6 , .3, 1} } --Blessing of Kings + --A{ id = 25898, type = "HELPFUL", indicator = { "topleft" }, color = { .6 , .3, 1} } --Greater Blessing of Kings + + --A{ id = 19740, type = "HELPFUL", indicator = { "topleft2" }, color = { 1 , 0.5, 0.3} } --Blessing of Might + --A{ id = 25782, type = "HELPFUL", indicator = { "topleft2" }, color = { 1 , 0.5, 0.3} } --Greater Blessing of Might + + --A{ id = 19742, type = "HELPFUL", indicator = { "topleft3" }, color = { 0.4, 1, 0.4} } --Blessing of Wisdom + --A{ id = 25894, type = "HELPFUL", indicator = { "topleft3" }, color = { 0.4, 1, 0.4} } --Greater Blessing of Wisdom + A{ id = 53563, type = "HELPFUL", indicator = { "top" }, showDuration = true, + isMine = true, + color = { 0,1,0 }, + --foreigncolor = { 0.96/2, 0.55/2, 0.73/2 }, + } + + Trace{id = 54968, type = "HEAL", indicator = { "topright" }, color = { 1, 1, 0}, fade = 0.7, priority = 96 } -- Glyph of Holy Light + + --InjectorConfig.UnitInRangeFunc = function(unit) return (IsSpellInRange(GetSpellInfo(635),unit) == 1) end + --// Use Holy Light for range check. Usual UnitInRange is about 38yd, not 41, tho it's probably good to have that margin. Disabled by default. + + DT("Magic", { indicator = { "bottom" }, color = { 0.2, 0.6, 1} }) + DT("Disease", { indicator = { "bottom" }, color = { 0.6, 0.4, 0} }) + DT("Poison", { indicator = { "bottom" }, color = { 0, 0.6, 0} }) +end +if playerClass == "SHAMAN" then + A{ id = 61295, type = "HELPFUL", indicator = { "bottomright" }, showDuration = true, isMine = true, color = { 0.2 , 0.2, 1} } --Riptide + A{ id = 974, type = "HELPFUL", indicator = { "top" }, showDuration = true, + --isMine = true, + stackcolor = { + [1] = { 0,.4, 0}, + [2] = { 0,.5, 0}, + [3] = { 0,.6, 0}, + [4] = { 0,.7, 0}, + [5] = { 0,.8, 0}, + [6] = { 0, 0.9, 0}, + [7] = {.1, 1, .1}, + [8] = {.2, 1, .2}, + }, + foreigncolor = {0,0,.5}, } --Earth Shield + + Trace{id = 1064, type = "HEAL", indicator = { "topright" }, color = { 1, 1, 0}, fade = 0.7, priority = 96 } -- Chain Heal + --Trace{id = 51558, type = "HEAL", indicator = { "topright" }, color = { 1, 0.6, 0.6 }, fade = 0.7, priority = 95 } -- Ancestral Awakening + + --InjectorConfig.UnitInRangeFunc = function(unit) return (IsSpellInRange(GetSpellInfo(331),unit) == 1) end + --// Use Healing Wave for range check. Usual UnitInRange is about 38yd, not 41, tho it's probably good to have that margin. Disabled by default. + + DT("Disease", { indicator = { "bottom" }, color = { 0.6, 0.4, 0} }) + DT("Poison", { indicator = { "bottom" }, color = { 0, 0.6, 0} }) + DT("Curse", { indicator = { "bottom" }, color = { 0.6, 0, 1} }) +end +if playerClass == "DRUID" then + A{ id = 1126, type = "HELPFUL", indicator = { "topleft" }, color = { 235/255 , 145/255, 199/255} } --Mark of the Wild + A{ id = 21849, type = "HELPFUL", indicator = { "topleft" }, color = { 235/255 , 145/255, 199/255} } --Gift of the Wild + A{ id = 467, type = "HELPFUL", indicator = { "topleft3" }, color = { 150/255, 100/255, 0 } } --Thorns + + A{ id = 774, type = "HELPFUL", indicator = { "bottomright" }, pulse = true, color = { 1, 0.2, 1}, showDuration = true, isMine = true } --Rejuvenation + A{ id = 8936, type = "HELPFUL", indicator = { "topright" }, priority = 82, color = { 198/255, 233/255, 80/255}, showDuration = true, isMine = true } --Regrowth + A{ id = 33763, type = "HELPFUL", indicator = { "top" }, showDuration = true, isMine = true, stackcolor = { + [1] = { 0, 0.8, 0}, + [2] = { 0.2, 1, 0.2}, + [3] = { 0.5, 1, 0.5}, + }} --Lifebloom + A{ id = 48438, type = "HELPFUL", indicator = { "topright" }, color = { 0.4, 1, 0.4}, showDuration = true, isMine = true } --Wild Growth + --A{ id = 2893, type = "HELPFUL", indicator = { "topleft3" }, priority = 82, color = { 0, 1, 0 } } --Abolish Poison + + --InjectorConfig.UnitInRangeFunc = function(unit) return (IsSpellInRange(GetSpellInfo(774),unit) == 1) end + --// Use Rejuvenation for range check. Usual UnitInRange is about 38yd, not 41, tho it's probably good to have that margin. Disabled by default. + + DT("Poison", { indicator = { "bottom" }, color = { 0, 0.6, 0} }) + DT("Curse", { indicator = { "bottom" }, color = { 0.6, 0, 1} }) +end +if playerClass == "MAGE" then + A{ id = 1459, type = "HELPFUL", indicator = { "topleft" }, color = { .4 , .4, 1} } --Arcane Intellect + A{ id = 23028, type = "HELPFUL", indicator = { "topleft" }, color = { .4 , .4, 1} } --Arcane Brilliance + A{ id = 61316, type = "HELPFUL", indicator = { "topleft" }, color = { .4 , .4, 1} } --Dalaran Brilliance + A{ id = 61024, type = "HELPFUL", indicator = { "topleft" }, color = { .4 , .4, 1} } --Dalaran Intellect + A{ id = 54648, type = "HELPFUL", indicator = { "topleft2" }, color = { 180/255, 0, 1 }, isMine = true } --Focus Magic + + DT("Curse", { indicator = { "bottom" }, color = { 0.6, 0, 1} }) +end \ No newline at end of file diff --git a/gradient.tga b/gradient.tga new file mode 100644 index 0000000..0cc4e88 Binary files /dev/null and b/gradient.tga differ diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..468b1c1 --- /dev/null +++ b/helpers.lua @@ -0,0 +1,223 @@ +local _, helpers = ... + +helpers.AddDispellType = function(dtype, data) + if not InjectorConfig.DebuffTypes then InjectorConfig.DebuffTypes = {} end + if type(data.indicator) == "string" then data.indicator = { data.indicator } end + if type(data.icon) == "table" then data.icon = data.icon[1] end + data.name = dtype + InjectorConfig.DebuffTypes[dtype] = data +end +helpers.AddAura = function (data) + if data.id then data.name = GetSpellInfo(data.id) end + if type(data.indicator) == "string" then data.indicator = { data.indicator } end + if type(data.icon) == "table" then data.icon = data.icon[1] end + if data.isMine then data.type = data.type.."|PLAYER" end + if data.debuffType then DT(data.debuffType, data) end + if not InjectorConfig.IndicatorAuras then InjectorConfig.IndicatorAuras = {} end + if data.prototype then setmetatable(data, { __index = function(t,k) return data.prototype[k] end }) end + InjectorConfig.IndicatorAuras[data.name] = data +--~ table.insert(InjectorConfig.IndicatorAuras, data) +end +helpers.AddTrace = function(data) + if not InjectorConfig.enableTraceHeals then return end + if data.id then data.name = GetSpellInfo(data.id) end + if type(data.indicator) == "string" then data.indicator = { data.indicator } end + --if type(data.type) == "string" then data.type = { data.type } end + data.type = "SPELL_"..data.type + if not InjectorConfig.TraceHeals then InjectorConfig.TraceHeals = {} end + if not data.name then print("id or name required") return end + InjectorConfig.TraceHeals[data.name] = data +end + + + + + + +function helpers.utf8sub(str, start, numChars) + local currentIndex = start + while numChars > 0 and currentIndex <= #str do + local char = string.byte(str, currentIndex) + if char >= 240 then + currentIndex = currentIndex + 4 + elseif char >= 225 then + currentIndex = currentIndex + 3 + elseif char >= 192 then + currentIndex = currentIndex + 2 + else + currentIndex = currentIndex + 1 + end + numChars = numChars - 1 + end + return str:sub(start, currentIndex - 1) +end + +function helpers.DisableBlizzParty(self) + for i=1,4 do + local party = "PartyMemberFrame"..i + local frame = _G[party] + + frame:UnregisterAllEvents() + frame.Show = function()end + frame:Hide() + _G[party..'HealthBar']:UnregisterAllEvents() + _G[party..'ManaBar']:UnregisterAllEvents() + end +end + +function helpers.Reverse(p1) + local p2 = "" + local dir + if string.find(p1,"CENTER") then return "CENTER" end + if string.find(p1,"TOP") then p2 = p2.."BOTTOM" end + if string.find(p1,"BOTTOM") then p2 = p2.."TOP" end + if string.find(p1,"LEFT") then p2 = p2.."RIGHT" end + if string.find(p1,"RIGHT") then p2 = p2.."LEFT" end + if p2 == "RIGHT" or p2 == "LEFT" then + dir = "HORIZONTAL" + elseif p2 == "TOP" or p2 == "BOTTOM" then + dir = "VERTICAL" + end + return p2, dir +end + + + + + + + + + +-- UIFrameFade clone from defauilt UI + +local SCALEFRAMES = {} +local frameScaleManager = CreateFrame("FRAME"); +-- Function that actually performs the scale change +--[[ +Fading frame attribute listing +============================================================ +frame.timeToScale [Num] Time it takes to scale the frame in or out +frame.mode ["IN", "OUT"] Scale mode +frame.finishedFunc [func()] Function that is called when scaling is finished +frame.finishedArg1 [ANYTHING] Argument to the finishedFunc +frame.finishedArg2 [ANYTHING] Argument to the finishedFunc +frame.finishedArg3 [ANYTHING] Argument to the finishedFunc +frame.finishedArg4 [ANYTHING] Argument to the finishedFunc +frame.scaleHoldTime [Num] Time to hold the scaled state + ]] + +local function UIFrameScaleRemoveFrame(frame) + tDeleteItem(SCALEFRAMES, frame); +end + +local function UIFrameScale_OnUpdate(self, elapsed) + local index = 1; + local frame, scaleInfo; + while SCALEFRAMES[index] do + frame = SCALEFRAMES[index]; + scaleInfo = SCALEFRAMES[index].scaleInfo; + -- Reset the timer if there isn't one, this is just an internal counter + if ( not scaleInfo.scaleTimer ) then + scaleInfo.scaleTimer = 0; + end + scaleInfo.scaleTimer = scaleInfo.scaleTimer + elapsed; + + -- If the scaleTimer is less then the desired scale time then set the scale otherwise hold the scale state, call the finished function, or just finish the scale + if ( scaleInfo.scaleTimer < scaleInfo.timeToScale ) then + if ( scaleInfo.mode == "IN" ) then + frame:SetScale((scaleInfo.scaleTimer / scaleInfo.timeToScale) * (scaleInfo.endScale - scaleInfo.startScale) + scaleInfo.startScale); + elseif ( scaleInfo.mode == "OUT" ) then + frame:SetScale(((scaleInfo.timeToScale - scaleInfo.scaleTimer) / scaleInfo.timeToScale) * (scaleInfo.startScale - scaleInfo.endScale) + scaleInfo.endScale); + end + else + frame:SetScale(scaleInfo.endScale); + -- If there is a scaleHoldTime then wait until its passed to continue on + if ( scaleInfo.scaleHoldTime and scaleInfo.scaleHoldTime > 0 ) then + scaleInfo.scaleHoldTime = scaleInfo.scaleHoldTime - elapsed; + else + -- Complete the scale and call the finished function if there is one + UIFrameScaleRemoveFrame(frame); + if ( scaleInfo.finishedFunc ) then + scaleInfo.finishedFunc(scaleInfo.finishedArg1, scaleInfo.finishedArg2, scaleInfo.finishedArg3, scaleInfo.finishedArg4); + scaleInfo.finishedFunc = nil; + end + end + end + + index = index + 1; + end + + if ( #SCALEFRAMES == 0 ) then + self:SetScript("OnUpdate", nil); + end +end + + +-- Generic scale function +local function UIFrameScale(frame, scaleInfo) + if (not frame) then + return; + end + if ( not scaleInfo.mode ) then + scaleInfo.mode = "IN"; + end + local scale; + if ( scaleInfo.mode == "IN" ) then + if ( not scaleInfo.startScale ) then + scaleInfo.startScale = 0.01; + end + if ( not scaleInfo.endScale ) then + scaleInfo.endScale = 1.0; + end + scale = 0; + elseif ( scaleInfo.mode == "OUT" ) then + if ( not scaleInfo.startScale ) then + scaleInfo.startScale = 1.0; + end + if ( not scaleInfo.endScale ) then + scaleInfo.endScale = 0.01; + end + scale = 1.0; + end + frame:SetScale(scaleInfo.startScale); + + frame.scaleInfo = scaleInfo; + frame:Show(); + + local index = 1; + while SCALEFRAMES[index] do + -- If frame is already set to scale then return + if ( SCALEFRAMES[index] == frame ) then + return; + end + index = index + 1; + end + tinsert(SCALEFRAMES, frame); + frameScaleManager:SetScript("OnUpdate", UIFrameScale_OnUpdate); +end + +-- Convenience function to do a simple scale in +local function UIFrameScaleIn(frame, timeToScale, startScale, endScale) + local scaleInfo = {}; + scaleInfo.mode = "IN"; + scaleInfo.timeToScale = timeToScale; + scaleInfo.startScale = startScale; + scaleInfo.endScale = endScale; + UIFrameScale(frame, scaleInfo); +end + +-- Convenience function to do a simple scale out +local function UIFrameScaleOut(frame, timeToScale, startScale, endScale) + local scaleInfo = {}; + scaleInfo.mode = "OUT"; + scaleInfo.timeToScale = timeToScale; + scaleInfo.startScale = startScale; + scaleInfo.endScale = endScale; + UIFrameScale(frame, scaleInfo); +end + + +helpers.UIFrameScale = UIFrameScale +helpers.UIFrameScaleOut = UIFrameScaleOut +helpers.UIFrameScaleIn = UIFrameScaleIn \ No newline at end of file diff --git a/libs.xml b/libs.xml new file mode 100644 index 0000000..f05d6f1 --- /dev/null +++ b/libs.xml @@ -0,0 +1,9 @@ + + +