You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

772 lines
27 KiB

-- Utils.lua: common lua used across multiple modules
local _,L = ...
local rematch = Rematch
-- takes an petid and returns what type of id it is ("pet" "species" "leveling" or "table")
-- pet: this is an owned petID (BattlePet-0-000etc)
-- species: this is a speciesID (42 268 etc)
-- leveling: this is a leveling pet (0)
-- table: this is a table of stats (for pet links and enemy battle unit pets)
function rematch:GetIDType(id)
if type(id)=="string" then
if id:match("^BattlePet%-%x%-%x%x%x%x%x%x%x%x%x%x%x%x$") then
return "pet"
elseif id:match("battlepet:%d+:%d+:%d+:%d+:%d+:%d+:.+") then
return "link"
elseif id:match("battle:%d:%d") then
return "battle"
end
elseif id==0 then
return "leveling"
elseif type(id)=="number" then
return "species"
-- elseif type(id)=="table" then
-- return "table"
end
end
-- returns the name of the team for display purposes
-- if color is true, prefixes white color code if it's a team with an npcID
function rematch:GetTeamTitle(key,color)
local forNpc = type(key)=="number"
local saved = RematchSaved
if saved[key] and forNpc then
local teamName = saved[key].teamName or format("NPC:%s",tostring(key))
if color then
return format("\124cffffffff%s\124r",teamName)
else
return teamName
end
else
return key
end
end
-- returns name of the given unit and npcID if it's an npc, or nil if unit doesn't exist
function rematch:GetUnitNameandID(unit)
if UnitExists(unit) then
local name = UnitName(unit)
local npcID = tonumber((UnitGUID(unit) or ""):match(".-%-%d+%-%d+%-%d+%-%d+%-(%d+)"))
if npcID and npcID~=0 then
if rematch.targetRedirects[npcID] and not RematchSaved[npcID] then -- if this is a challenge post pet then return npcID of its post
npcID = rematch.targetRedirects[npcID]
return (rematch:GetNameFromNpcID(npcID)),npcID
else
return name,npcID -- this is an npc, return its name and npcID
end
else
return name -- this is a player, return its name
end
end
end
local utilsInfo -- GetPetIcon is used in so many places, using a unique petInfo for it
function rematch:GetPetIcon(petID)
if not utilsInfo then
utilsInfo = rematch:CreatePetInfo()
end
utilsInfo:Fetch(petID)
return utilsInfo.icon or "Interface\\PaperDoll\\UI-Backpack-EmptySlot"
end
function rematch:GetPetName(petID)
local idType = rematch:GetIDType(petID)
if idType=="pet" then
local _,customName,_,_,_,_,_,name = C_PetJournal.GetPetInfoByPetID(petID)
return customName or name
elseif idType=="species" then
return (C_PetJournal.GetPetInfoBySpeciesID(petID))
elseif idType=="leveling" then
return L["Leveling Pet"]
else
return UNKNOWN
end
end
function rematch:GetPetSpeciesID(petID)
local idType = rematch:GetIDType(petID)
local speciesID
if idType=="pet" then
return (C_PetJournal.GetPetInfoByPetID(petID))
elseif idType=="species" then
return petID
end
end
-- DesensitizedText doesn't work for Russian or German clients; use the less efficient string:lower() compare
local locale = GetLocale()
local useAltSensitivity = locale=="ruRU" or locale=="deDE"
-- DesensitizeText returns text in a literal (magic characters escaped) and case-insensitive format
local function literal(c) return "%"..c end
local function caseinsensitive(c) return format("[%s%s]",c:lower(),c:upper()) end
function rematch:DesensitizeText(text)
if type(text)=="string" then
if useAltSensitivity then -- for ruRU/deDE clients use the lower case text
return text:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]",literal):lower()
else
return text:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]",literal):gsub("%a",caseinsensitive)
end
end
end
-- when doing a case-insensitive match, use this instead of a direct match; so it can handle ruRU/deDE matches
function rematch:match(candidate,pattern)
if candidate and pattern then
if type(candidate)~="string" then
candidate=tostring(candidate)
end
if useAltSensitivity then -- match the lower case candidate to the pattern (which is also lower case)
return candidate:lower():match(pattern)
else
return candidate:match(pattern)
end
end
end
-- use this instead of SetPetLoadOutInfo directly in case first slot changes (to resummon pet back)
function rematch:SlotPet(slot,petID)
-- if KeepSummoned enabled, we want to resummon summoned pet after swap finishes
if RematchSettings.KeepSummoned and not IsFlying() then
-- if KeepCompanion timer already running, we've already begun a pet swap, restart timer
if rematch:IsTimerRunning("KeepCompanion") then
rematch:StartTimer("KeepCompanion",0.5,rematch.CheckKeepCompanion)
else -- if timer wasn't running, we're initiating a swap now
rematch.preLoadCompanion = C_PetJournal.GetSummonedPetGUID()
rematch:StartTimer("KeepCompanion",0.5,rematch.CheckKeepCompanion)
-- it's possible that slotting a slot other than 1 will auto summon a pet (loading pet from slot 1 to 3
end
end
C_PetJournal.SetPetLoadOutInfo(slot,petID)
end
function rematch:CheckKeepCompanion()
local GCDPetID = rematch:GetGCDPetID()
if (GCDPetID and C_PetJournal.GetPetCooldownByGUID(GCDPetID)~=0) or InCombatLockdown() then
rematch:StartTimer("KeepCompanion",0.5,rematch.CheckKeepCompanion)
else
local summonedPetID = C_PetJournal.GetSummonedPetGUID()
if rematch.preLoadCompanion ~= summonedPetID then
if not rematch.preLoadCompanion then
C_PetJournal.SummonPetByGUID(summonedPetID)
else
C_PetJournal.SummonPetByGUID(rematch.preLoadCompanion)
end
rematch:StartTimer("KeepCompanion",0.5,rematch.CheckKeepCompanion)
end
end
end
-- hides all the pop-up and flyout widgets when doing any major event
-- can pass name of function "HideMenu" or "HideFlyout" to hide everything but that
-- if a menu item just clicked, it's automatically excluded from hiding (menu will take care of hiding itself)
-- if completely is true, everything is hidden with no exceptions
local hideWidgetFuncs = {"HideMenu","HideFlyout","HidePetCard","HideTooltip","HideWinRecord"}
function rematch:HideWidgets(except,completely)
for _,funcName in pairs(hideWidgetFuncs) do
if rematch[funcName] and (completely or (funcName~=except and (funcName~="HideMenu" or not rematch:UIJustChanged()))) then
-- if funcName~=except and rematch[funcName] and (funcName~="HideMenu" or not rematch:UIJustChanged()) then
rematch[funcName]()
end
end
end
-- returns the pet on the cursor if there is one
function rematch:GetCursorPet()
local hasPet,petID = GetCursorInfo()
if hasPet=="battlepet" then
return petID
end
end
-- anchors frame to relativeTo depending on the quarter of the screen the reference frame
-- exists (will go up parents of relativeTo to find reference parent)
-- if no relativeTo given, it will choose RematchFrame or RematchJournal
-- if center is true it will anchor to reference frame's center
-- yfromtop is the y offset when frame is anchoring to top (hidden title area of pet card)
-- yfrombottom is the y offset when frame is anchoring to the bottom (hidden controls of winrecord)
local references = { ["RematchFrame"]=true, ["UIParent"]=true }
function rematch:SmartAnchor(frame,relativeTo,center,yfromtop,yfrombottom)
if not relativeTo then
relativeTo = RematchFrame -- will check if journal visible and change to it
end
local reference = relativeTo
while not references[reference:GetName() or ""] do
local parent = reference:GetParent()
if not parent or parent==UIParent then
break -- stop when we've run out of parents or reached UIParent
else
reference = parent
end
end
if not reference then return end -- failed catastrophically
-- pet card has a (usually) invisible titlebar; yspecial will nudge yoffset up 22 if anchoring to top
-- local yfromtop = (frame==RematchPetCard or frame==RematchWinRecordCard) and 22 or 0
-- local yfrombottom = frame==RematchWinRecordCard and -22 or 0
frame:ClearAllPoints()
if center then -- simply center it to reference
frame:SetPoint("CENTER",reference,"CENTER")
else
local corner = rematch:GetCorner(reference,UIParent)
if corner=="BOTTOMLEFT" or reference==CollectionsJournal then
rematch:SmartSetPoint(frame, "BOTTOMLEFT", relativeTo, "TOPRIGHT", 0, yfrombottom or 0)
elseif corner=="TOPLEFT" then
rematch:SmartSetPoint(frame, "TOPLEFT", relativeTo, "BOTTOMRIGHT", 0, yfromtop or 0)
elseif corner=="TOPRIGHT" then
rematch:SmartSetPoint(frame, "TOPRIGHT", relativeTo.Pet or relativeTo, "BOTTOMLEFT", 0, yfromtop or 0)
else
rematch:SmartSetPoint(frame, "BOTTOMRIGHT", relativeTo.Pet or relativeTo, "TOPLEFT", 0, yfrombottom or 0)
end
end
end
function rematch:oldSmartAnchor(frame,relativeTo,center,yfromtop,yfrombottom)
if not relativeTo then
relativeTo = RematchFrame -- will check if journal visible and change to it
end
local reference = relativeTo
while not references[reference:GetName() or ""] do
local parent = reference:GetParent()
if not parent or parent==UIParent then
break -- stop when we've run out of parents or reached UIParent
else
reference = parent
end
end
if not reference then return end -- failed catastrophically
frame:ClearAllPoints()
if center then -- simply center it to reference
frame:SetPoint("CENTER",reference,"CENTER")
else
local corner = rematch:GetCorner(reference,UIParent)
if corner=="BOTTOMLEFT" or reference==CollectionsJournal then
frame:SetPoint("BOTTOMLEFT",relativeTo,"TOPRIGHT",0,yfrombottom or 0)
elseif corner=="TOPLEFT" then
frame:SetPoint("TOPLEFT",relativeTo,"BOTTOMRIGHT",0,yfromtop or 0)
elseif corner=="TOPRIGHT" then
frame:SetPoint("TOPRIGHT",relativeTo.Pet or relativeTo,"BOTTOMLEFT",0,yfromtop or 0)
else
frame:SetPoint("BOTTOMRIGHT",relativeTo.Pet or relativeTo,"TOPLEFT",0,yfrombottom or 0)
end
end
end
-- if a frame is being anchored, instead of anchoring directly to relativeTo, anchor it to UIParent
-- this is because some frames (winrecord card) won't anchor to a texture
function rematch:SmartSetPoint(frame, anchorPoint, relativeTo, relativePoint, xoff, yoff)
-- for a frame being anchored to a Frame, use a SetPoint to anchor straight to Frame
--if relativeTo and relativeTo:GetObjectType()=="Frame" then
if frame~=RematchWinRecordCard then -- grr for some reason this is needed just for winrecord
frame:SetPoint(anchorPoint, relativeTo, relativePoint, xoff, yoff)
else -- for a frame being anchored to a Texture or FontString, anchor to UIParent equivalent
local relativeScale = relativeTo:GetEffectiveScale()
local uiScale = UIParent:GetEffectiveScale()
xoff = xoff or 0
yoff = yoff or 0
local relativeX, relativeY
if relativePoint=="TOPLEFT" then
relativeX = relativeTo:GetLeft() + xoff
relativeY = relativeTo:GetTop() + yoff
elseif relativePoint=="TOPRIGHT" then
relativeX = relativeTo:GetRight() + xoff
relativeY = relativeTo:GetTop() + yoff
elseif relativePoint=="BOTTOMLEFT" then
relativeX = relativeTo:GetLeft() + xoff
relativeY = relativeTo:GetBottom() + yoff
else -- BOTTOMRIGHT
relativeX = relativeTo:GetRight() + xoff
relativeY = relativeTo:GetBottom() + yoff
end
-- adjust x/y to (x or y)*relativeToScale/UIParentScale
local scale = relativeTo:GetEffectiveScale() / UIParent:GetEffectiveScale()
frame:SetPoint(anchorPoint, UIParent, "BOTTOMLEFT", relativeX*scale, relativeY*scale)
end
end
-- To prevent tooltips and pet cards appearing when menus/pet cards/etc hide over an element,
-- any major event will mark the time it happened with timeUIChanged. Then this function
-- can be called to know whether an OnEnter should be run.
function rematch:UIJustChanged()
return GetTime()==rematch.timeUIChanged
end
-- returns the corner of reference that frame is closest to
-- (if frame is closest to TOPRIGHT corner of reference, returns "TOPRIGHT")
-- used in SmartAnchor and anchoring RematchFrame to UIParent
-- if coords is true, the x,y offset from UIParent's BOTTOMLEFT is returned
function rematch:GetCorner(frame,reference,coords)
local fx,fy = frame:GetCenter()
local rx,ry = reference:GetCenter()
local left, right, top, bottom
if coords then
left = frame:GetLeft()
right = frame:GetRight()
top = frame:GetTop()
bottom = frame:GetBottom()
end
if not coords then
ry=ry*1.2 -- raise y threshold up 20% of reference if not returning coords (to favor anchoring upwards)
end
if fx<rx and fy<ry then -- bottomleft
return "BOTTOMLEFT",left,bottom
elseif fx<rx and fy>ry then -- topleft
return "TOPLEFT",left,top
elseif fx>rx and fy>ry then -- topright
return "TOPRIGHT",right,top
else -- bottomright (or dead center)
return "BOTTOMRIGHT",right,bottom
end
end
-- adds a leveling border to a button (only 3 main loadouts and queue leveling slot uses this)
function rematch:AddSpecialBorder(button)
button.SpecialBorder = button:CreateTexture(nil,"BACKGROUND")
local cx,cy = button:GetSize()
button.SpecialBorder:SetSize(cx+10,cy+10)
button.SpecialBorder:SetPoint("CENTER")
button.SpecialBorder:SetTexture("Interface\\PetBattles\\PetBattle-GoldSpeedFrame")
button.SpecialBorder:SetTexCoord(0.1171875,0.7421875,0.1171875,0.734375)
button.SpecialFootnote = CreateFrame("Button",nil,button,"RematchFootnoteButtonTemplate,RematchTooltipScripts")
button.SpecialFootnote:SetPoint("TOPRIGHT",4,4)
button.SpecialFootnote:SetScript("OnClick",rematch.SpecialFootnoteOnClick)
button.SpecialFootnote:RegisterForClicks("AnyUp")
end
-- adds frameName to UISpecialFrames if value is true, removes it if value is false
function rematch:SetESCable(frameName,value)
if value and not tContains(UISpecialFrames,frameName) then
tinsert(UISpecialFrames,frameName)
elseif not value then
for i=#UISpecialFrames,1,-1 do
if UISpecialFrames[i]==frameName then
tremove(UISpecialFrames,i)
end
end
end
end
function rematch:print(...)
print(format("%s%s:\124r",rematch.hexGold,L["Rematch"]),...)
end
-- these two functions find the highest framelevel of a frame and its children
-- highestLevel = rematch:FindHighestFrameLevel(startingFrame)
-- RematchJournal is excluded
local function findHigherFrameLevel(...)
local numFrames = select("#",...)
for i=1,numFrames do
local frame = select(i,...)
if not RematchJournal or frame~=RematchJournal then
local level = frame:GetFrameLevel()
if frame:GetName() or frame:IsVisible() then -- if something badly wants to be higher framelevel (like PBT) let it
rematch.highestFrameLevel = max(rematch.highestFrameLevel,level)
end
findHigherFrameLevel(frame:GetChildren())
end
end
end
-- PBT's TeamFrame.lua creates a teamMovementFrame with 10k framelevel lol
function rematch:FindHighestFrameLevel(frame)
rematch.highestFrameLevel = 0
findHigherFrameLevel(frame)
return rematch.highestFrameLevel
end
--[[ List scrolling ]]
function rematch:ListScrollToTop(scrollFrame)
scrollFrame.scrollBar:SetValue(0)
PlaySound(1115)
end
function rematch:ListScrollToBottom(scrollFrame)
scrollFrame.scrollBar:SetValue(scrollFrame.range)
PlaySound(1115)
end
function rematch:ListScrollToIndex(scrollFrame,index)
if index then
if scrollFrame.scrollBar:IsEnabled() then
local buttons = scrollFrame.buttons
local height = math.max(0,floor(scrollFrame.buttonHeight*(index-((scrollFrame:GetHeight()/scrollFrame.buttonHeight))/2)))
HybridScrollFrame_SetOffset(scrollFrame,height)
scrollFrame.scrollBar:SetValue(height)
else
rematch:ListScrollToTop(scrollFrame)
end
end
end
-- does a flashing glow in a listbutton (after 0.05 seconds to let it update)
function rematch:ListBling(scrollFrame,var,value)
rematch.blingScrollFrame = scrollFrame
rematch.blingVar = var
rematch.blingValue = value
C_Timer.After(0.05,rematch.ListDoBling)
end
-- does the actual glow effect after 0.05 seconds
function rematch:ListDoBling()
local scrollFrame = rematch.blingScrollFrame
local foundButton
if scrollFrame then
for _,button in ipairs(scrollFrame.buttons) do
if button[rematch.blingVar]==rematch.blingValue then
foundButton = button
local bling = RematchPetListBling
bling:SetParent(button)
bling:SetFrameLevel(button:GetFrameLevel()+5)
bling:SetAllPoints(true)
bling:Show()
break
end
end
end
rematch.blingScrollFrame = nil
rematch.blingVar = nil
rematch.blingValue = nil
end
-- takes a petType (1-10) and returns a text string for the icon (20x20) with the thin circle border
function rematch:PetTypeAsText(petType,size)
size = size or 16
local suffix = PET_TYPE_SUFFIX[petType]
return suffix and format("\124TInterface\\PetBattles\\PetIcon-%s:%d:%d:0:0:128:256:102:63:129:168\124t",suffix,size,size) or "?"
end
function rematch:SetTopToggleButton(button,up)
button.up = up
rematch.TopToggleButtonOnMouseUp(button)
if up then
-- button.Icon:SetTexture("Interface\\Buttons\\UI-MicroStream-Yellow")
-- button.Icon:SetTexCoord(0,1,1,0)
-- button.Icon:SetTexCoord(1,0,0,0,1,1,0,1) -- 90 degrees (up)
else
-- button.Icon:SetTexture("Interface\\Buttons\\UI-MicroStream-Yellow")
-- button.Icon:SetTexCoord(0,1,0,1)
-- button.Icon:SetTexCoord(0,1,1,1,0,0,1,0) -- -90 degrees (down)
end
end
function rematch:TopToggleButtonOnMouseDown(button)
if self:IsEnabled() then
self.TopLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
self.TopRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
self.BottomLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
self.BottomRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
self.MiddleLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
self.MiddleRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Down")
if self.up then
self.Icon:SetPoint("CENTER",-1,0)
else
self.Icon:SetPoint("CENTER",-1,-2)
end
end
end
function rematch:TopToggleButtonOnMouseUp(button)
self.TopLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
self.TopRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
self.BottomLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
self.BottomRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
self.MiddleLeft:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
self.MiddleRight:SetTexture("Interface\\Buttons\\UI-Silver-Button-Up")
if self.up then
self.Icon:SetPoint("CENTER",0,1)
self.Icon:SetTexCoord(1,0,0,0,1,1,0,1) -- 90 degrees (up)
else
self.Icon:SetPoint("CENTER",0,-1)
self.Icon:SetTexCoord(0,1,1,1,0,0,1,0) -- -90 degrees (down)
end
end
-- adjusts the scale of a frame depending on settings.SmallerWindow and if
-- the regular frame is up; if force is true then adjusts regardless if frame
-- is visible or not.
function rematch:AdjustScale(frame,force)
local settings = RematchSettings
if settings.CustomScale and settings.CustomScaleValue and (rematch.Frame:IsVisible() or force) then
frame:SetScale(settings.CustomScaleValue/100)
else
frame:SetScale(1)
end
end
-- to be called when some action (prompt to load->with window; auto load->show etc)
-- wants to show the window. if neither standalone or journal are up, it shows
-- the PreferredMode (1=minimized, 2=maximized, 3=journal)
function rematch:AutoShow()
-- if rematch is already on screen, then don't do anything
if rematch.Frame:IsVisible() or rematch.Journal:IsVisible() then
return
end
local frame = rematch.Frame
local settings = RematchSettings
local mode = settings.PreferredMode
if mode==3 then
if settings.UseDefaultJournal then
mode = 2 -- if they have journal integration disabled but journal preferred window, show standalone
elseif not rematch.Journal:IsVisible() then
ToggleCollectionsJournal(2)
end
end
if not frame:IsVisible() then
if mode==1 then
settings.Minimized = true
rematch.Frame.Toggle()
elseif mode==2 then
settings.Minimized = nil
rematch.Frame.Toggle()
end
end
end
-- reparents (and positions if anchor given) a child to parent and shows it
function rematch:Reparent(child,parent,anchorPoint,...)
child:SetParent(parent)
if anchorPoint then
child:ClearAllPoints()
child:SetPoint(anchorPoint,...)
end
child:Show()
end
-- sets the passed texture to the petType using the prefix or the PetIcons if no prefix
-- prefixes used:
-- "Interface\\PetBattles\\PetIcon-" (Loadouts)
function rematch:FillPetTypeIcon(texture,petType,prefix)
if not petType then
texture:SetTexture(nil)
elseif prefix then
texture:SetTexture(prefix..PET_TYPE_SUFFIX[petType])
else -- if no prefix then we're grabbing icon from Rematch\Textures\PetIcons
local x = ((petType-1)%4)*0.25
local y = floor((petType-1)/4)*0.25
texture:SetTexCoord(x,x+0.25,y,y+0.171875)
end
end
-- returns true if any loaded pets are under 25
function rematch:IsLowLevelPetLoaded()
for i=1,3 do
local petID = C_PetJournal.GetPetLoadOutInfo(i)
if petID then
local level = select(3,C_PetJournal.GetPetInfoByPetID(petID))
if level and level<25 then
return true
end
end
end
end
-- when an ability on the pet card is double clicked, search for the ability in the pet panel
function rematch:SearchAbility(abilityID)
if abilityID then
local _,name = C_PetBattles.GetAbilityInfoByID(abilityID)
if name then
rematch.Roster:ClearAllFilters()
rematch:ShowPets()
rematch.PetPanel.Top.SearchBox:SetFocus(true)
rematch.PetPanel.Top.SearchBox:SetText("\""..name.."\"")
rematch.PetPanel.Top.SearchBox:ClearFocus()
end
end
end
--[[ Temporary Tables
Occasionally there's a need for a table of data that doesn't need to be kept,
such as movesets during filters or speciesAt25 while filling queue, etc.
To use:
1) In the module's init, register name of table and function to populate its data:
rematch:RegisterTempTable("nameoftable"[,func])
2) Everytime you need to get the table:
local data = rematch:GetTempTable("nameoftable")
3) When done with all tables:
rematch:WipeTempTables()
]]
rematch.tempTables = { Data={}, Populate={}, Active={} }
function rematch:RegisterTempTable(name,func)
rematch.tempTables.Populate[name] = func
rematch.tempTables.Data[name] = {}
end
function rematch:GetTempTable(name)
local tempTable = rematch.tempTables
local data = tempTable.Data[name]
-- if table already active, just return data
if tempTable.Active[name] then
return data
end
-- if data is empty, then run its Populate function
if not next(data) then
local func = tempTable.Populate[name]
if func then
func(rematch,data)
end
end
tempTable.Active[name] = true -- flag table as active
return data -- and return data
end
function rematch:WipeTempTables()
local tempTable = rematch.tempTables
for _,data in pairs(tempTable.Data) do
wipe(data)
end
wipe(tempTable.Active)
end
-- returns the abilityList,levelList of a speciesID from a reusable table
local rAbilityList,rAbilityLevels,rAbilitySpecies = {},{}
function rematch:GetAbilities(speciesID)
if not speciesID then return {},{} end -- if no species given, return empty ability tables
if speciesID and speciesID~=rAbilitySpecies then
C_PetJournal.GetPetAbilityList(speciesID,rAbilityList,rAbilityLevels)
rAbilitySpecies = speciesID
end
return rAbilityList,rAbilityLevels
end
-- returns cached pet stats
local rStatsHealth, rStatsMaxHealth, rStatsPower, rStatsSpeed, rStatsRarity, rStatsPetID
function rematch:GetPetStats(petID)
local health, maxHealth, power, speed, rarity
if petID ~= rStatsPetID then
rStatsHealth, rStatsMaxHealth, rStatsPower, rStatsSpeed, rStatsRarity = C_PetJournal.GetPetStats(petID)
rStatsPetID = petID
end
return rStatsHealth, rStatsMaxHealth, rStatsPower, rStatsSpeed, rStatsRarity
end
function rematch:DebugStack()
local callers = {}
local stack = debugstack()
for caller in string.gmatch(stack,"Interface\\AddOns\\.-\\(.-%.lua:%d+)") do
tinsert(callers,(caller:gsub(".-\\","")))
end
tremove(callers,1) -- remove the call to this function
tremove(callers,1) -- and the print that called the call
return "\124cffc0c0c0"..table.concat(callers,", ")
end
-- Summoning/dismissing all pets are on GCD, but not all petIDs think so!
-- For the toolbar cooldown display and Keep Companion option, we need to be aware
-- of the GCD. If a GCD petID isn't already discovered, it will go through the
-- first 8 owned pets to find one that's participating in the GCD.
function rematch:GetGCDPetID()
if not rematch.GCDPetID then
local limit = 1
for petID in rematch.Roster:AllOwnedPets() do
if limit<8 then
local start,duration = C_PetJournal.GetPetCooldownByGUID(petID)
if start and start~=0 then
rematch.GCDPetID = petID
return petID
end
else
break -- only checking first 5 pets
end
limit = limit + 1
end
end
return rematch.GCDPetID
end
-- searches for the name of the speciesID in the pet panel
function rematch:SearchForSpecies(speciesID)
local roster = rematch.Roster
roster:UpdateOwned()
roster:ClearAllFilters()
local searchBox = rematch.PetPanel.Top.SearchBox
local speciesName = C_PetJournal.GetPetInfoBySpeciesID(speciesID)
if speciesName then
speciesName = format("\"%s\"",speciesName)
searchBox:SetText(speciesName)
roster:SetSearch(speciesName)
searchBox:ClearFocus()
end
end
function rematch:SetRoundTexture(texture,filepath)
texture:SetMask("Textures\\MinimapMask")
texture:SetTexture(filepath)
end
-- for PetTags and and TeamStrings to store base-32 numbers in strings
local digitsOut = {} -- to avoid garbage creation, this is reused to build a 32-base number
local digitsIn = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
-- convert number to base 32: VV = 1023
function rematch:ToBase32(number)
number = tonumber(number)
if number then
wipe(digitsOut)
number = math.abs(floor(number))
repeat
local digit = (number%32) + 1
number = floor(number/32)
tinsert(digitsOut, 1, digitsIn:sub(digit,digit))
until number==0
return table.concat(digitsOut,"")
end
end
-- in BfA, UnitBuff no longer accepts a spell name as an argument grr
local timeBuffsChecked = nil
local activeBuffs = {} -- indexed by name of buff, the index of the buff
function rematch:UnitBuff(buffName)
-- if this isn't the same execution thread when buffs last checked, gather buffs
local now = GetTime()
if now~=timeBuffsChecked then
timeBuffsChecked = now
wipe(activeBuffs)
local i = 1
local buff
repeat
buff = UnitBuff("player",i)
if buff then
activeBuffs[buff] = i
end
i = i + 1
until not buff
end
-- return UnitBuff of the named buff
if activeBuffs[buffName] then
return UnitBuff("player",activeBuffs[buffName])
end
end
-- configures the journal if it's up or the standalone frame otherwise
function rematch:Configure()
if RematchJournal:IsVisible() then
RematchJournal:ConfigureJournal()
else
RematchFrame:ConfigureFrame()
end
end