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 local GetItemInfo, GetItemInfoInstant = C_Item and C_Item.GetItemInfo or GetItemInfo, C_Item and C_Item.GetItemInfoInstant or GetItemInfoInstant local GetSpecialization = GetSpecialization local GetSpecializationInfo = C_SpecializationInfo and C_SpecializationInfo.GetSpecializationInfo or GetSpecializationInfo if not GetSpecialization and ExRT.isClassic then GetSpecialization = function() local n,m = 1,1 for spec=1,3 do local selectedNum = 0 for talPos=1,22 do local name, iconTexture, tier, column, rank, maxRank, isExceptional, available = GetTalentInfo(spec, talPos) if name and maxRank > 0 and rank > 0 then selectedNum = selectedNum + 1 end end if selectedNum > m then n = spec m = selectedNum end end return n end end 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 if GetMouseFoci then obj = GetMouseFoci()[1] else obj = GetMouseFocus() end 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 local GetSpellLink = C_Spell and C_Spell.GetSpellLink or GetSpellLink 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 if type(t1) ~= "table" or type(t2) ~= "table" then return c, t end 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.table_random(t) local keys_num = 0 for k,v in pairs(t) do keys_num = keys_num + 1 end if keys_num == 0 then return end local key_need = math.random(1,keys_num) keys_num = 0 for k,v in pairs(t) do keys_num = keys_num + 1 if keys_num == key_need then return v end end end local sort_f = function(a,b) return a[1]" or tostring(v)) end local str = "{"..table.concat(res,",").."}" return str end function ExRT.F.table_keys_to_string(t,sep) local str = "" for k in pairs(t) do str = str .. (str ~= "" and (sep or " ") or "") .. k end return str end function ExRT.F.tohex(num,size) return format("%0"..(size or "1").."X",num) end function ExRT.F.UnitInGuild(unit) local sunit = 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) == sunit then return true end end if UnitIsInMyGuild(unit) or UnitIsInMyGuild(sunit) then return true 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 name and 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 name and 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(checkNotInGroup) local role = UnitGroupRolesAssigned('player') if (not role or role == "NONE") and checkNotInGroup and GetSpecializationInfo then role = select(5,GetSpecializationInfo(GetSpecialization() or 0)) end 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 if ExRT.isClassic and not ExRT.isWoD then 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 = false 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 = false end if isMelee then return role, "MDD" else return role, "RDD" end 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, geterrorhandler(), ...) 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 do local alertWindow = nil local alertFunc = nil local alertArg1 = nil local ce = 0 local function CreateEdit() ce = ce + 1 local e = alertWindow.EditBox[ce] if not e then e = ELib:Edit(alertWindow):Size(400,16):Point("TOPLEFT",90,-20-(ce-1)*20) alertWindow.EditBox[ce] = e e:SetScript("OnEnterPressed",function (self) self:GetParent().OK:Click("LeftButton") end) end e:Show() return alertWindow.EditBox[ce] end local function CreateWindow() alertWindow = ExRT.lib:Popup():Size(500,65) alertWindow:SetFrameStrata("FULLSCREEN_DIALOG") alertWindow.EditBox = {} alertWindow.OK = ExRT.lib:Button(alertWindow,ACCEPT):Size(130,20):Point("BOTTOM",0,3):OnClick(function (self) alertWindow:Hide() local r = {} for i=1,#alertWindow.EditBox do local e = alertWindow.EditBox[i] local input = e:GetText() r[i] = input end alertFunc(r) end) end function ExRT.F.ShowInput2(text,func,...) if not alertWindow then CreateWindow() end ce = 0 alertWindow.title:SetText(text) for i=1,select("#",...) do local opt = select(i,...) local e = CreateEdit() e:LeftText(type(opt) == "table" and opt.text or opt) e:SetScript("OnTextChanged",type(opt) == "table" and opt.funcOnEdit or nil) e:Tooltip(type(opt) == "table" and opt.tip or nil) e:SetText(type(opt) == "table" and opt.defText or "") if type(opt) == "table" and opt.onlyNum then e:SetNumeric(true) else e:SetNumeric(false) end end alertFunc = func alertWindow:ClearAllPoints() alertWindow:SetPoint("CENTER",UIParent,0,0) alertWindow:SetHeight(45+20*ce) alertWindow:Show() alertWindow.EditBox[1]:SetFocus() 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 ---------------> Profiling Window <--------------- do local profilingWindow function ExRT.F:ProfilingWindow() if not profilingWindow then profilingWindow = ExRT.lib:Popup("Profiling"):Size(700,300) local self = profilingWindow profilingWindow.decorationLine = ELib:DecorationLine(self,true,"BACKGROUND",-5):Point("TOPLEFT",self,0,-15):Point("BOTTOMRIGHT",self,"TOPRIGHT",0,-35) profilingWindow.list = ELib:ScrollTableList(self,300,80,80,80,80,0):Size(700,300-35-25):Point("TOP",0,-35):FontSize(11):HideBorders() profilingWindow.headertext1 = ELib:Text(self," Event"):Point("LEFT",self.list,2,0):Point("TOP",self.decorationLine,0,0):Size(300,20):Left():Middle():Color():Shadow() profilingWindow.headertext2 = ELib:Text(self,"Total, ms"):Point("LEFT",self.headertext1,"RIGHT",0,0):Point("TOP",self.decorationLine,0,0):Size(80,20):Left():Middle():Color():Shadow() profilingWindow.headertext3 = ELib:Text(self,"Count"):Point("LEFT",self.headertext2,"RIGHT",0,0):Point("TOP",self.decorationLine,0,0):Size(80,20):Left():Middle():Color():Shadow() profilingWindow.headertext4 = ELib:Text(self,"Peak"):Point("LEFT",self.headertext3,"RIGHT",0,0):Point("TOP",self.decorationLine,0,0):Size(80,20):Left():Middle():Color():Shadow() profilingWindow.headertext5 = ELib:Text(self,"Per 1, ms"):Point("LEFT",self.headertext4,"RIGHT",0,0):Point("TOP",self.decorationLine,0,0):Size(80,20):Left():Middle():Color():Shadow() profilingWindow.headertext6 = ELib:Text(self,"Per sec, ms"):Point("LEFT",self.headertext5,"RIGHT",0,0):Point("TOP",self.decorationLine,0,0):Size(80,20):Left():Middle():Color():Shadow() local t = 0 profilingWindow:SetScript("OnUpdate",function(self,elapsed) t = t + elapsed if t < 0.5 then return end t = t % 0.5 profilingWindow.nextBoss:SetEnabled(not ExRT.F:IsProfilingBoss()) if not ExRT.Profiling.Start then return end local r,total = {},{"Total",0,"","","",0} local now = (ExRT.Profiling.End or debugprofilestop()) - ExRT.Profiling.Start for event in pairs(ExRT.Profiling.T) do local t = ExRT.Profiling.T[event] local c = ExRT.Profiling.C[event] local m = ExRT.Profiling.M[event] r[#r+1] = {event,format("%.1f",t),c,format("%.1f",m),format("%.1f",t/c),format("%.1f",t/now*1000),sort=t} total[2] = total[2] + t end sort(r,function(a,b) return a.sort>b.sort end) total[6] = format("%.1f",total[2] / now*1000) total[2] = format("%.1f",total[2]) tinsert(r,1,total) if ExRT.F:IsProfilingBoss() and not ExRT.Profiling.BossStarted then wipe(r) end profilingWindow.list.L = r profilingWindow.list:Update() profilingWindow.startBut:SetText(ExRT.Profiling.Enabled and "Stop profiling" or "Start profiling") end) profilingWindow.startBut = ELib:Button(profilingWindow,"Start profiling"):Size(200,20):Point("BOTTOMLEFT",2,2):OnClick(function() ExRT.F:StartStopProfiling() t = 0.5 end) profilingWindow.exportBut = ELib:Button(profilingWindow,"Export to string"):Size(200,20):Point("LEFT",profilingWindow.startBut,"RIGHT",2,0):OnClick(function() ExRT.F:GetProfiling() end) profilingWindow.nextBoss = ELib:Button(profilingWindow,"Next boss encounter"):Size(200,20):Point("LEFT",profilingWindow.exportBut,"RIGHT",2,0):OnClick(function() ExRT.F:StartProfilingBoss() end) end profilingWindow:NewPoint("TOPLEFT",UIParent,5,-5) profilingWindow: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.isCata and not ExRT.isMoP then ExRT.GDB.ClassSpecializationList = { WARRIOR = {746,815,845}, --Arms,Fury,Protection PALADIN = {831,839,855}, --Holy,Protection,Retribution HUNTER = {811,807,809}, --Beast Mastery,Marksmanship,Survival ROGUE = {182,181,183}, --Assassination,Combat,Subtlety PRIEST = {760,813,795}, --Discipline,Holy,Shadow DEATHKNIGHT = {398,399,400}, --Blood,Frost,Unholy SHAMAN = {261,263,262}, --Elemental,Enhancement,Restoration MAGE = {799,851,823}, --Arcane,Fire,Frost WARLOCK = {871,867,865}, --Affliction,Demonology,Destruction DRUID = {752,750,748}, --Balance,Feral Combat,Restoration } ExRT.GDB.ClassSpecializationIcons = { [181] = 132090, [182] = 132292, [183] = 132320, [261] = 136048, [262] = 136052, [263] = 136051, [398] = 135770, [399] = 135773, [400] = 135775, [746] = 132355, [748] = 136041, [750] = 132276, [752] = 136096, [760] = 135940, [795] = 136207, [799] = 135932, [807] = 236179, [809] = 461113, [811] = 461112, [813] = 237542, [815] = 132347, [823] = 135846, [831] = 135920, [839] = 236264, [845] = 132341, [851] = 135810, [855] = 135873, [865] = 136186, [867] = 136172, [871] = 136145, } ExRT.GDB.ClassSpecializationRole = { [181] = "MELEE", [182] = "MELEE", [183] = "MELEE", [261] = "RANGE", [262] = "HEAL", [263] = "MELEE", [398] = "TANK", [399] = "MELEE", [400] = "MELEE", [746] = "MELEE", [748] = "HEAL", [750] = "MELEE", [752] = "RANGE", [760] = "HEAL", [795] = "RANGE", [799] = "RANGE", [807] = "RANGE", [809] = "RANGE", [811] = "RANGE", [813] = "HEAL", [815] = "MELEE", [823] = "RANGE", [831] = "HEAL", [839] = "TANK", [845] = "TANK", [851] = "RANGE", [855] = "MELEE", [865] = "RANGE", [867] = "RANGE", [871] = "RANGE", } end if ExRT.isClassic then --GetClassInfo local classLocalizateEngine = {} if LocalizedClassList then local classList = LocalizedClassList() MergeTable(classLocalizateEngine, classList) elseif FillLocalizedClassList then FillLocalizedClassList(classLocalizateEngine) end 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.JournalInstance = { {0,64,238,63,240,234,227,239,231,237,233,241,229,228,236,232,226,230,311,316,246,0,741,742,743,744}, --Classic {1,255,259,256,248,261,262,260,254,257,258,253,252,250,247,251,249,0,745,750,747,748,749,751,746,752}, --Burning Crusade {2,285,286,281,282,279,277,273,272,275,274,283,271,280,284,278,276,0,760,754,759,755,756,753,758,757,761}, --Wrath of the Lich King {3,64,63,77,65,70,66,68,71,67,69,76,184,185,186,0,73,72,78,74,75,187}, --Cataclysm {4,312,313,302,303,321,311,316,246,324,0,322,320,317,330,362,369}, --Mists of Pandaria {5,385,537,547,558,536,476,556,559,0,457,477,557,669}, --Warlords of Draenor {6,716,767,762,721,727,707,740,726,777,800,860,900,945,0,768,822,959,786,861,875,946}, --Legion {7,1012,1001,1041,968,1002,1023,1022,1021,1036,1030,1178,0,1028,1031,1176,1177,1179,1180}, --Battle for Azeroth {8,1189,1186,1182,1185,1183,1184,1188,1187,1194,0,1190,1193,1195,1192}, --Shadowlands {9,1197,1203,1198,1199,1196,1202,1201,1204,1209,0,1200,1207,1208,1205}, --Dragonflight {-1,65,68,313,537,556,767,762,721,740,800,1001,968,1022,1021,1197,1203,1198,1199,1196,1202,1201,1204,1209,0,1200,1207,1208,1205,n=EXPANSION_NAME9,s=4}, --Current Season {-1,1270,1271,1274,1269,1182,1184,1023,71,n=EXPANSION_NAME10,s=1}, --Current Season {10,1268,1267,1210,1269,1271,1272,1270,1274,1298,1303,0,1273,1296,1278,1302}, --The War Within } --/run local s="" for i=1,100 do local id=EJ_GetInstanceByIndex(i,false) if not id then break end s=s..id.."," end GExRT.F:Export2(s) ExRT.GDB.MapIDToJournalInstance = { [2830]=1303,[2810]=1302,[2792]=1301,[2774]=1278,[2773]=1298,[2769]=1296,[2669]=1274,[2662]=1270,[2661]=1272,[2660]=1271,[2657]=1273,[2652]=1269,[2651]=1210,[2649]=1267,[2648]=1268,[2579]=1209,[2574]=1205, [2569]=1208,[2559]=1192,[2549]=1207,[2527]=1204,[2526]=1201,[2522]=1200,[2521]=1202,[2520]=1196,[2519]=1199,[2516]=1198,[2515]=1203,[2481]=1195,[2451]=1197,[2450]=1193,[2441]=1194,[2296]=1190,[2293]=1187, [2291]=1188,[2290]=1184,[2289]=1183,[2287]=1185,[2286]=1182,[2285]=1186,[2284]=1189,[2217]=1180,[2164]=1179,[2097]=1178,[2096]=1177,[2070]=1176,[1877]=1030,[1864]=1036,[1862]=1021,[1861]=1031,[1861]=1031, [1841]=1022,[1822]=1023,[1771]=1002,[1763]=968,[1762]=1041,[1754]=1001,[1753]=945,[1712]=946,[1677]=900,[1676]=875,[1651]=860,[1648]=861,[1594]=1012,[1571]=800,[1544]=777,[1530]=786,[1520]=959,[1520]=959, [1520]=959,[1516]=726,[1501]=740,[1493]=707,[1492]=727,[1477]=721,[1466]=762,[1458]=767,[1456]=716,[1448]=669,[1358]=559,[1279]=556,[1228]=557,[1228]=557,[1209]=476,[1208]=536,[1205]=457,[1195]=558,[1182]=547, [1176]=537,[1175]=385,[1136]=369,[1098]=362,[1011]=324,[1009]=330,[1008]=317,[1007]=246,[1004]=316,[1001]=311,[996]=322,[996]=322,[994]=321,[967]=187,[962]=303,[961]=302,[960]=313,[959]=312,[940]=186,[939]=185, [938]=184,[859]=76,[757]=75,[755]=69,[754]=74,[725]=67,[724]=761,[720]=78,[671]=72,[670]=71,[669]=73,[668]=276,[658]=278,[657]=68,[650]=284,[649]=757,[645]=66,[644]=70,[643]=65,[632]=280,[631]=758,[624]=753, [619]=271,[616]=756,[615]=755,[608]=283,[604]=274,[603]=759,[602]=275,[601]=272,[600]=273,[599]=277,[595]=279,[585]=249,[580]=752,[578]=282,[576]=281,[575]=286,[574]=285,[568]=77,[565]=746,[564]=751,[560]=251, [558]=247,[557]=250,[556]=252,[555]=253,[554]=258,[553]=257,[552]=254,[550]=749,[548]=748,[547]=260,[546]=262,[545]=261,[544]=747,[543]=248,[542]=256,[540]=259,[534]=750,[533]=754,[532]=745,[531]=744,[509]=743, [469]=742,[429]=1277,[429]=1277,[429]=1277,[409]=741,[389]=226,[349]=232,[329]=1292,[329]=1292,[269]=255,[249]=760,[230]=228,[229]=229,[209]=241,[129]=233,[109]=237,[90]=231,[70]=239,[48]=227,[47]=234, [43]=240,[36]=63,[34]=238,[33]=64, } ExRT.GDB.EncountersList = { {350,652,653,654,655,656,657,658,659,660,661,662}, {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}, {142,528,2013,530,529,2014,2015,532,531,533,534,2016,535}, {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}, {183,2006,2007}, {184,1999,2001,2000}, {185,1992,1993,1990}, {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}, {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}, {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}, {291,1064,1065,1063,1062,1060,1081}, {293,1051,1050,1048,1049}, {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}, {322,1045,1044,1046,1047}, {324,1056,1059,1058,1057}, {325,1043,1041,1042}, {333,1189,1190,1191,1192,1193,1194}, {337,1178,1179,1188,1180,1181,1182}, {347,1891,1892,1893}, {348,1894,1895,1897,1898}, {398,1272,1273,1274}, {399,1337,1340,1339}, {401,1881,1882,1883,1884,1271}, {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}, {457,1465,1502,1447,1464}, {476,1426,1427,1428,1429,1430}, {554,1563,1564,1571,1587}, {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}, {2190,2666,2667,2668,2669,2670,2671,2672,2672,2673}, {2316,2816,2861,2836}, --The Rookery:Dung {2308,2847,2835,2848}, --Priory of the Sacred Flame:Dung {2303,2829,2826,2787,2788}, --Darkflame Cleft:Dung {2341,2854,2880,2888,2883}, --The Stonevault:Dung {2357,2926,2906,2901}, --Ara-Kara, City of Echoes:Dung {2335,2900,2931,2929,2930}, --Cinderbrew Meadery:Dung {2359,2837,2838,2839}, --The Dawnbreaker:Dung {2343,2907,2908,2905,2909}, --City of Threads:Dung {2387,3020,3019,3053,3054}, --Operation: Floodgate:Dung {"11.2",2449,3107,3108,3109}, --Eco-Dome Al'dani:Dung {232,663,664,665,666,667,668,669,670,671,672}, {287,610,611,612,613,614,615,616,617}, {319,709,710,711,712,713,714,715,716,717}, {331,651}, {330,649,650}, {332,623,624,625,626,627,628}, {334,730,731,732,733}, {329,618,619,620,621,622}, {339,601,602,603,604,605,606,607,608,609}, {335,724,725,726,727,728,729}, {162,1107,1110,1116,1117,1112,1115,1113,1109,1121,1118,1111,1108,1120,1119,1114}, {155,1090}, {141,1094}, {147,1132,1136,1139,1142,1140,1137,1131,1135,1141,1133,1138,1134,1143,1130}, {172,1088,1087,1086,1089,1085}, {248,1084}, {186,1101,1100,1099,1096,1104,1097,1102,1095,1103,1098,1105,1106}, {200,1150}, {285,1027,1024,1022,1023,1025,1026}, {294,1030,1032,1028,1029,1082}, {328,1035,1034}, {367,1197,1204,1205,1206,1200,1185,1203}, {409,1292,1294,1295,1296,1297,1298,1291,1299}, {471,1395,1390,1434,1436,1500,1407}, {474,1507,1504,1463,1498,1499,1501}, {456,1409,1505,1506,1431}, {508,1577,1575,1570,1565,1578,1573,1572,1574,1576,1559,1560,1579,1580}, {556,1602,1598,1624,1604,1622,1600,1606,1603,1595,1594,1599,1601,1593,1623}, {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 {2232,2820,2709,2737,2731,2728,2708,2824,2786,2677}, --d {2292,2902,2917,2898,2918,2919,2920,2921,2922}, --Nerub-ar Palace:Raid {2406,3009,3010,3011,3012,3013,3014,3015,3016}, --Liberation of Undermine:Raid {"11.2",2460,3129,3131,3130,3132,3122,3133,3134,3135}, --Manaforge Omega } function ExRT.F.EJ_AutoScan() local TIER_MIN = 11 local NEW_DATA = { encounterIDtoEJ = {}, EncountersList_dung = {}, EncountersList_raid = {}, MapIDToJournalInstance = {}, JournalInstance = {-2}, } local encounterMap = {} local dungeonPosInsert = 1 for i=1,#ExRT.GDB.EncountersList do local dung = ExRT.GDB.EncountersList[i] if dung[1] == 232 then dungeonPosInsert = i end for j=2,#dung do encounterMap[ dung[j] ] = true end end local instanceMap = {} for i=1,#ExRT.GDB.JournalInstance do local tier = ExRT.GDB.JournalInstance[i] for j=2,#tier do instanceMap[ tier[j] ] = true end end for i=TIER_MIN,EJ_GetNumTiers() do EJ_SelectTier(i) for j=1,2 do local isRaid = j == 1 and true or false local instanceIndex = 1 while EJ_GetInstanceByIndex(instanceIndex,isRaid) do local instanceID, iname, description, bgImage, buttonImage1, loreImage, buttonImage2, dungeonAreaMapID, link, shouldDisplayDifficulty, mapID = EJ_GetInstanceByIndex(instanceIndex,isRaid) EJ_SelectInstance(instanceID) local isNewInstance = true local EncountersList_new = {dungeonAreaMapID} if dungeonAreaMapID == 0 then isNewInstance = false --ignore world bosses end local encounterIndex = 1 while EJ_GetEncounterInfoByIndex(encounterIndex, instanceID) do local name, description, journalEncounterID, rootSectionID, link, journalInstanceID, dungeonEncounterID, instanceID = EJ_GetEncounterInfoByIndex(encounterIndex, instanceID) EncountersList_new[#EncountersList_new+1] = dungeonEncounterID if dungeonEncounterID and encounterMap[dungeonEncounterID] then isNewInstance = false end if dungeonEncounterID and not ExRT.GDB.encounterIDtoEJ[dungeonEncounterID] then NEW_DATA.encounterIDtoEJ[dungeonEncounterID] = journalEncounterID end encounterIndex = encounterIndex + 1 end if isNewInstance then if isRaid then tinsert(NEW_DATA.EncountersList_raid, EncountersList_new) elseif isNewInstance then tinsert(NEW_DATA.EncountersList_dung, EncountersList_new) end for k=2,#EncountersList_new do encounterMap[ EncountersList_new[k] ] = true end --print('EncountersList','added',iname,encounterIndex-1) end if mapID and not ExRT.GDB.MapIDToJournalInstance[mapID] then NEW_DATA.MapIDToJournalInstance[mapID] = instanceID --print('MapIDToJournalInstance','added',iname) end if not instanceMap[instanceID] then instanceMap[instanceID] = true tinsert(NEW_DATA.JournalInstance, 2, instanceID) --print('JournalInstance','added',iname) end instanceIndex = instanceIndex + 1 end end end VMRT.Addon.EJ_CHECK_VER = ExRT.clientUIinterface VMRT.Addon.EJ_DATA = NEW_DATA VMRT.Addon.EJ_CHECK_VER_PTR = ExRT.clientBuildVersion ExRT.F.EJ_LoadData() end function ExRT.F.EJ_LoadData() if not VMRT.Addon.EJ_DATA then return end local NEW_DATA = VMRT.Addon.EJ_DATA local encounterMap = {} local dungeonPosInsert = 1 for i=1,#ExRT.GDB.EncountersList do local dung = ExRT.GDB.EncountersList[i] if dung[1] == 232 then dungeonPosInsert = i end for j=2,#dung do encounterMap[ dung[j] ] = true end end for i=1,#NEW_DATA.EncountersList_raid do local new_dung = NEW_DATA.EncountersList_raid[i] if new_dung[2] and not encounterMap[ new_dung[2] ] then tinsert(ExRT.GDB.EncountersList, new_dung) end end for i=1,#NEW_DATA.EncountersList_dung do local new_dung = NEW_DATA.EncountersList_dung[i] if new_dung[2] and not encounterMap[ new_dung[2] ] then tinsert(ExRT.GDB.EncountersList, dungeonPosInsert, new_dung) dungeonPosInsert = dungeonPosInsert + 1 end end for k,v in pairs(NEW_DATA.MapIDToJournalInstance) do if not ExRT.GDB.MapIDToJournalInstance[k] then ExRT.GDB.MapIDToJournalInstance[k] = v end end for k,v in pairs(NEW_DATA.encounterIDtoEJ) do if not ExRT.GDB.encounterIDtoEJ[k] then ExRT.GDB.encounterIDtoEJ[k] = v end end if #NEW_DATA.JournalInstance > 1 then tinsert(ExRT.GDB.JournalInstance,NEW_DATA.JournalInstance) end end local ACTUAL_RAID = 1735 local ACTUAL_DUNG = 1666 if UnitLevel'player' > 70 then ACTUAL_DUNG = 2316 ACTUAL_RAID = 2292 elseif 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] == 2096 then --stop loop for old data break end end for i=#ExRT.GDB.JournalInstance,1,-1 do local t = ExRT.GDB.JournalInstance[i] if type(t[1]) == "string" then if StringVerToNumber(t[1]) > ver_now then tremove(ExRT.GDB.JournalInstance, i) else tremove(t, 1) end else for j=#t,2,-1 do if type(t[j]) == "string" then if StringVerToNumber(t[j]) > ver_now then tremove(t, j) end tremove(t, j) end end end end end function ExRT.F.GetEncountersList(onlyRaid,onlyActual,reverse,onlyDung) local new = {} local isActual,isRaid for _,v in ipairs(ExRT.GDB.EncountersList) do if v[1] == 232 then isRaid = true isActual = false if onlyDung then break end 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