--[[ ############################################################################## _____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_ # ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__ # __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____ # ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____ # ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____ # _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____ # __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ # _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_# ___\///////////___________\///___________\/////////_____\///////////_# ############################################################################## S U P E R - V I L L A I N - U I By: Munglunch # ############################################################################## ########################################################## LOCALIZED LUA FUNCTIONS ########################################################## ]]-- --[[ GLOBALS ]]-- local _G = _G; local unpack = _G.unpack; local select = _G.select; local assert = _G.assert; local type = _G.type; local error = _G.error; local pcall = _G.pcall; local print = _G.print; local ipairs = _G.ipairs; local pairs = _G.pairs; local next = _G.next; local rawset = _G.rawset; local rawget = _G.rawget; local tostring = _G.tostring; local tonumber = _G.tonumber; local table = _G.table; local string = _G.string; local math = _G.math; --[[ STRING METHODS ]]-- local match, format = string.match, string.format; local gmatch, gsub = string.gmatch, string.gsub; --[[ MATH METHODS ]]-- local floor, modf = math.floor, math.modf; local iLevelFilter = ITEM_LEVEL:gsub( "%%d", "(%%d+)" ) --[[ ########################################################## GET ADDON DATA ########################################################## ]]-- local SV = select(2, ...) local L = SV.L --[[ ########################################################## MISC UTILITY FUNCTIONS ########################################################## ]]-- local RefClassRoles, RefUnitRoles; local PlayerClass = select(2,UnitClass("player")); if(PlayerClass == "PRIEST") then RefClassRoles = {"C", "C", "C"} RefUnitRoles = {"HEALER", "HEALER", "DAMAGER"} elseif(PlayerClass == "WARLOCK") then RefClassRoles = {"C", "C", "C"} RefUnitRoles = {"DAMAGER", "DAMAGER", "DAMAGER"} elseif(PlayerClass == "WARRIOR") then RefClassRoles = {"M", "M", "T"} RefUnitRoles = {"DAMAGER", "DAMAGER", "TANK"} elseif(PlayerClass == "HUNTER") then RefClassRoles = {"M", "M", "M"} RefUnitRoles = {"DAMAGER", "DAMAGER", "DAMAGER"} elseif(PlayerClass == "ROGUE") then RefClassRoles = {"M", "M", "M"} RefUnitRoles = {"DAMAGER", "DAMAGER", "DAMAGER"} elseif(PlayerClass == "MAGE") then RefClassRoles = {"C", "C", "C"} RefUnitRoles = {"DAMAGER", "DAMAGER", "DAMAGER"} elseif(PlayerClass == "DEATHKNIGHT") then RefClassRoles = {"T", "M", "M"} RefUnitRoles = {"TANK", "DAMAGER", "DAMAGER"} elseif(PlayerClass == "DRUID") then RefClassRoles = {"C", "M", "T", "C"} RefUnitRoles = {"DAMAGER", "DAMAGER", "TANK", "HEALER"} elseif(PlayerClass == "SHAMAN") then RefClassRoles = {"C", "M", "C"} RefUnitRoles = {"DAMAGER", "DAMAGER", "HEALER"} elseif(PlayerClass == "MONK") then RefClassRoles = {"T", "C", "M"} RefUnitRoles = {"TANK", "HEALER", "DAMAGER"} elseif(PlayerClass == "PALADIN") then RefClassRoles = {"C", "T", "M"} RefUnitRoles = {"HEALER", "TANK", "DAMAGER"} end function SV:PlayerInfoUpdate() local spec = GetSpecialization() local role, unitRole; if spec then if(self.CurrentSpec == spec) then return end role = RefClassRoles[spec] unitRole = RefUnitRoles[spec] if role == "T" and UnitLevel("player") == MAX_PLAYER_LEVEL then local bonus, pvp = GetCombatRatingBonus(COMBAT_RATING_RESILIENCE_PLAYER_DAMAGE_TAKEN), false; if bonus > GetDodgeChance() and bonus > GetParryChance() then role = "M" end end self.CurrentSpec = spec self.RoleIsSet = true else local intellect = select(2, UnitStat("player", 4)) local agility = select(2, UnitStat("player", 2)) local baseAP, posAP, negAP = UnitAttackPower("player") local totalAP = baseAP + posAP + negAP; if totalAP > intellect or agility > intellect then role = "M" else role = "C" end end if self.UnitRole ~= unitRole then self.UnitRole = unitRole end if self.ClassRole ~= role then self.ClassRole = role; if self.RoleChangedCallback then self.RoleChangedCallback() end end end --[[ ########################################################## POSITIONING UTILITY FUNCTIONS ########################################################## ]]-- SV.PointIndexes = { ["TOP"] = "TOP", ["BOTTOM"] = "BOTTOM", ["LEFT"] = "LEFT", ["RIGHT"] = "RIGHT", ["TOPRIGHT"] = "UP AND RIGHT", ["TOPLEFT"] = "UP AND LEFT", ["BOTTOMRIGHT"] = "DOWN AND RIGHT", ["BOTTOMLEFT"] = "DOWN AND LEFT", ["CENTER"] = "CENTER", ["RIGHTTOP"] = "RIGHT AND UP", ["LEFTTOP"] = "LEFT AND UP", ["RIGHTBOTTOM"] = "RIGHT AND DOWN", ["LEFTBOTTOM"] = "LEFT AND DOWN", ["INNERRIGHT"] = "INNER RIGHT", ["INNERLEFT"] = "INNER LEFT", ["INNERTOPRIGHT"] = "INNER TOP RIGHT", ["INNERTOPLEFT"] = "INNER TOP LEFT", ["INNERBOTTOMRIGHT"] = "INNER BOTTOM RIGHT", ["INNERBOTTOMLEFT"] = "INNER BOTTOM LEFT", } do local _inverted = { TOP = "BOTTOM", BOTTOM = "TOP", LEFT = "RIGHT", RIGHT = "LEFT", TOPRIGHT = "BOTTOMRIGHT", TOPLEFT = "BOTTOMLEFT", BOTTOMRIGHT = "TOPRIGHT", BOTTOMLEFT = "TOPLEFT", CENTER = "CENTER", RIGHTTOP = "TOPLEFT", LEFTTOP = "TOPRIGHT", RIGHTBOTTOM = "BOTTOMLEFT", LEFTBOTTOM = "BOTTOMRIGHT", INNERRIGHT = "RIGHT", INNERLEFT = "LEFT", INNERTOPRIGHT = "TOPRIGHT", INNERTOPLEFT = "TOPLEFT", INNERBOTTOMRIGHT = "BOTTOMRIGHT", INNERBOTTOMLEFT = "BOTTOMLEFT", } setmetatable(_inverted, { __index = function(t, k) return "CENTER" end}) local _translated = { TOP = "TOP", BOTTOM = "BOTTOM", LEFT = "LEFT", RIGHT = "RIGHT", TOPRIGHT = "TOPRIGHT", TOPLEFT = "TOPLEFT", BOTTOMRIGHT = "BOTTOMRIGHT", BOTTOMLEFT = "BOTTOMLEFT", CENTER = "CENTER", RIGHTTOP = "TOPRIGHT", LEFTTOP = "TOPLEFT", RIGHTBOTTOM = "BOTTOMRIGHT", LEFTBOTTOM = "BOTTOMLEFT", INNERRIGHT = "RIGHT", INNERLEFT = "LEFT", INNERTOPRIGHT = "TOPRIGHT", INNERTOPLEFT = "TOPLEFT", INNERBOTTOMRIGHT = "BOTTOMRIGHT", INNERBOTTOMLEFT = "BOTTOMLEFT", } setmetatable(_translated, { __index = function(t, k) return "CENTER" end}) function SV:GetReversePoint(point) return _inverted[point]; end function SV:SetReversePoint(frame, point, target, x, y) if((not frame) or (not point)) then return; end local anchor = _inverted[point]; local relative = _translated[point]; x = x or 0; y = y or 0; target = target or frame:GetParent() frame:SetPoint(anchor, target, relative, x, y) --[[ auto-set specific properties to save on logic ]]-- frame.initialAnchor = anchor; end end function SV:AnchorToCursor(frame) local x, y = GetCursorPosition() local vHold = (UIParent:GetHeight() * 0.33) local scale = self.Screen:GetEffectiveScale() local initialAnchor = "CENTER" local mod = 0 if(y > (vHold * 2)) then initialAnchor = "TOPLEFT" mod = -12 elseif(y < vHold) then initialAnchor = "BOTTOMLEFT" mod = 12 end frame:ClearAllPoints() frame:SetPoint(initialAnchor, self.Screen, "BOTTOMLEFT", (x / scale), (y / scale) + mod) end --[[ ########################################################## ITEM UTILITY FUNCTIONS ########################################################## ]]-- do local _failsafe = {0} local _upgrades = { [ 1] = {8, 1, 1}, [373] = {4, 1, 2}, [374] = {8, 2, 2}, [375] = {4, 1, 3}, [376] = {4, 2, 3}, [377] = {4, 3, 3}, [378] = {7, 0, 0}, [379] = {4, 1, 2}, [380] = {4, 2, 2}, [445] = {0, 0, 2}, [446] = {4, 1, 2}, [447] = {8, 2, 2}, [451] = {0, 0, 1}, [452] = {8, 1, 1}, [453] = {0, 0, 2}, [454] = {4, 1, 2}, [455] = {8, 2, 2}, [456] = {0, 0, 1}, [457] = {8, 1, 1}, [458] = {0, 0, 4}, [459] = {4, 1, 4}, [460] = {8, 2, 4}, [461] = {12, 3, 4}, [462] = {16, 4, 4}, [465] = {0, 0, 2}, [466] = {4, 1, 2}, [467] = {8, 2, 2}, [468] = {0, 0, 4}, [469] = {4, 1, 4}, [470] = {8, 2, 4}, [471] = {12, 3, 4}, [472] = {16, 4, 4}, [491] = {0, 0, 4}, [492] = {4, 1, 4}, [493] = {8, 2, 4}, [494] = {0, 0, 6}, [495] = {4, 1, 6}, [496] = {8, 2, 6}, [497] = {12, 3, 6}, [498] = {16, 4, 6}, [504] = {12, 3, 4}, [505] = {16, 4, 4}, [506] = {20, 5, 6}, [507] = {24, 6, 6} } local _heirlooms = { 44102,42944,44096,42943,42950,48677,42946,42948,42947,42992,50255,44103, 44107,44095,44098,44097,44105,42951,48683,48685,42949,48687,42984,44100, 44101,44092,48718,44091,42952,48689,44099,42991,42985,48691,44094,44093, 42945,48716 } -- DEPRECATED -- local _heirloom_regex = "|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*):?(%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?"; local _slots = { ["HeadSlot"] = {true, true}, ["NeckSlot"] = {true, false}, ["ShoulderSlot"] = {true, true}, ["BackSlot"] = {true, false}, ["ChestSlot"] = {true, true}, ["WristSlot"] = {true, true}, ["MainHandSlot"] = {true, true, true}, ["SecondaryHandSlot"] = {true, true}, ["HandsSlot"] = {true, true, true}, ["WaistSlot"] = {true, true, true}, ["LegsSlot"] = {true, true, true}, ["FeetSlot"] = {true, true, true}, ["Finger0Slot"] = {true, false, true}, ["Finger1Slot"] = {true, false, true}, ["Trinket0Slot"] = {true, false, true}, ["Trinket1Slot"] = {true, false, true} } setmetatable(_upgrades, { __index = function(t, k) return _failsafe end}) local function _justthetip() for i=1, #GameTooltip.shoppingTooltips do if(not GameTooltip.shoppingTooltips[i]:IsShown()) then return GameTooltip.shoppingTooltips[i] end end end local function _getHeirloomLevel(unit, itemID) if(not itemID) then return; end local baseLevel = UnitLevel(unit) if baseLevel > 85 then baseLevel = 85 end if baseLevel > 80 then for i=1, #_heirlooms do if(_heirlooms[i] == itemID) then baseLevel = 80; end end if baseLevel > 80 then return (((baseLevel - 81) * 12.2) + 272) end elseif baseLevel > 67 then return (((baseLevel - 68) * 6) + 130) elseif baseLevel > 59 then return (((baseLevel - 60) * 3) + 85) end return baseLevel end local function _getItemInfo(itemString) local itemId = tonumber(itemString:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:%d+:(%d+)")) if itemId then local lvl = _upgrades[itemId][1] local cur = _upgrades[itemId][2] local max = _upgrades[itemId][3] return cur, max, lvl end return nil end local function _getItemLevel(unit, itemString) local name, link, quality, iLevel = GetItemInfo(itemString) local itemId = tonumber(itemString:match("item:%d+:%d+:%d+:%d+:%d+:%d+:%-?%d+:%-?%d+:%d+:%d+:(%d+)")) if iLevel and itemId then if(quality == 7) then iLevel = _getHeirloomLevel(unit, itemId) else iLevel = iLevel + _upgrades[itemId][1] end end return iLevel end local function _scanItemLevel(unit, itemString) local tooltip = _justthetip(); if(not tooltip) then return _getItemLevel(unit, itemString) end tooltip:SetOwner(UIParent, "ANCHOR_NONE"); tooltip:SetHyperlink(itemString); tooltip:Show(); local iLevel = 0; local tname = tooltip:GetName().."TextLeft%s"; for i = 2, tooltip:NumLines() do local text = _G[tname:format(i)]:GetText(); if(text and text ~= "") then local value = tonumber(text:match(iLevelFilter)); if(value) then iLevel = value; end end end tooltip:Hide(); return iLevel end function SV:ParseGearSlots(unit, inspecting, firstCallback, secondCallback) local category = (inspecting) and "Inspect" or "Character"; local averageLevel,totalSlots,upgradeAdjust,globalName = 0,0,0; for slotName,flags in pairs(_slots) do globalName = ("%s%s"):format(category, slotName) local slotId = GetInventorySlotInfo(slotName) local iLink = GetInventoryItemLink(unit, slotId) if(iLink and type(iLink) == "string") then local iLevel = _scanItemLevel(unit, iLink) if(iLevel and iLevel > 0) then totalSlots = totalSlots + 1; averageLevel = averageLevel + iLevel if(flags[1] and firstCallback and type(firstCallback) == "function") then firstCallback(globalName, iLevel) end end end if(slotId ~= nil) then if(not inspecting and flags[2] and secondCallback and type(secondCallback) == "function") then secondCallback(globalName, slotId) end end end if(averageLevel < 1 or totalSlots < 15) then return end return floor(averageLevel / totalSlots) end end --[[ ########################################################## CHAT LOG PARSING FUNCTIONS (from LibDeformat by:ckknight) ########################################################## ]]-- do local FORMAT_SEQUENCES = { ["s"] = ".+", ["c"] = ".", ["%d*d"] = "%%-?%%d+", ["[fg]"] = "%%-?%%d+%%.?%%d*", ["%%%.%d[fg]"] = "%%-?%%d+%%.?%%d*", } local STRING_BASED_SEQUENCES = { ["s"] = true, ["c"] = true, } local cache = setmetatable({}, {__mode='k'}) local function _deformat(pattern) local func = cache[pattern] if func then return func end local unpattern = '^' .. pattern:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") .. '$' local number_indexes = {} local index_translation = nil local highest_index if not pattern:find("%%1%$") then local i = 0 while true do i = i + 1 local first_index local first_sequence for sequence in pairs(FORMAT_SEQUENCES) do local index = unpattern:find("%%%%" .. sequence) if index and (not first_index or index < first_index) then first_index = index first_sequence = sequence end end if not first_index then break end unpattern = unpattern:gsub("%%%%" .. first_sequence, "(" .. FORMAT_SEQUENCES[first_sequence] .. ")", 1) number_indexes[i] = not STRING_BASED_SEQUENCES[first_sequence] end highest_index = i - 1 else local i = 0 while true do i = i + 1 local found_sequence for sequence in pairs(FORMAT_SEQUENCES) do if unpattern:find("%%%%" .. i .. "%%%$" .. sequence) then found_sequence = sequence break end end if not found_sequence then break end unpattern = unpattern:gsub("%%%%" .. i .. "%%%$" .. found_sequence, "(" .. FORMAT_SEQUENCES[found_sequence] .. ")", 1) number_indexes[i] = not STRING_BASED_SEQUENCES[found_sequence] end highest_index = i - 1 i = 0 index_translation = {} pattern:gsub("%%(%d)%$", function(w) i = i + 1 index_translation[i] = tonumber(w) end) end if highest_index == 0 then cache[pattern] = SV.fubar else local t = {} t[#t+1] = [=[ return function(text) local ]=] for i = 1, highest_index do if i ~= 1 then t[#t+1] = ", " end t[#t+1] = "a" if not index_translation then t[#t+1] = i else t[#t+1] = index_translation[i] end end t[#t+1] = [=[ = text:match(]=] t[#t+1] = ("%q"):format(unpattern) t[#t+1] = [=[) if not a1 then return ]=] for i = 1, highest_index do if i ~= 1 then t[#t+1] = ", " end t[#t+1] = "nil" end t[#t+1] = "\n" t[#t+1] = [=[ end ]=] t[#t+1] = "return " for i = 1, highest_index do if i ~= 1 then t[#t+1] = ", " end t[#t+1] = "a" t[#t+1] = i if number_indexes[i] then t[#t+1] = "+0" end end t[#t+1] = "\n" t[#t+1] = [=[ end ]=] t = table.concat(t, "") cache[pattern] = assert(loadstring(t))() end return cache[pattern] end function SV:DeFormat(text, pattern) if type(text) ~= "string" then error(("Error: DeFormat text argument %s (%s)."):format(type(text), text), 2) elseif type(pattern) ~= "string" then error(("Error: DeFormat pattern argument %s (%s)."):format(type(pattern), pattern), 2) end return _deformat(pattern)(text) end end --[[ ########################################################## SIMPLE BUTTON CONSTRUCT ########################################################## ]]-- local Button_OnEnter = function(self, ...) if InCombatLockdown() then return end GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT", 0, 4) GameTooltip:ClearLines() GameTooltip:AddLine(self.TText, 1, 1, 1) GameTooltip:Show() end function SV:CreateButton(frame, label, anchor, x, y, width, height, tooltip) local button = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") button:SetWidth(width) button:SetHeight(height) button:SetPoint(anchor, x, y) button:SetText(label) button:RegisterForClicks("AnyUp") button:SetHitRectInsets(0, 0, 0, 0); button:SetFrameStrata("FULLSCREEN_DIALOG"); button.TText = tooltip button:SetButtonTemplate() button:SetScript("OnEnter", Button_OnEnter) button:SetScript("OnLeave", GameTooltip_Hide) return button end