local GlobalAddonName, ExRT = ... ExRT.F.FUNC_FILE_LOADED = true local UnitName, GetTime, GetCursorPosition, UnitIsUnit = UnitName, GetTime, GetCursorPosition, UnitIsUnit local select, floor, tonumber, tostring, string_sub, string_find, string_len, bit_band, type, unpack, pairs, format, strsplit = select, floor, tonumber, tostring, string.sub, string.find, string.len, bit.band, type, unpack, pairs, format, strsplit local string_gsub, string_match = string.gsub, string.match local RAID_CLASS_COLORS, COMBATLOG_OBJECT_TYPE_MASK, COMBATLOG_OBJECT_CONTROL_MASK, COMBATLOG_OBJECT_REACTION_MASK, COMBATLOG_OBJECT_AFFILIATION_MASK, COMBATLOG_OBJECT_SPECIAL_MASK = RAID_CLASS_COLORS, COMBATLOG_OBJECT_TYPE_MASK, COMBATLOG_OBJECT_CONTROL_MASK, COMBATLOG_OBJECT_REACTION_MASK, COMBATLOG_OBJECT_AFFILIATION_MASK, COMBATLOG_OBJECT_SPECIAL_MASK local UnitGroupRolesAssigned = UnitGroupRolesAssigned or ExRT.NULLfunc local GetRaidRosterInfo = GetRaidRosterInfo do local antiSpamArr = {} function ExRT.F.AntiSpam(numantispam,addtime) local t = GetTime() if not antiSpamArr[numantispam] or antiSpamArr[numantispam] < t then antiSpamArr[numantispam] = t + addtime return true else return false end end function ExRT.F.ResetAntiSpam(numantispam) antiSpamArr[numantispam] = nil end end do --Used GLOBALS: CUSTOM_CLASS_COLORS local classColorArray = nil function ExRT.F.classColor(class) classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] if classColorArray and classColorArray.colorStr then return classColorArray.colorStr else return "ffbbbbbb" end end function ExRT.F.classColorNum(class) classColorArray = type(CUSTOM_CLASS_COLORS)=="table" and CUSTOM_CLASS_COLORS[class] or RAID_CLASS_COLORS[class] if classColorArray then return classColorArray.r,classColorArray.g,classColorArray.b else return 0.8,0.8,0.8 end end function ExRT.F.classColorByGUID(guid) local class,_ = "" if guid and guid ~= "" and guid ~= "0000000000000000" then _,class = GetPlayerInfoByGUID(guid) end return ExRT.F.classColor(class) end end function ExRT.F.clearTextTag(text,SpellLinksEnabled) if text then text = string_gsub(text,"|c........","") text = string_gsub(text,"|r","") text = string_gsub(text,"|T.-:0|t ","") text = string_gsub(text,"|HExRT:.-|h(.-)|h","%1") if SpellLinksEnabled then text = string_gsub(text,"|H(spell:.-)|h(.-)|h","|cff71d5ff|H%1|h[%2]|h|r") else text = string_gsub(text,"|H(spell:.-)|h(.-)|h","%2") end return text end end function ExRT.F.splitLongLine(text,maxLetters,SpellLinksEnabled) maxLetters = maxLetters or 250 local result = {} repeat local lettersNow = maxLetters if SpellLinksEnabled then local lastC = 0 local lastR = 0 for i=1,(maxLetters-1) do local word = string.sub(text,i,i+1) if word == "|c" then lastC = i elseif word == "|r" then lastR = i end end if lastC > 0 and lastC > lastR then lettersNow = lastC - 1 end end local utf8pos = 1 local textLen = string.len(text) while true do local char = string.sub(text,utf8pos,utf8pos) local c = char:byte() local lastPos = utf8pos if c > 0 and c <= 127 then utf8pos = utf8pos + 1 elseif c >= 194 and c <= 223 then utf8pos = utf8pos + 2 elseif c >= 224 and c <= 239 then utf8pos = utf8pos + 3 elseif c >= 240 and c <= 244 then utf8pos = utf8pos + 4 else utf8pos = utf8pos + 1 end if utf8pos > lettersNow then lettersNow = lastPos - 1 break elseif utf8pos >= textLen then break end end result[#result + 1] = string.sub(text,1,lettersNow) text = string.sub(text,lettersNow+1) until string.len(text) < maxLetters if string.len(text) > 0 then result[#result + 1] = text end return unpack(result) end function ExRT.F:SetScaleFix(scale) local l = self:GetLeft() local t = self:GetTop() local s = self:GetScale() if not l or not t or not s then return end s = scale / s self:SetScale(scale) local f = self:GetScript("OnDragStop") self:ClearAllPoints() self:SetPoint("TOPLEFT",UIParent,"BOTTOMLEFT",l / s,t / s) if f then f(self) end end function ExRT.F:SetScaleFixTR(scale) --local l = self:GetLeft() + self:GetWidth() * self:GetEffectiveScale() local l = self:GetRight() local t = self:GetTop() local s = self:GetScale() if not l or not t or not s then return end s = scale / s self:SetScale(scale) local f = self:GetScript("OnDragStop") self:ClearAllPoints() self:SetPoint("TOPRIGHT",UIParent,"BOTTOMLEFT",l / s,t / s) if f then f(self) end end function ExRT.F:GetCursorPos() local x_f,y_f = GetCursorPosition() local s = self.GetEffectiveScale and self:GetEffectiveScale() or self:GetParent():GetEffectiveScale() x_f, y_f = x_f/s, y_f/s local x,y = self:GetLeft(),self:GetTop() x = x_f-x y = (y_f-y)*(-1) return x,y end do local function FindAllParents(self,obj) while obj do if obj == self then return true end obj = obj:GetParent() end end function ExRT.F:IsInFocus(x,y,childs) if not x then x,y = ExRT.F.GetCursorPos(self) end local obj = GetMouseFocus() if x > 0 and y > 0 and x < self:GetWidth() and y < self:GetHeight() and (obj == self or (childs and FindAllParents(self,obj))) then return true end end end function ExRT.F:LockMove(isLocked,touchTexture,dontTouchMouse) if isLocked then if touchTexture then touchTexture:SetColorTexture(0,0,0,0.3) end self:SetMovable(true) if not dontTouchMouse then self:EnableMouse(true) end else if touchTexture then touchTexture:SetColorTexture(0,0,0,0) end self:SetMovable(false) if not dontTouchMouse then self:EnableMouse(false) end end end local MAX_RAID_GROUP = 5 if ExRT.isClassic and not ExRT.isBC then MAX_RAID_GROUP = 8 end local DIFF_TO_MAX_GROUP = { [8] = 1, --party mythic [1] = 1, --party normal [2] = 1, --party hc [14] = 6, --raid normal [15] = 6, --raid hc [16] = 4, --raid mythic [3] = 2, --10ppl [5] = 2, --10ppl [9] = 8, --40ppl [186] = 8, --classic 40ppl [148] = 4, --classic 20ppl [185] = 4, --classic 20ppl [175] = 2, --bc 10ppl [176] = 5, --bc 25ppl [151] = 6, --lfr [17] = 6, --lfr [7] = 5, --lfr [legacy] [33] = 6, --timewalk raid [18] = 8, --event 40ppl [193] = 2, --10ppl hc [194] = 5, --25ppl hc } function ExRT.F.GetRaidDiffMaxGroup() local _,instance_type,difficulty = GetInstanceInfo() if (instance_type == "party" or instance_type == "scenario") and not IsInRaid() then return 1 elseif instance_type ~= "raid" then return 8 elseif difficulty and DIFF_TO_MAX_GROUP[difficulty] then return DIFF_TO_MAX_GROUP[difficulty] else return MAX_RAID_GROUP end end function ExRT.F.GetDifficultyForCooldownReset() local _,_,difficulty = GetInstanceInfo() if difficulty == 3 or difficulty == 4 or difficulty == 5 or difficulty == 6 or difficulty == 7 or difficulty == 14 or difficulty == 15 or difficulty == 16 or difficulty == 17 or (ExRT.isLK and (difficulty == 175 or difficulty == 176 or difficulty == 193 or difficulty == 194)) then return true end return false end function ExRT.F.Round(i) return floor(i+0.5) end function ExRT.F.NumberInRange(i,mi,mx,incMi,incMx) if i and ((incMi and i >= mi) or (not incMi and i > mi)) and ((incMx and i <= mx) or (not incMx and i < mx)) then return true end end function ExRT.F.delUnitNameServer(unitName) unitName = strsplit("-",unitName) return unitName end function ExRT.F.UnitCombatlogname(unit) local name,server = UnitName(unit or "?") if name and server and server~="" then name = name .. "-" .. server end return name end do local old_types = { Player = 0, Creature = 3, Pet = 4, Vehicle = 5, GameObject = 6, --NEW Vignette = 7, --NEW Item = 8, --NEW Item:976:0:4000000003A91C1A Uniq = 9, --NEW } function ExRT.F.GetUnitTypeByGUID(guid) if guid then local _type = string_match(guid,"^([A-z]+)%-") if _type then return old_types[_type] end end end end function ExRT.F.UnitIsPlayerOrPet(guid) local id = ExRT.F.GetUnitTypeByGUID(guid) if id == 0 or id == 4 then return true end end function ExRT.F.GetUnitInfoByUnitFlag(unitFlag,infoType) --> TYPE if infoType == 1 then return bit_band(unitFlag,COMBATLOG_OBJECT_TYPE_MASK) --[1024]="player", [2048]="NPC", [4096]="pet", [8192]="GUARDIAN", [16384]="OBJECT" --> CONTROL elseif infoType == 2 then return bit_band(unitFlag,COMBATLOG_OBJECT_CONTROL_MASK) --[256]="by players", [512]="by NPC", --> REACTION elseif infoType == 3 then return bit_band(unitFlag,COMBATLOG_OBJECT_REACTION_MASK) --[16]="FRIENDLY", [32]="NEUTRAL", [64]="HOSTILE" --> Controller affiliation elseif infoType == 4 then return bit_band(unitFlag,COMBATLOG_OBJECT_AFFILIATION_MASK) --[1]="player", [2]="PARTY", [4]="RAID", [8]="OUTSIDER" --> Special elseif infoType == 5 then return bit_band(unitFlag,COMBATLOG_OBJECT_SPECIAL_MASK) --Not all ! [65536]="TARGET", [131072]="FOCUS", [262144]="MAINTANK", [524288]="MAINASSIST" end end function ExRT.F.UnitIsFriendlyByUnitFlag(unitFlag) if ExRT.F.GetUnitInfoByUnitFlag(unitFlag,2) == 256 then return true end end function ExRT.F.UnitIsFriendlyByUnitFlag2(unitFlag) local reaction = ExRT.F.GetUnitInfoByUnitFlag(unitFlag or 0,3) if reaction == 16 then return true elseif reaction == 32 then if ExRT.F.GetUnitInfoByUnitFlag(unitFlag,2) == 256 then return true end end end function ExRT.F.dprint(...) return nil end function ExRT.F.dtime(...) return nil end if ExRT.isDev then --debug or debug ultra ExRT.F.dprint = function(...) print(...) end local debugprofilestop = debugprofilestop local lastTime = nil ExRT.F.dtime = function(arg,...) if arg and lastTime then arg[#arg+1] = {debugprofilestop() - lastTime,...} else lastTime = debugprofilestop() end end end function ExRT.F.LinkSpell(SpellID,SpellLink) if not SpellLink then SpellLink = GetSpellLink(SpellID) end if SpellLink then if ChatEdit_GetActiveWindow() then ChatEdit_InsertLink(SpellLink) else ChatFrame_OpenChat(SpellLink) end end end function ExRT.F.LinkItem(itemID, itemLink) if not itemLink then if not itemID then return end itemLink = select(2,GetItemInfo(itemID)) end if not itemLink then return end if IsModifiedClick("DRESSUP") then return DressUpItemLink(itemLink) else if ChatEdit_GetActiveWindow() then ChatEdit_InsertLink(itemLink) else ChatFrame_OpenChat(itemLink) end end end function ExRT.F.shortNumber(num) if num < 1000 then return tostring(num) elseif num < 1000000 then return format("%.1fk",num/1000) elseif num < 1000000000 then return format("%.2fm",num/1000000) else return format("%.3fM",num/1000000000) end end function ExRT.F.classIconInText(class,size) if CLASS_ICON_TCOORDS[class] then size = size or 0 if class == "EVOKER" then return "|Tinterface\\icons\\classicon_evoker:"..size..":"..size.."|t" else return "|TInterface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES:"..size..":"..size..":0:0:256:256:".. floor(CLASS_ICON_TCOORDS[class][1]*256) ..":"..floor(CLASS_ICON_TCOORDS[class][2]*256) ..":"..floor(CLASS_ICON_TCOORDS[class][3]*256) ..":"..floor(CLASS_ICON_TCOORDS[class][4]*256) .."|t" end end end function ExRT.F.GUIDtoID(guid) local type,_,serverID,instanceID,zoneUID,id,spawnID = strsplit("-", guid or "") return tonumber(id or 0) end function ExRT.F.table_copy(table1,table2) table.wipe(table2) for key,val in pairs(table1) do table2[key] = val end end function ExRT.F.table_copy2(table1) local table2 = {} for key,val in pairs(table1) do if type(val) == 'table' then table2[key] = ExRT.F.table_copy2(val) else table2[key] = val end end return table2 end function ExRT.F.table_wipe(arr) if not arr or type(arr) ~= "table" then return end for key,val in pairs(arr) do if type(val) == "table" then ExRT.F.table_wipe(val) end arr[key] = nil end end function ExRT.F.table_find(arr,subj,pos) if pos then for j=1,#arr do if arr[j][pos] == subj then return j end end else for j=1,#arr do if arr[j] == subj then return j end end end end function ExRT.F.table_find2(arr,subj) for key,val in pairs(arr) do if val == subj then return key end end end function ExRT.F.table_find3(arr,subj,pos) for j=1,#arr do if arr[j][pos] == subj then return arr[j] end end end function ExRT.F.table_len(arr) local len = 0 for _ in pairs(arr) do len = len + 1 end return len end function ExRT.F.table_add(arr,add) for i=1,#add do arr[#arr+1] = add[i] end end function ExRT.F.table_add2(arr,add) for key,val in pairs(add) do arr[key] = val end end function ExRT.F.table_keys(arr) local r = {} for key,val in pairs(arr) do r[#r+1] = key end return r end do local function swap(array, index1, index2) array[index1], array[index2] = array[index2], array[index1] end local math_random = math.random function ExRT.F.table_shuffle(array) local counter = #array while counter > 1 do local index = math_random(counter) swap(array, index, counter) counter = counter - 1 end end end do local printDiff = false local printTable = nil local function cmp(t1,t2,r,p) local c,t = 0,0 p = p or "." for k,v in pairs(t1) do if type(v) == "table" then if type(t2[k]) == "table" then local c2,t2 = cmp(v,t2[k],false,p..k..".") c = c + c2 t = t + t2 else t = t + 1 if printTable then printTable[p..k] = true elseif printDiff then print(p..k) end end else t = t + 1 if t1[k] == t2[k] then c = c + 1 elseif type(v) == "number" and type(t2[k]) == "number" and tostring(v) == tostring(t2[k]) then c = c + 1 elseif printTable then printTable[p..k] = true elseif printDiff then print(p..k) end end end if not r then local c2,t2 = cmp(t2,t1,true,p) c = c + c2 t = t + t2 end return c, t end function ExRT.F.table_compare(t1,t2,showDiff) printDiff = showDiff if type(printDiff) == "table" then printTable = printDiff printDiff = nil else printTable = nil end local c, t = cmp(t1,t2) return c / max(1,t), c, t end end function ExRT.F.table_rewrite(t1,t2) local toRemove = {} for k,v in pairs(t1) do if not t2[k] then toRemove[k] = true elseif type(v) == "table" and type(t2[k]) == "table" then ExRT.F.table_rewrite(v,t2[k]) else t1[k] = t2[k] end end for k,v in pairs(toRemove) do t1[k] = nil end for k,v in pairs(t2) do if not t1[k] then t1[k] = v end end end function ExRT.F.tohex(num,size) return format("%0"..(size or "1").."X",num) end function ExRT.F.UnitInGuild(unit) unit = ExRT.F.delUnitNameServer(unit) local gplayers = GetNumGuildMembers() or 0 for i=1,gplayers do local name = GetGuildRosterInfo(i) if name and ExRT.F.delUnitNameServer(name) == unit then return true end end return false end function ExRT.F.chatType(toSay) local isInInstance = IsInGroup(LE_PARTY_CATEGORY_INSTANCE) local isInParty = IsInGroup() local isInRaid = IsInRaid() local playerName = nil local chat_type = (isInInstance and "INSTANCE_CHAT") or (isInRaid and "RAID") or (isInParty and "PARTY") if not chat_type and not toSay then chat_type = "WHISPER" playerName = UnitName("player") elseif not chat_type then chat_type = "SAY" end return chat_type, playerName end function ExRT.F.IsBonusOnItem(link,bonus) if link then local _,itemID,enchant,gem1,gem2,gem3,gem4,suffixID,uniqueID,level,specializationID,upgradeType,instanceDifficultyID,numBonusIDs,restLink = strsplit(":",link,15) if restLink then local bonuses = {strsplit(":",strsplit("|h",restLink),nil)} numBonusIDs = tonumber(numBonusIDs) or 0 local isTable = type(bonus) == "table" for i = 1, numBonusIDs do local bonusID = tonumber(bonuses[i]) or -999 if (isTable and bonus[bonusID]) or (not isTable and bonusID == bonus) then return true end end end end end function ExRT.F.GetItemBonuses(link) if link then local _,itemID,enchant,gem1,gem2,gem3,gem4,suffixID,uniqueID,level,specializationID,upgradeType,instanceDifficultyID,numBonusIDs,restLink = strsplit(":",link,15) numBonusIDs = tonumber(numBonusIDs or "?") or 0 local bonusStr = "" for i=1,numBonusIDs do local bonus = select(i,strsplit(":",restLink)) if bonus then bonusStr = bonusStr .. bonus .. ":" end end bonusStr = strtrim(bonusStr,":") return bonusStr,numBonusIDs end return "",0 end --/dump GMRT.F.GetItemBonuses(select(2,GameTooltip:GetItem())) function ExRT.F.IsPlayerRLorOfficer(unitName) local shortName = ExRT.F.delUnitNameServer(unitName) for i=1,GetNumGroupMembers() do --if name and (name == unitName or ExRT.F.delUnitNameServer(name) == shortName) then if UnitIsUnit(unitName,"raid"..i) or UnitIsUnit(shortName,"raid"..i) then local name,rank = GetRaidRosterInfo(i) if rank > 0 then return rank else return false end end end -- nil: not in party or raid -- false: no rl, no officer -- 1: officer -- 2: rl end function ExRT.F.GetPlayerParty(unitName) for i=1,GetNumGroupMembers() do local name,_,subgroup = GetRaidRosterInfo(i) if UnitIsUnit(name,unitName) then return subgroup end end return 0 end function ExRT.F.GetOwnPartyNum() for i=1,GetNumGroupMembers() do local name,_,subgroup = GetRaidRosterInfo(i) if UnitIsUnit(name,'player') then return subgroup end end return 1 end function ExRT.F.CreateAddonMsg(...) local result = "" for i=1,select('#',...) do local a = select(i,...) result = result..(result ~= "" and "\t" or "")..tostring(a) end return result end function ExRT.F.GetPlayerRole() local role = UnitGroupRolesAssigned('player') if role == "HEALER" then local _,class = UnitClass('player') return role, (class == "PALADIN" or class == "MONK") and "MHEALER" or "RHEALER" elseif role ~= "DAMAGER" then --TANK, NONE return role else local _,class = UnitClass('player') local isMelee = (class == "WARRIOR" or class == "PALADIN" or class == "ROGUE" or class == "DEATHKNIGHT" or class == "MONK" or class == "DEMONHUNTER") if class == "DRUID" then isMelee = GetSpecialization() ~= 1 elseif class == "SHAMAN" then isMelee = GetSpecialization() == 2 elseif class == "HUNTER" then isMelee = GetSpecialization() == 3 end if isMelee then return role, "MDD" else return role, "RDD" end end end function ExRT.F.GetUnitRole(unit) local role = UnitGroupRolesAssigned(unit) if role == "HEALER" then local _,class = UnitClass(unit) return role, (class == "PALADIN" or class == "MONK") and "MHEALER" or "RHEALER" elseif role ~= "DAMAGER" then --TANK, NONE return role else local _,class = UnitClass(unit) local isMelee = (class == "WARRIOR" or class == "PALADIN" or class == "ROGUE" or class == "DEATHKNIGHT" or class == "MONK" or class == "DEMONHUNTER") if class == "DRUID" then isMelee = not (UnitPowerType(unit) == 8) --astral power elseif class == "SHAMAN" then isMelee = UnitPowerMax(unit) >= 150 elseif class == "HUNTER" then isMelee = (ExRT.A.Inspect and UnitName(unit) and ExRT.A.Inspect.db.inspectDB[UnitName(unit)] and ExRT.A.Inspect.db.inspectDB[UnitName(unit)].spec) == 255 end if isMelee then return role, "MDD" else return role, "RDD" end end end function ExRT.F.TextureToText(textureName,widthInText,heightInText,textureWidth,textureHeight,leftTexCoord,rightTexCoord,topTexCoord,bottomTexCoord) return "|T"..textureName..":"..(widthInText or 0)..":"..(heightInText or 0)..":0:0:"..textureWidth..":"..textureHeight..":".. format("%d",leftTexCoord*textureWidth)..":"..format("%d",rightTexCoord*textureWidth)..":"..format("%d",topTexCoord*textureHeight)..":"..format("%d",bottomTexCoord*textureHeight).."|t" end function ExRT.F.GetRaidTargetText(icon,size) size = size or 0 return ExRT.F.TextureToText([[Interface\TargetingFrame\UI-RaidTargetingIcons]],size,size,256,256,((icon-1)%4)/4,((icon-1)%4+1)/4,floor((icon-1)/4)/4,(floor((icon-1)/4)+1)/4) end function ExRT.F.IterateMediaData(mediaType) local list if LibStub then local loaded,media = pcall(LibStub,"LibSharedMedia-3.0") if loaded and media then list = media:HashTable(mediaType) end end return next, list or {} end --[[ for index, name, subgroup, class, guid, rank, level, online, isDead, combatRole in ExRT.F.IterateRoster do <...> end ]] function ExRT.F.IterateRoster(maxGroup,index) index = (index or 0) + 1 maxGroup = maxGroup or 8 if IsInRaid() then if index > GetNumGroupMembers() then return end local name, rank, subgroup, level, class, fileName, zone, online, isDead, role, isML, combatRole = GetRaidRosterInfo(index) if subgroup > maxGroup then return ExRT.F.IterateRoster(maxGroup,index) end local guid = UnitGUID(name or "raid"..index) name = name or "" return index, name, subgroup, fileName, guid, rank, level, online, isDead, combatRole else local name, rank, subgroup, level, class, fileName, online, isDead, combatRole, _ local unit = index == 1 and "player" or "party"..(index-1) local guid = UnitGUID(unit) if not guid then return end subgroup = 1 name, _ = UnitName(unit) name = name or "" if _ then name = name .. "-" .. _ end class, fileName = UnitClass(unit) if UnitIsGroupLeader(unit) then rank = 2 else rank = 1 end level = UnitLevel(unit) if UnitIsConnected(unit) then online = true end if UnitIsDeadOrGhost(unit) then isDead = true end combatRole = UnitGroupRolesAssigned(unit) return index, name, subgroup, fileName, guid, rank, level, online, isDead, combatRole end end function ExRT.F.vpairs(t) local prev local function it() local k,v = next(t,prev) prev = k return v end return it end function ExRT.F:SafeCall(func, ...) local res, arg1, arg2, arg3, arg4, arg5, arg6, arg7 = xpcall(func, nil, ...) if res then return arg1, arg2, arg3, arg4, arg5, arg6, arg7 end end do -- UTF8 -- returns the number of bytes used by the UTF-8 character at byte i in s -- also doubles as a UTF-8 character validator local function utf8charbytes(s, i) -- argument defaults i = i or 1 -- argument checking if type(s) ~= "string" then error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")") end if type(i) ~= "number" then error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")") end local c = strbyte(s, i) -- determine bytes needed for character, based on RFC 3629 -- validate byte 1 if c > 0 and c <= 127 then -- UTF8-1 return 1 elseif c >= 194 and c <= 223 then -- UTF8-2 local c2 = strbyte(s, i + 1) if not c2 then error("UTF-8 string terminated early") end -- validate byte 2 if c2 < 128 or c2 > 191 then error("Invalid UTF-8 character") end return 2 elseif c >= 224 and c <= 239 then -- UTF8-3 local c2 = strbyte(s, i + 1) local c3 = strbyte(s, i + 2) if not c2 or not c3 then error("UTF-8 string terminated early") end -- validate byte 2 if c == 224 and (c2 < 160 or c2 > 191) then error("Invalid UTF-8 character") elseif c == 237 and (c2 < 128 or c2 > 159) then error("Invalid UTF-8 character") elseif c2 < 128 or c2 > 191 then error("Invalid UTF-8 character") end -- validate byte 3 if c3 < 128 or c3 > 191 then error("Invalid UTF-8 character") end return 3 elseif c >= 240 and c <= 244 then -- UTF8-4 local c2 = strbyte(s, i + 1) local c3 = strbyte(s, i + 2) local c4 = strbyte(s, i + 3) if not c2 or not c3 or not c4 then error("UTF-8 string terminated early") end -- validate byte 2 if c == 240 and (c2 < 144 or c2 > 191) then error("Invalid UTF-8 character") elseif c == 244 and (c2 < 128 or c2 > 143) then error("Invalid UTF-8 character") elseif c2 < 128 or c2 > 191 then error("Invalid UTF-8 character") end -- validate byte 3 if c3 < 128 or c3 > 191 then error("Invalid UTF-8 character") end -- validate byte 4 if c4 < 128 or c4 > 191 then error("Invalid UTF-8 character") end return 4 else error("Invalid UTF-8 character") end end function ExRT.F.utf8len(s) local pos = 1 local bytes = strlen(s) local len = 0 while pos <= bytes do len = len + 1 pos = pos + utf8charbytes(s, pos) end return len end -- functions identically to string.sub except that i and j are UTF-8 characters -- instead of bytes function ExRT.F.utf8sub(s, i, j) -- argument defaults j = j or -1 local pos = 1 local bytes = strlen(s) local len = 0 -- only set l if i or j is negative local l = (i >= 0 and j >= 0) or ExRT.F.utf8len(s) local startChar = (i >= 0) and i or l + i + 1 local endChar = (j >= 0) and j or l + j + 1 -- can't have start before end! if startChar > endChar then return "" end -- byte offsets to pass to string.sub local startByte, endByte = 1, bytes while pos <= bytes do len = len + 1 if len == startChar then startByte = pos end pos = pos + utf8charbytes(s, pos) if len == endChar then endByte = pos - 1 break end end return strsub(s, startByte, endByte) end end function ExRT.F.GetFirstTableInText(str) local strlen = str:len() local i = 1 local deep = 0 local start local inStr local inWideStr local wideStrEqCount = 0 while i <= strlen do local b = str:byte(i) if not start and not inStr and b == 123 then start = i elseif start and not inStr and b == 123 then deep = deep + 1 elseif start and not inStr and b == 125 then if deep <= 0 then return str:sub(start,i) else deep = deep - 1 end elseif not inStr and b == 34 then --" inStr = i elseif inStr and not inWideStr and b == 92 then --\ i = i + 1 elseif inStr and not inWideStr and b == 34 then --" inStr = false elseif not inStr and b == 91 then -- [ = [ local k = i + 1 local eqc = 0 while k <= strlen do local c1 = str:byte(k) if c1 == 61 then eqc = eqc + 1 elseif c1 == b then inStr = i inWideStr = i wideStrEqCount = eqc break else break end k = k + 1 end elseif inStr and inWideStr and b == 93 then -- ] = ] local k = i + 1 local eqc = 0 while k <= strlen do local c1 = str:byte(k) if c1 == 61 then eqc = eqc + 1 elseif c1 == b then if eqc == wideStrEqCount then i = k inStr = false inWideStr = false end break else break end k = k + 1 end end i = i + 1 end end do local chatWindow = nil local activeChat = "RAID" local activeName = "" local UpdateLines = nil local function CreateChatWindow() chatWindow = ELib:Template("ExRTDialogModernTemplate",UIParent) _G["MRTToChatWindow"] = chatWindow chatWindow:SetSize(400,300) chatWindow:SetPoint("CENTER") chatWindow:SetFrameStrata("DIALOG") chatWindow:SetClampedToScreen(true) chatWindow:EnableMouse(true) chatWindow:SetMovable(true) chatWindow:RegisterForDrag("LeftButton") chatWindow:SetDontSavePosition(true) chatWindow:SetScript("OnDragStart", function(self) self:StartMoving() end) chatWindow:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end) chatWindow.border = ExRT.lib:Shadow(chatWindow,20) chatWindow.title:SetText(ExRT.L.ChatwindowName) chatWindow.box = ExRT.lib:MultiEdit(chatWindow):Size(230,265):Point(10,-23):Font('x',11) local chats = { {"ME",ExRT.L.ChatwindowChatSelf}, {"SAY",ExRT.L.ChatwindowChatSay}, {"PARTY",ExRT.L.ChatwindowChatParty}, {"INSTANCE_CHAT",ExRT.L.ChatwindowChatInstance}, {"RAID",ExRT.L.ChatwindowChatRaid}, {"WHISPER",ExRT.L.ChatwindowChatWhisper}, {"TARGET",ExRT.L.ChatwindowChatWhisperTarget}, {"GUILD",ExRT.L.ChatwindowChatGuild}, {"OFFICER",ExRT.L.ChatwindowChatOfficer}, } chatWindow.dropDown = ExRT.lib:DropDown(chatWindow,130,#chats):Size(130):Point(255,-60):SetText(ExRT.L.ChatwindowChatRaid) chatWindow.dropDownText = ExRT.lib:Text(chatWindow,ExRT.L.ChatwindowChannel,10):Size(350,14):Point("BOTTOMLEFT",chatWindow.dropDown,"TOPLEFT",5,2):Color():Shadow() for i=1,#chats do local chatData = chats[i] chatWindow.dropDown.List[i] = { text = chatData[2], notCheckable = true, justifyH = "CENTER", arg1 = chatData[1], arg2 = chatData[2], func = function (this,arg1,arg2) chatWindow.dropDown:SetText(arg2) ExRT.lib:DropDownClose() activeChat = arg1 end } end chatWindow.target = ExRT.lib:Edit(chatWindow):Size(130,20):Point(255,-115):OnChange(function (self) activeName = self:GetText() end) chatWindow.targetText = ExRT.lib:Text(chatWindow,ExRT.L.ChatwindowNameEB,10):Size(350,14):Point("BOTTOMLEFT",chatWindow.target,"TOPLEFT",5,2):Bottom():Color():Shadow() chatWindow.button = ExRT.lib:Button(chatWindow,ExRT.L.ChatwindowSend):Size(130,22):Point(255,-150):OnClick(function (self) local lines = {strsplit("\n", chatWindow.box.EditBox:GetText())} local channel = activeChat local whisper = activeName if channel == "TARGET" then channel = "WHISPER" whisper = ExRT.F.UnitCombatlogname("target") if not whisper then return end end if channel == "ME" then for i=1,#lines do if lines[i] ~= "" then print(lines[i]) end end chatWindow:Hide() return end if whisper == "" then whisper = nil end for i=1,#lines do if lines[i] ~= "" then SendChatMessage(lines[i],channel,nil,whisper) end end chatWindow:Hide() end) chatWindow.helpText = ExRT.lib:Text(chatWindow,ExRT.L.ChatwindowHelp,10):Size(130,100):Point("TOP",chatWindow.button,"BOTTOM",0,-10):Top():Color():Shadow() chatWindow.chk1 = ExRT.lib:Check(chatWindow,"Option 1"):Point(255,-260):OnClick(function() UpdateLines() end) end function UpdateLines() local lines = chatWindow.lines if not lines or type(lines)~="table" then return end local editData = "" local linesCount = #lines local clearTags = chatWindow.clearTags local option1 = chatWindow.option1enabled and chatWindow.chk1:GetChecked() and "%1" or "" for i=1,linesCount do local thisLine = lines[i] if thisLine ~= "" then thisLine = thisLine:gsub("@1@(.-)@1#",option1) if clearTags then thisLine = ExRT.F.clearTextTag(thisLine) end if strlen(thisLine) > 254 then thisLine = strjoin("\n",ExRT.F.splitLongLine(thisLine,254)) end editData = editData .. thisLine if i ~= linesCount then editData = editData .. "\n" end end end chatWindow.box.EditBox:SetText(editData) end function ExRT.F:ToChatWindow(lines,clearTags,option1Name) if not lines or type(lines)~="table" then return end if not chatWindow then CreateChatWindow() end chatWindow.lines = lines chatWindow.clearTags = clearTags chatWindow.option1enabled = option1Name if option1Name then chatWindow.chk1.text:SetText(option1Name) chatWindow.chk1:SetChecked(false) chatWindow.chk1:Show() else chatWindow.chk1:Hide() end UpdateLines() chatWindow:Show() end end do local alertWindow = nil local alertFunc = nil local alertArg1 = nil local function CreateWindow() alertWindow = ExRT.lib:Popup():Size(500,65) alertWindow:SetFrameStrata("FULLSCREEN_DIALOG") alertWindow.EditBox = ExRT.lib:Edit(alertWindow):Size(480,16):Point("TOP",0,-20) alertWindow.OK = ExRT.lib:Button(alertWindow,ACCEPT):Size(130,20):Point("TOP",0,-40):OnClick(function (self) alertWindow:Hide() local input = alertWindow.EditBox:GetText() alertFunc(alertArg1,input) end) alertWindow.EditBox:SetScript("OnEnterPressed",function (self) self:GetParent().OK:Click("LeftButton") end) end function ExRT.F.ShowInput(text,func,arg1,onlyNum,defText,funcOnEdit) if not alertWindow then CreateWindow() end alertWindow.OK:Enable() alertWindow.title:SetText(text) alertWindow.EditBox:SetScript("OnTextChanged",funcOnEdit) alertWindow.EditBox:SetText(defText or "") alertWindow:ClearAllPoints() alertWindow:SetPoint("CENTER",UIParent,0,0) alertFunc = func alertArg1 = arg1 if onlyNum then alertWindow.EditBox:SetNumeric(true) else alertWindow.EditBox:SetNumeric(false) end alertWindow:Show() alertWindow.EditBox:SetFocus() end function ExRT.F.ShowText(text) if not alertWindow then CreateWindow() end alertWindow.title:SetText("") alertWindow:ClearAllPoints() alertWindow:SetPoint("CENTER",UIParent,0,0) alertWindow.EditBox:SetNumeric(false) alertWindow.EditBox:SetText(text) alertWindow:Show() alertWindow.EditBox:SetFocus() alertFunc = ExRT.NULLfunc end end ---------------> Chat links hook <--------------- do local chatLinkFormat = "|HMRT:%s:0|h|cffffff00[MRT: %s]|r|h" local funcTable = {} local function createChatHook() local SetHyperlink = ItemRefTooltip.SetHyperlink function ItemRefTooltip:SetHyperlink(link, ...) local funcName = link:match("^MRT:([^:]+):") if funcName then local func = funcTable[funcName] if not func then return end func() else SetHyperlink(self, link, ...) end end end function ExRT.F.CreateChatLink(funcName,func,stringName) if createChatHook then createChatHook() createChatHook = nil end if not funcName or not stringName or type(func) ~= "function" then return "" end funcTable[funcName] = func return chatLinkFormat:format(funcName,stringName) end end ---------------> Export Window <--------------- do local exportWindow function ExRT.F:Export(stringData,hideTextInfo,windowName,onChangeFunc) if not exportWindow then exportWindow = ELib:Popup(ExRT.L.Export):Size(650,615) exportWindow.Edit = ELib:MultiEdit(exportWindow):Point("TOP",0,-20):Size(640,575) exportWindow.TextInfo = ELib:Text(exportWindow,ExRT.L.ExportInfo,11):Color():Point("BOTTOM",0,3):Size(640,15):Bottom():Left() exportWindow:SetScript("OnHide",function(self) self.Edit:SetText("") end) exportWindow.Next = ExRT.lib:Button(exportWindow,">>>"):Size(100,16):Point("BOTTOMRIGHT",0,0):OnClick(function (self) self.now = self.now + 1 self:SetText(">>> "..self.now.."/"..#exportWindow.hugeText) exportWindow.Edit:SetText(exportWindow.hugeText[self.now]) exportWindow.Edit.EditBox:HighlightText() exportWindow.Edit.EditBox:SetFocus() if self.now == #exportWindow.hugeText then self:Hide() end end) end exportWindow.title:SetText(windowName or ExRT.L.Export) exportWindow.Edit.OnTextChanged = onChangeFunc exportWindow:NewPoint("CENTER",UIParent,0,0) exportWindow.TextInfo:SetShown(not hideTextInfo) exportWindow:Show() if #stringData > 200000 then exportWindow.hugeText = {} while stringData and stringData ~= "" do local newText = stringData:sub(1,200000)..strsplit("\n",stringData:sub(200001)) exportWindow.hugeText[#exportWindow.hugeText+1] = newText stringData = select(2,strsplit("\n",stringData:sub(200001),2)) end exportWindow.Next.now = 0 exportWindow.Next:Show() exportWindow.Next:Click() else exportWindow.hugeText = nil exportWindow.Next:Hide() exportWindow.Edit:SetText(stringData) exportWindow.Edit.EditBox:HighlightText() exportWindow.Edit.EditBox:SetFocus() end end end do local exportWindow local pendingToShow function ExRT.F:Export2(stringData) if not exportWindow then exportWindow = ExRT.lib:Popup("Export"):Size(650,50) exportWindow.Edit = ExRT.lib:Edit(exportWindow):Point("TOP",0,-20):Size(640,25) exportWindow:SetScript("OnHide",function(self) self.Edit:SetText("") self.onEnterFunc = nil if pendingToShow then stringData = select(2,next(pendingToShow)) if not stringData then pendingToShow = nil return end tremove(pendingToShow, 1) C_Timer.After(0,function() exportWindow.Edit:SetText(stringData) exportWindow:Show() end) end end) exportWindow.Edit:SetScript("OnEditFocusGained", function(self) self:HighlightText() end) exportWindow.Edit:SetScript("OnMouseUp", function(self, button) self:HighlightText() if button == "RightButton" then self:GetParent():Hide() end end) exportWindow.Edit:SetScript("OnKeyUp", function(self, c) if (c == "c" or c == "C") and IsControlKeyDown() then self:GetParent():Hide() end end) exportWindow.Edit:SetScript("OnEnterPressed",function(self) if self:GetParent().onEnterFunc then self:GetParent().onEnterFunc(self:GetText()) self:GetParent():Hide() end end) function exportWindow:OnShow() self.Edit:SetFocus() end elseif exportWindow:IsShown() then if not pendingToShow then pendingToShow = {} end pendingToShow[#pendingToShow+1] = stringData return end if type(stringData) == "function" then exportWindow.onEnterFunc = stringData stringData = "" else exportWindow.onEnterFunc = nil end if type(stringData) == "table" then stringData = table.concat(ExRT.F.TableToText(stringData)) elseif type(stringData) ~= "string" then stringData = tostring(stringData) end exportWindow:NewPoint("CENTER",UIParent,0,0) exportWindow.Edit:SetText(stringData) exportWindow:Show() end end ---------------> Import/Export data <--------------- do local function StringToText(str) if str:find("\n") then local n = 0 if str:find("%]$") then n = n + 1 end while str:find("%["..string.rep("=",n).."%[") or str:find("%]"..string.rep("=",n).."%]") do n = n + 1 end return "["..string.rep("=",n).."["..str.."]"..string.rep("=",n).."]", true else return "\""..str:gsub("\\","\\\\"):gsub("\"","\\\"").."\"" end end local function IterateTable(t) local prev local index = 1 local indexMax local isIndexDone local function it() if not indexMax then local v = t[index] if v then index = index + 1 return index-1, v, true else indexMax = index - 1 end end local k,v = next(t,prev) prev = k while k and type(k)=="number" and k >= 1 and k <= indexMax do k,v = next(t,prev) prev = k end return k, v, false end return it end function ExRT.F.TableToText(t,e,b) b = b or {} b[t] = true e = e or {"{"} local ignoreIndex = false for k,v,isIndex in IterateTable(t) do local newline = "" local ignore = true if type(v) == "boolean" or type(v) == "number" or type(v) == "string" or type(v) == "table" then ignore = false end if type(v) == "table" and b[v] then ignore = true end if ignore then ignoreIndex = true elseif isIndex and not ignoreIndex then elseif type(k)=="number" then newline = newline .. "["..k.."]=" elseif type(k)=="string" then if k:match("[A-Za-z_][A-Za-z_0-9]*") == k then newline = newline .. k .. "=" else local kstr, ismultiline = StringToText(k) newline = newline .. "["..(ismultiline and " " or "")..kstr..(ismultiline and " " or "").."]=" end elseif type(k)=="boolean" then newline = newline .. "["..(k and "true" or "false").."]=" else ignore = true end if not ignore then local tableToExplore if type(v)=="number" then newline = newline .. v .. "," elseif type(v)=="string" then newline = newline .. StringToText(v).."," elseif type(v)=="boolean" then newline = newline ..(v and "true" or "false").."," elseif type(v)=="table" then newline = newline .."{" tableToExplore = v end e[#e+1] = newline if tableToExplore then ExRT.F.TableToText(tableToExplore,e,b) e[#e] = e[#e] .. "," end end end e[#e] = e[#e]:gsub(",$","") e[#e+1] = "}" return e end local string_byte = string.byte function ExRT.F.TextToTable(str,map,offset) if not map and string_byte(str, 1) == 123 then str = str:sub(2,-2) --Expect valid table here end local strlen = str:len() local i = 1 local prev = 1 map = map or {} offset = offset or 0 local inTable, inString, inWideString local startTable, wideStringEqCount = 1, 0 while i <= strlen do local b1 = string_byte(str, i) if not inString and not inTable and b1 == 123 then inTable = 0 startTable = i elseif not inString and inTable and b1 == 123 then inTable = inTable + 1 elseif not inString and inTable and b1 == 125 then if inTable == 0 then map[startTable+offset] = i + offset map[startTable+0.5+offset] = 1 inTable = false else inTable = inTable - 1 end elseif not inString and b1 == 34 then --" if map[i+offset+0.5] == 2 then i = map[i+offset] - offset else inString = i end elseif inString and not inWideString and b1 == 92 then --\ i = i + 1 elseif inString and not inWideString and b1 == 34 then --" map[inString+offset] = i + offset map[inString+0.5+offset] = 2 inString = false elseif not inString and b1 == 91 then -- [ = [ if map[i+offset+0.5] == 3 then i = map[i+offset] - offset else local k = i + 1 local eqc = 0 while k <= strlen do local c1 = string_byte(str, k) if c1 == 61 then eqc = eqc + 1 elseif c1 == b1 then inString = i inWideString = i i = k wideStringEqCount = eqc break else break end k = k + 1 end end elseif inString and inWideString and b1 == 93 then -- ] = ] local k = i + 1 local eqc = 0 while k <= strlen do local c1 = string_byte(str, k) if c1 == 61 then eqc = eqc + 1 elseif c1 == b1 then if eqc == wideStringEqCount then i = k map[inWideString+offset] = i + offset map[inWideString+0.5+offset] = 3 inString = false inWideString = false end break else break end k = k + 1 end end if not inString and not inTable and (b1 == 44 or i == strlen) then --, map[-prev-offset] = i - (b1 == 44 and 1 or 0) + offset prev = i + 1 end i = i + 1 end local res = {} local numKey = 1 i = 1 while i <= strlen do if map[-i-offset] then local s, e = i, map[-i-offset] - offset local k = s local key, value local isError prev = k while k <= e do if map[k+offset] then if map[k+0.5+offset] == 1 then value = ExRT.F.TextToTable( str:sub(k+1,map[k+offset]-offset-1) ,map,k+offset) elseif map[k+0.5+offset] == 2 then value = str:sub(k + 1,map[k+offset]-offset-1):gsub("\\\"","\""):gsub("\\\\","\\") elseif map[k+0.5+offset] == 3 then value = str:sub(k,map[k+offset]-offset):gsub("^%[=*%[",""):gsub("%]=*%]$","") end k = map[k+offset] + 1 - offset else local b1 = string_byte(str, k) if b1 == 61 then --= if value then key = value value = nil else key = str:sub(prev, k-1):trim() if key:find("^%[") and key:find("%]$") then key = key:gsub("^%[",""):gsub("%]$","") if tonumber(key) then key = tonumber(key) end elseif key == "true" then key = true elseif key == "false" then key = false elseif tonumber(key) then key = tonumber(key) else key = key:match("[A-Za-z_][A-Za-z_0-9]*") end if not key then isError = true break end end prev = k + 1 elseif k == e and not value then value = str:sub(prev, k):trim() if value == "true" then value = true elseif value == "false" then value = false else value = tonumber(value) end end k = k + 1 end end if not isError then if not key then key = numKey numKey = numKey + 1 end res[key] = value end i = map[-i-offset] - offset end i = i + 1 end return res end function ExRT.F.CreateImportExportWindows() local function ImportOnUpdate(self, elapsed) self.tmr = self.tmr + elapsed if self.tmr >= 0.1 then self.tmr = 0 self:SetScript("OnUpdate",nil) local str = table.concat(self.buff):trim() self.parent:Hide() self.buff = {} self.buffPos = 0 if self.parent.ImportFunc then self.parent:ImportFunc(str) end end end local importWindow = ELib:Popup(ExRT.L.Import.." "..ExRT.L.ImportHelp):Size(650,100) importWindow.Edit = ELib:MultiEdit(importWindow):Point("TOP",0,-20):Size(640,75) importWindow:SetScript("OnHide",function(self) self.Edit:SetText("") end) importWindow:SetScript("OnShow",function(self) self.Edit.EditBox.buffPos = 0 self.Edit.EditBox.tmr = 0 self.Edit.EditBox.buff = {} self.Edit.EditBox:SetFocus() end) importWindow.Edit.EditBox:SetMaxBytes(1) importWindow.Edit.EditBox:SetScript("OnChar", function(self, c) self.buffPos = self.buffPos + 1 self.buff[self.buffPos] = c self:SetScript("OnUpdate",ImportOnUpdate) end) importWindow.Edit.EditBox.parent = importWindow local exportWindow = ELib:Popup(ExRT.L.Export):Size(650,50) exportWindow.Edit = ELib:Edit(exportWindow):Point("TOP",0,-20):Size(640,25) exportWindow:SetScript("OnHide",function(self) self.Edit:SetText("") end) exportWindow.Edit:SetScript("OnEditFocusGained", function(self) self:HighlightText() end) exportWindow.Edit:SetScript("OnMouseUp", function(self, button) self:HighlightText() if button == "RightButton" then self:GetParent():Hide() end end) exportWindow.Edit:SetScript("OnKeyUp", function(self, c) if (c == "c" or c == "C") and IsControlKeyDown() then self:GetParent():Hide() end end) function exportWindow:OnShow() self.Edit:SetFocus() end return importWindow, exportWindow end end -------------------> Data <-------------------- ExRT.GDB.ClassSpecializationIcons = { [62] = "Interface\\Icons\\Spell_Holy_MagicalSentry", [63] = "Interface\\Icons\\Spell_Fire_FireBolt02", [64] = "Interface\\Icons\\Spell_Frost_FrostBolt02", [65] = "Interface\\Icons\\Spell_Holy_HolyBolt", [66] = "Interface\\Icons\\Ability_Paladin_ShieldoftheTemplar", [70] = "Interface\\Icons\\Spell_Holy_AuraOfLight", [71] = "Interface\\Icons\\Ability_Warrior_SavageBlow", [72] = "Interface\\Icons\\Ability_Warrior_InnerRage", [73] = "Interface\\Icons\\Ability_Warrior_DefensiveStance", [102] = "Interface\\Icons\\Spell_Nature_StarFall", [103] = "Interface\\Icons\\Ability_Druid_CatForm", [104] = "Interface\\Icons\\Ability_Racial_BearForm", [105] = "Interface\\Icons\\Spell_Nature_HealingTouch", [250] = "Interface\\Icons\\Spell_Deathknight_BloodPresence", [251] = "Interface\\Icons\\Spell_Deathknight_FrostPresence", [252] = "Interface\\Icons\\Spell_Deathknight_UnholyPresence", [253] = "INTERFACE\\ICONS\\ability_hunter_bestialdiscipline", [254] = "Interface\\Icons\\Ability_Hunter_FocusedAim", [255] = "INTERFACE\\ICONS\\ability_hunter_camouflage", [256] = "Interface\\Icons\\Spell_Holy_PowerWordShield", [257] = "Interface\\Icons\\Spell_Holy_GuardianSpirit", [258] = "Interface\\Icons\\Spell_Shadow_ShadowWordPain", [259] = "Interface\\Icons\\Ability_Rogue_DeadlyBrew", [260] = "Interface\\Icons\\INV_Sword_30", [261] = "Interface\\Icons\\Ability_Stealth", [262] = "Interface\\Icons\\Spell_Nature_Lightning", [263] = "Interface\\Icons\\Spell_Shaman_ImprovedStormstrike", [264] = "Interface\\Icons\\Spell_Nature_MagicImmunity", [265] = "Interface\\Icons\\Spell_Shadow_DeathCoil", [266] = "Interface\\Icons\\Spell_Shadow_Metamorphosis", [267] = "Interface\\Icons\\Spell_Shadow_RainOfFire", [268] = "Interface\\Icons\\spell_monk_brewmaster_spec", [269] = "Interface\\Icons\\spell_monk_windwalker_spec", [270] = "Interface\\Icons\\spell_monk_mistweaver_spec", [577] = "Interface\\Icons\\ability_demonhunter_specdps", [581] = "Interface\\Icons\\ability_demonhunter_spectank", [1467] = "Interface\\Icons\\classicon_evoker_devastation", [1468] = "Interface\\Icons\\classicon_evoker_preservation", [1473] = "Interface\\Icons\\classicon_evoker_augmentation", } ExRT.GDB.ClassList = { "WARRIOR", "PALADIN", "HUNTER", "ROGUE", "PRIEST", "DEATHKNIGHT", "SHAMAN", "MAGE", "WARLOCK", "MONK", "DRUID", "DEMONHUNTER", "EVOKER", } ExRT.GDB.ClassSpecializationList = { ["WARRIOR"] = {71, 72, 73}, ["PALADIN"] = {65, 66, 70}, ["HUNTER"] = {253, 254, 255}, ["ROGUE"] = {259, 260, 261}, ["PRIEST"] = {256, 257, 258}, ["DEATHKNIGHT"] = {250, 251, 252}, ["SHAMAN"] = {262, 263, 264}, ["MAGE"] = {62, 63, 64}, ["WARLOCK"] = {265, 266, 267}, ["MONK"] = {268, 269, 270}, ["DRUID"] = {102, 103, 104, 105}, ["DEMONHUNTER"] = {577, 581}, ["EVOKER"] = {1467, 1468, 1473}, } ExRT.GDB.ClassArmorType = { WARRIOR="PLATE", PALADIN="PLATE", HUNTER="MAIL", ROGUE="LEATHER", PRIEST="CLOTH", DEATHKNIGHT="PLATE", SHAMAN="MAIL", MAGE="CLOTH", WARLOCK="CLOTH", MONK="LEATHER", DRUID="LEATHER", DEMONHUNTER="LEATHER", EVOKER="MAIL", } ExRT.GDB.ClassSpecializationRole = { [62] = 'RANGE', [63] = 'RANGE', [64] = 'RANGE', [65] = 'HEAL', [66] = 'TANK', [70] = 'MELEE', [71] = 'MELEE', [72] = 'MELEE', [73] = 'TANK', [102] = 'RANGE', [103] = 'MELEE', [104] = 'TANK', [105] = 'HEAL', [250] = 'TANK', [251] = 'MELEE', [252] = 'MELEE', [253] = 'RANGE', [254] = 'RANGE', [255] = 'MELEE', [256] = 'HEAL', [257] = 'HEAL', [258] = 'RANGE', [259] = 'MELEE', [260] = 'MELEE', [261] = 'MELEE', [262] = 'RANGE', [263] = 'MELEE', [264] = 'HEAL', [265] = 'RANGE', [266] = 'RANGE', [267] = 'RANGE', [268] = 'TANK', [269] = 'MELEE', [270] = 'HEAL', [577] = 'MELEE', [581] = 'TANK', [1467] = 'RANGE', [1468] = 'HEAL', [1473] = 'RANGE', } ExRT.GDB.ClassID = { WARRIOR=1, PALADIN=2, HUNTER=3, ROGUE=4, PRIEST=5, DEATHKNIGHT=6, SHAMAN=7, MAGE=8, WARLOCK=9, MONK=10, DRUID=11, DEMONHUNTER=12, EVOKER=13, } if ExRT.isClassic then --GetClassInfo local classLocalizateEngine = {} FillLocalizedClassList(classLocalizateEngine) ExRT.Classic.GetClassInfo = function(id) return classLocalizateEngine[ ExRT.GDB.ClassList[id] ] or "unk" end --GetSpecializationInfoByID local specializationData = { [62] = {name="Arcane",class=8,role="DAMAGER",desc="Manipulates raw Arcane magic, destroying enemies with overwhelming power.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=135932}, [63] = {name="Fire",class=8,role="DAMAGER",desc="Focuses the pure essence of Fire magic, assaulting enemies with combustive flames.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=135810}, [64] = {name="Frost",class=8,role="DAMAGER",desc="Freezes enemies in their tracks and shatters them with Frost magic.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=135846}, [65] = {name="Holy",class=2,role="HEALER",desc="Invokes the power of the Light to heal and protect allies and vanquish evil from the darkest corners of the world.|n|nPreferred Weapon: Sword, Mace, and Shield",icon=135920}, [66] = {name="Protection",class=2,role="TANK",desc="Uses Holy magic to shield $Ghimself:herself; and defend allies from attackers.|n|nPreferred Weapon: Sword, Mace, Axe, and Shield",icon=236264}, [70] = {name="Retribution",class=2,role="DAMAGER",desc="A righteous crusader who judges and punishes opponents with weapons and Holy magic.|n|nPreferred Weapon: Two-Handed Sword, Mace, Axe",icon=135873}, [71] = {name="Arms",class=1,role="DAMAGER",desc="A battle-hardened master of weapons, using mobility and overpowering attacks to strike $Ghis:her; opponents down.|n|nPreferred Weapon: Two-Handed Axe, Mace, Sword",icon=132355}, [72] = {name="Fury",class=1,role="DAMAGER",desc="A furious berserker unleashing a flurry of attacks to carve $Ghis:her; opponents to pieces.|n|nPreferred Weapons: Dual Two-Handed Axes, Maces, Swords",icon=132347}, [73] = {name="Protection",class=1,role="TANK",desc="A stalwart protector who uses a shield to safeguard $Ghimself:herself; and $Ghis:her; allies.|n|nPreferred Weapon: Axe, Mace, Sword, and Shield",icon=132341}, [74] = {name="Ferocity",class=0,role="DAMAGER",desc="Driven by a frenzied persistence to pursue prey, these beasts stop at nothing to achieve victory; even death is temporary for these predators.",icon=236159}, [79] = {name="Cunning",class=0,role="DAMAGER",desc="",icon=132150}, [81] = {name="Tenacity",class=0,role="TANK",desc="",icon=132121}, [102] = {name="Balance",class=11,role="DAMAGER",desc="Can shapeshift into a powerful Moonkin, balancing the power of Arcane and Nature magic to destroy enemies.|n|nPreferred Weapon: Staff, Dagger, Mace",icon=136096}, [103] = {name="Feral",class=11,role="DAMAGER",desc="Takes on the form of a great cat to deal damage with bleeds and bites.|n|nPreferred Weapon: Staff, Polearm",icon=132115}, [104] = {name="Guardian",class=11,role="TANK",desc="Takes on the form of a mighty bear to absorb damage and protect allies.|n|nPreferred Weapon: Staff, Polearm",icon=132276}, [105] = {name="Restoration",class=11,role="HEALER",desc="Channels powerful Nature magic to regenerate and revitalize allies.|n|nPreferred Weapon: Staff, Dagger, Mace",icon=136041}, [250] = {name="Blood",class=6,role="TANK",desc="A dark guardian who manipulates and corrupts life energy to sustain $Ghimself:herself; in the face of an enemy onslaught.|n|nPreferred Weapon: Two-Handed Axe, Mace, Sword",icon=135770}, [251] = {name="Frost",class=6,role="DAMAGER",desc="An icy harbinger of doom, channeling runic power and delivering vicious weapon strikes.|n|nPreferred Weapons: Dual Axes, Maces, Swords",icon=135773}, [252] = {name="Unholy",class=6,role="DAMAGER",desc="A master of death and decay, spreading infection and controlling undead minions to do $Ghis:her; bidding.|n|nPreferred Weapon: Two-Handed Axe, Mace, Sword",icon=135775}, [253] = {name="Beast Mastery",class=3,role="DAMAGER",desc="A master of the wild who can tame a wide variety of beasts to assist $Ghim:her; in combat.|n|nPreferred Weapon: Bow, Crossbow, Gun",icon=461112}, [254] = {name="Marksmanship",class=3,role="DAMAGER",desc="A master sharpshooter who excels in bringing down enemies from afar.|n|nPreferred Weapon: Bow, Crossbow, Gun",icon=236179}, [255] = {name="Survival",class=3,role="DAMAGER",desc="An adaptive ranger who favors using explosives, animal venom, and coordinated attacks with their bonded beast.|n|nPreferred Weapon: Polearm, Staff",icon=461113}, [256] = {name="Discipline",class=5,role="HEALER",desc="Uses magic to shield allies from taking damage as well as heal their wounds.|n|nPreferred Weapon: Staff, Wand, Dagger, Mace",icon=135940}, [257] = {name="Holy",class=5,role="HEALER",desc="A versatile healer who can reverse damage on individuals or groups and even heal from beyond the grave.|n|nPreferred Weapon: Staff, Wand, Dagger, Mace",icon=237542}, [258] = {name="Shadow",class=5,role="DAMAGER",desc="Uses sinister Shadow magic and terrifying Void magic to eradicate enemies.|n|nPreferred Weapon: Staff, Wand, Dagger, Mace",icon=136207}, [259] = {name="Assassination",class=4,role="DAMAGER",desc="A deadly master of poisons who dispatches victims with vicious dagger strikes.|n|nPreferred Weapons: Daggers",icon=236270}, [260] = {name="Outlaw",class=4,role="DAMAGER",desc="A ruthless fugitive who uses agility and guile to stand toe-to-toe with enemies.|n|nPreferred Weapons: Axes, Maces, Swords, Fist Weapons",icon=236286}, [261] = {name="Subtlety",class=4,role="DAMAGER",desc="A dark stalker who leaps from the shadows to ambush $Ghis:her; unsuspecting prey.|n|nPreferred Weapons: Daggers",icon=132320}, [262] = {name="Elemental",class=7,role="DAMAGER",desc="A spellcaster who harnesses the destructive forces of nature and the elements.|n|nPreferred Weapon: Mace, Dagger, and Shield",icon=136048}, [263] = {name="Enhancement",class=7,role="DAMAGER",desc="A totemic warrior who strikes foes with weapons imbued with elemental power.|n|nPreferred Weapons: Dual Axes, Maces, Fist Weapons",icon=237581}, [264] = {name="Restoration",class=7,role="HEALER",desc="A healer who calls upon ancestral spirits and the cleansing power of water to mend allies' wounds.|n|nPreferred Weapon: Mace, Dagger, and Shield",icon=136052}, [265] = {name="Affliction",class=9,role="DAMAGER",desc="A master of shadow magic who specializes in drains and damage-over-time spells.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=136145}, [266] = {name="Demonology",class=9,role="DAMAGER",desc="A commander of demons who twists the souls of $Ghis:her; army into devastating power.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=136172}, [267] = {name="Destruction",class=9,role="DAMAGER",desc="A master of chaos who calls down fire to burn and demolish enemies.|n|nPreferred Weapon: Staff, Wand, Dagger, Sword",icon=136186}, [268] = {name="Brewmaster",class=10,role="TANK",desc="A sturdy brawler who uses unpredictable movement and mystical brews to avoid damage and protect allies.|n|nPreferred Weapon: Staff, Polearm",icon=608951}, [269] = {name="Windwalker",class=10,role="DAMAGER",desc="A martial artist without peer who pummels foes with hands and fists.|n|nPreferred Weapons: Fist Weapons, Axes, Maces, Swords",icon=608953}, [270] = {name="Mistweaver",class=10,role="HEALER",desc="A healer who masters the mysterious art of manipulating life energies aided by the wisdom of the Jade Serpent.|n|nPreferred Weapon: Staff, Mace, Sword",icon=608952}, [535] = {name="Ferocity",class=0,role="DAMAGER",desc="Driven by a rabid persistence to pursue prey, these carnivorous beasts stop at nothing to achieve victory; even death is temporary for these predators.",icon=236159}, [536] = {name="Cunning",class=0,role="DAMAGER",desc="",icon=132150}, [537] = {name="Tenacity",class=0,role="TANK",desc="",icon=132121}, [577] = {name="Havoc",class=12,role="DAMAGER",desc="A brooding master of warglaives and the destructive power of Fel magic.|n|nPreferred Weapons: Warglaives, Swords, Axes, Fist Weapons",icon=1247264}, [581] = {name="Vengeance",class=12,role="TANK",desc="Embraces the demon within to incinerate enemies and protect their allies.|n|nPreferred Weapons: Warglaives, Swords, Axes, Fist Weapons",icon=1247265}, [1467] = {name="Devastation",class=13,role="DAMAGER",desc="Releases innate power as chaotic Red flames or focused Blue magic to bathe the battlefield in destruction. Preferred Weapon: Staff, Sword, Dagger, Mace",icon=4511811}, [1468] = {name="Preservation",class=13,role="HEALER",desc="Calls upon the Emerald Dream to rejuvenate life, and the Bronze sands of time to prevent harm. Preferred Weapon: Staff, Sword, Dagger, Mace",icon=4511812}, [1473] = {name="Augmentation",class=13,role="DAMAGER",desc="Imbues allies with the might of the Black dragons, and bends time & fate in their favor with Bronze magic. Preferred Weapon: Staff, Sword, Dagger, Mace",icon=5198700}, } ExRT.Classic.GetSpecializationInfoByID = function(id) local data = specializationData[id] or ExRT.NULL return id, data.name, data.desc, data.icon, data.role, ExRT.GDB.ClassList[data.class] end end ExRT.GDB.EncountersList = { {350,652,653,654,655,656,657,658,659,660,661,662}, {331,651}, {330,649,650}, {332,623,624,625,626,627,628}, {334,730,731,732,733}, {329,618,619,620,621,622}, {335,724,725,726,727,728,729}, {129,519,520,2009,522,521,2010,2011,524,523,525,526,2012,527}, {130,2002,2003,2004,2005}, {132,1969,1966,1967,1989,1968}, {133,2026,2024,2025}, {136,2030,2027,2029,2028}, {138,1987,1985,1984,1986}, {140,1994,1996,1995,1998}, {141,1094}, {142,528,2013,530,529,2014,2015,532,531,533,534,2016,535}, {162,1107,1110,1116,1117,1112,1115,1113,1109,1121,1118,1111,1108,1120,1119,1114}, {155,1093,1092,1091,1090}, {147,1132,1136,1139,1142,1140,1137,1131,1135,1141,1164,1165,1166,1133,1138,1134,1143,1130}, {153,1978,1983,1980,1988,1981}, {156,1126,1127,1128,1129}, {157,1971,1972,1973}, {160,1974,1976,1977,1975}, {168,2018,2019,2020}, {171,2022,2023,2021}, {172,1088,1087,1086,1089,1085}, {183,2006,2007}, {184,1999,2001,2000}, {185,1992,1993,1990}, {186,1101,1100,1099,1096,1104,1097,1102,1095,1103,1098,1105,1106}, {200,1147,1149,1148,1150}, {213,1443,1444,1445,1446}, {219,593,594,595,596,597,598,599,600}, {220,492,488,486,487,490,491,493}, {221,1667,1668,1669,1675,1676,1670,1671,1672}, {225,1144,1145,1146}, {226,379,378,380,381,382}, {230,547,548,549,551,552,553,554,1887}, {232,663,664,665,666,667,668,669,670,671,672}, {233,785,784,786,787,788,789,790,791,792,793}, {234,343,344,345,346,350,347,348,349,361,362,363,364,365,366,367,368}, {242,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245}, {246,1935,1936,1937,1938}, {247,718,719,720,721,722,723}, {248,1084}, {250,267,268,269,270,271,272,274,273,275}, {256,1889,1890}, {258,1902,1903,1904}, {260,1908,1909,1910,1911}, {261,1922,1923,1924}, {262,1945,1946,1947,1948}, {263,1942,1943,1944}, {265,1939,1940,1941}, {266,1925,1926,1927,1928,1929}, {267,1930,1931,1932,1933,1934}, {269,1913,1914,1915,1916}, {272,1899,1900,250,1901}, {273,1919,1920,1921}, {274,1905,1906,1907}, {277,1052,1053,1054,1055}, {279,585,586,587,588,589,590,591,592}, {280,422,423,427,424,425,426,428,429}, {282,1033,1250,1332}, {283,1040,1038,1039,1037,1036}, {285,1027,1024,1022,1023,1025,1026}, {287,610,611,612,613,614,615,616,617}, {291,1064,1065,1063,1062,1060,1081}, {293,1051,1050,1048,1049}, {294,1030,1032,1028,1029,1082,1083}, {297,1080,1076,1075,1077,1074,1079,1078}, {300,1662,1663,1664,1665,1666}, {301,1656,438,1659,1660,1661}, {302,444,446,447,448,449,450}, {306,451,452,453,454,455,456,457,458,459,460,461,462,463}, {310,1069,1070,1071,1073,1072}, {317,473,474,476,475,477,478,472,479,480,481,482,483,484,1885}, {319,709,710,711,712,713,714,715,716,717}, {322,1045,1044,1046,1047}, {324,1056,1059,1058,1057}, {325,1043,1041,1042}, {328,1035,1034}, {333,1189,1190,1191,1192,1193,1194}, {337,1178,1179,1188,1180,1181,1182}, {339,601,602,603,604,605,606,607,608,609}, {347,1891,1892,1893}, {348,1894,1895,1897,1898}, {367,1197,1204,1205,1206,1200,1185,1203}, {398,1272,1273,1274}, {399,1337,1340,1339}, {401,1881,1882,1883,1884,1271}, {409,1292,1294,1295,1296,1297,1298,1291,1299}, {429,1418,1417,1416,1439}, {431,1422,1421,1420}, {435,1423,1424,1425}, {437,1397,1405,1406,1419}, {439,1412,1413,1414}, {443,1303,1304,1305,1306}, {453,1442,1509,1510,1441}, {456,1409,1505,1506,1431}, {457,1465,1502,1447,1464}, {471,1395,1390,1434,1436,1500,1407}, {474,1507,1504,1463,1498,1499,1501}, {476,1426,1427,1428,1429,1430}, {508,1577,1575,1570,1565,1578,1573,1572,1574,1576,1559,1560,1579,1580,1581}, {554,1563,1564,1571,1587}, {556,1602,1598,1624,1604,1622,1600,1606,1603,1595,1594,1599,1601,1593,1623,1605}, {573,1655,1653,1652,1654}, {574,1677,1688,1679,1682}, {593,1686,1685,1678,1714}, {595,1749,1748,1750,1754}, {601,1698,1699,1700,1701}, {606,1715,1732,1736}, {616,1761,1758,1759,1760,1762}, {620,1746,1757,1751,1752,1756}, {624,1755,1770,1801}, {703,1805,1806,1807,1808,1809}, {708,1822,1823,1824}, {710,1815,1850,1816,1817,1818}, {713,1810,1811,1812,1813,1814}, {731,1790,1791,1792,1793}, {732,1845,1846,1847,1848,1851,1852,1855,1856}, {733,1836,1837,1838,1839}, {749,1825,1826,1827,1828,1829}, {751,1832,1833,1834,1835}, {761,1868,1869,1870}, {790,1879,1880,1888,1917,1950,1951,1952,1953,1949}, {809,1957,1954,1961,1960,1964,1965,1959,2017,2031}, {845,2055,2057,2039,2053}, {903,2065,2066,2067,2068}, {934,2084,2085,2086,2087}, {936,2093,2094,2095,2096}, {974,2101,2102,2103,2104}, {1010,2105,2106,2107,2108}, {1015,2113,2114,2115,2116,2117}, {1041,2111,2118,2112,2123}, {1162,2098,2097,2109,2099,2100}, {1038,2124,2125,2126,2127}, {1039,2130,2131,2132,2133}, {1004,2139,2142,2140,2143}, {1490,2290,2292,2312,2291,2257,2258,2259,2260}, {1666,2387,2388,2389,2390}, {1674,2382,2384,2385,2386}, {1669,2397,2392,2393}, {1663,2401,2380,2403,2381}, {1693,2357,2356,2358,2359}, {1683,2391,2365,2366,2364,2404}, {1679,2395,2394,2400,2396}, {1675,2360,2361,2362,2363}, {2096,2570,2568,2567,2569}, {2071,2555,2556,2557,2558,2559}, {2093,2637,2636,2581,2580}, {2080,2613,2612,2610,2611}, {2097,2562,2563,2564,2565}, {2095,2609,2606,2623}, {2073,2582,2585,2583,2584}, {2082,2615,2616,2617,2618}, {610,1721,1706,1720,1722,1719,1723,1705},--HM {596,1696,1691,1693,1694,1689,1692,1690,1713,1695,1704},--BF {661,1778,1785,1787,1798,1786,1783,1788,1794,1777,1800,1784,1795,1799},--HFC {777,1853,1841,1873,1854,1876,1877,1864},--EN {806,1958,1962,2008},--tov {764,1849,1865,1867,1871,1862,1886,1842,1863,1872,1866},--nighthold {850,2032,2048,2036,2037,2050,2054,2052,2038,2051},--tos {909,2076,2074,2064,2070,2075,2082,2069,2088,2073,2063,2092},--antorus {1148,2144,2141,2136,2128,2134,2145,2135,2122},--uldir {1358,2265,2263,2284,2266,2285,2271,2268,2272,2276,2280,2281}, --bfd {1345,2269,2273}, --storms {1512,2298,2305,2289,2304,2303,2311,2293,2299}, --ethernal place {1582,2329,2327,2334,2328,2336,2333,2331,2335,2343,2345,2337,2344}, --nyalotha {1735,2398,2418,2402,2383,2405,2406,2412,2399,2417,2407}, --castle Nathria {1998,2423,2433,2429,2432,2434,2430,2436,2431,2422,2435}, --sod {2047,2512,2540,2553,2544,2539,2542,2529,2546,2543,2549,2537}, --sfo {2119,2587,2639,2590,2592,2635,2605,2614,2607}, --voti {2166,2688,2682,2687,2693,2680,2689,2683,2684,2685}, --a {"10.2",2232,2820,2709,2737,2731,2728,2708,2824,2786,2677}, --d } local ACTUAL_RAID = 1735 local ACTUAL_DUNG = 1666 if UnitLevel'player' > 60 then ACTUAL_DUNG = 2096 ACTUAL_RAID = 2119 end do local function StringVerToNumber(ver) local i = 0 local r = 0 for n in ver:gmatch("%d+") do r = r + tonumber(n) * (10 ^ i) i = i - 2 end return r end local ver_now = StringVerToNumber(GetBuildInfo()) for i=#ExRT.GDB.EncountersList,1,-1 do local t = ExRT.GDB.EncountersList[i] if type(t[1]) == "string" then if StringVerToNumber(t[1]) > ver_now then tremove(ExRT.GDB.EncountersList, i) else tremove(t, 1) end elseif t[1] == ACTUAL_DUNG then --stop loop for old data break end end end function ExRT.F.GetEncountersList(onlyRaid,onlyActual,reverse) local new = {} local isActual,isRaid for _,v in ipairs(ExRT.GDB.EncountersList) do if v[1] == 610 then isRaid = true isActual = false elseif v[1] == ACTUAL_DUNG then isActual = true elseif v[1] == ACTUAL_RAID then isActual = true end if (not onlyActual or isActual) and (not onlyRaid or isRaid) then new[#new+1] = v end end if reverse then local len = #new for i=1,floor(len/2) do new[i], new[len+1-i] = new[len+1-i], new[i] end end return new end