--[[ This module handles the conversion of sidelined teams into strings and vice versa. Old string format: :::::: :::::: :: New string format: ::::: Preferences format: P::::::: (in old format, empty fields have a 0; in new format, fields can be empty: "P:::5:") Optional notes must be at the end of the string: N: rematch:ConvertSidelineToString() rematch:ConvertStringToSideline() rematch:ConvertSidelineToPlainText() ]] local _,L = ... local rematch = Rematch -- import patterns local patterns = { legacyTeam = "^([^\n]-):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(.*)", team = "^([^\n]-):(%w*):(%w+):(%w+):(%w+):(.*)", prefs = "P:(%d*):(%d*):(%d*):(%d*):([%d%.]*):([%d%.]*):(.*)", notes = "N:(.+)", } -- patterns for exporting teams into plain text local exportStubs = { title_npcID_only = L["%s (NPC#%d)"], -- 1=name of npc, 2=npcID title_npcID_name = L["%s (%s NPC#%d)"], -- 1=name of team, 2=name of npc, 3=npcID pet_basic = L["%d: %s"], -- 1=slot, 2=pet name pet_with_abilities = L["%d: %s (%d,%d,%d)"], -- 1=slot, 2=pet name, 3-5=1/2 ability prefs = L["Preferred leveling pets: %s."], -- 1=concatenated string of preferences minHP_basic = L["at least %d health"], -- 1=minHP minHP_allowMM = L["at least %d health (or any Magic/Mechanical)"], -- 1=minHP minHP_expectedDD = L["at least %d health (%s damage expected)"], -- 1=minHP, 2=expectedDD text minHP_allowMM_expectedDD = L["at least %d health (or any Magic/Mechanical, %s damage expected)"], -- 1=minHP, 2=expectedDD text (", " critical!) maxHP = L["at most %d health"], -- 1=maxHP minXP = L["at least level %s"], -- 1=minXP maxXP = L["at most level %s"], -- 1=maxXP } -- converts an existing sideline into a team string function rematch:ConvertSidelineToString() local team,key = rematch:GetSideline() local legacy = RematchSettings.UseLegacyExport local teamString if key then local result = rematch.info wipe(result) -- start with name of team tinsert(result,tonumber(key) and team.teamName or key) -- add the target npcID if type(key)=="number" then -- this team has a target npcID tinsert(result,legacy and key or rematch:ToBase32(key)) else -- this team does not have an npcID tinsert(result,legacy and "0" or "") end -- then a tag for each of the three pets for i=1,3 do local petID,ability1,ability2,ability3 = team[i][1],team[i][2],team[i][3],team[i][4] local petInfo = rematch.petInfo:Fetch(petID) if not petInfo.valid then petID = team[i][5] -- if petID isn't valid, use the saved speciesID -- if no saved speciesID it's ok, petID will be nil and pet unknown (ZU) end if not legacy then -- for new format, add PetTag tinsert(result,rematch:CreatePetTag(petID,ability1,ability2,ability3)) else -- for legacy format, add speciesID and abilities tinsert(result,petInfo.speciesID or 0) tinsert(result,ability1 or 0) tinsert(result,ability2 or 0) tinsert(result,ability3 or 0) end end -- string so far is name, npcID and pet tags teamString = table.concat(result,":")..":" -- add preferences if team has any if rematch:HasPreferences(team) and not RematchSettings.DontIncludePreferences then local emptyField = legacy and "0" or "" -- field to use for empty values wipe(result) tinsert(result,"P") tinsert(result,team.minHP or emptyField) tinsert(result,team.allowMM and "1" or emptyField) tinsert(result,team.expectedDD or emptyField) tinsert(result,team.maxHP or emptyField) tinsert(result,team.minXP or emptyField) tinsert(result,team.maxXP or emptyField) teamString = teamString .. table.concat(result,":")..":" end -- add notes if team has any if team.notes and team.notes:trim()~="" and not RematchSettings.DontIncludeNotes then teamString = teamString .. format("N:%s",team.notes):gsub("\n","\\n") end return teamString end end function rematch:ConvertStringToSideline(text) if type(text)=="string" then if rematch:AttemptLegacyStringToSideline(text:match(patterns.legacyTeam)) then return "legacy teamstring sidelined for "..rematch:GetSidelineTitle() elseif rematch:AttemptStringToSideline(text:match(patterns.team)) then return "teamstring sidelined for "..rematch:GetSidelineTitle() else return false end end end -- legacy teamstrings will be converted to the normal format during conversion function rematch:AttemptLegacyStringToSideline(...) local name,npcID = ... if name and npcID then -- there was a pattern match, convert to normal format local tags = rematch.info wipe(tags) for i=1,3 do local speciesID,a1,a2,a3 = select(3+(i-1)*4,...) local tag = rematch:CreatePetTag(tonumber(speciesID),tonumber(a1),tonumber(a2),tonumber(a3)) tinsert(tags,tag) end return rematch:AttemptStringToSideline(name,rematch:ToBase32(npcID),tags[1],tags[2],tags[3],select(15,...)) end end function rematch:AttemptStringToSideline(...) local name,npcID = ... if name and npcID then -- there was a pattern match, attempt to parse new teamstring -- sideline a blank team, keyed to npcID if one exists, by name otherwise local team,key if npcID=="" or npcID=="0" then -- this is a team without an npcID team,key = rematch:SetSideline(name,{}) else team,key = rematch:SetSideline(tonumber(npcID,32),{teamName=name}) end -- assign tab to current tab team.tab = RematchSettings.SelectedTab>1 and RematchSettings.SelectedTab or nil -- now add pets local otherPetIDs = rematch.info wipe(otherPetIDs) for i=3,5 do local petTag = select(i,...) local petID = rematch:FindPetFromPetTag(petTag,otherPetIDs[1],otherPetIDs[2]) if type(petID)=="string" then -- if an actual petID was found (not a speciesID) tinsert(otherPetIDs,petID) -- add this pet to otherPetIDs so it doesn't get reused end team[i-2] = {petID,rematch:GetAbilitiesFromTag(petTag)} end -- attempt to get preferences and notes local extras = select(6,...) if extras and extras~="" then if extras:match(patterns.prefs) then rematch:AttemptStringExtrasToSideline("P",extras:match(patterns.prefs)) elseif extras:match(patterns.notes) then rematch:AttemptStringExtrasToSideline("N",extras:match(patterns.notes)) end end return true end end -- for preferences: takes a string and returns it in number form if it's not "" or "0" local function getNumber(stat) if stat=="" or stat=="0" then return nil else return tonumber(stat) end end -- this will add either preferences or notes from a string to a team -- the first parameter should be "P" for preferences and "N" for notes -- the remaining parameters are the matches from a prefs or notes pattern -- NOTE: notes must always be last extras! function rematch:AttemptStringExtrasToSideline(...) local extraType = ... local team,key = rematch:GetSideline() -- if these are preferences, add their values if extraType=="P" then team.minHP = getNumber(select(2,...)) team.allowMM = select(3,...)=="1" and true or nil team.expectedDD = getNumber(select(4,...)) team.maxHP = getNumber(select(5,...)) team.minXP = getNumber(select(6,...)) team.maxXP = getNumber(select(7,...)) -- call this function again with remainder if remainder matches notes pattern local extras = select(8,...) if extras and extras:match(patterns.notes) then rematch:AttemptStringExtrasToSideline("N",extras:match(patterns.notes)) end end -- if these are notes, add the notes if extraType=="N" then local notes = select(2,...) if notes then team.notes = notes:gsub("\\n","\n") end end end -- returns true if the passed name,npcID form an already-used team key local function isKeyUsed(name,npcID) if not npcID or npcID==0 then return RematchSaved[name] and true else return RematchSaved[tonumber(npcID)] and true end end -- if text contains string teams, returns the number of teams and the number of those keys -- already used for an existing saved team. the first team is sidelined. if gather given -- as a table, string lines are added to it -- if tabMap table is passed, then populate its keys with encountered tabs and the team lines in those tabs function rematch:TestTextForStringTeams(text,gather,tabMap) local currentTabName = nil text = (text or ""):trim() if text=="" then return end local numTeams = 0 -- number of teams in the text local numUsedKeys = 0 -- number of keys already used by an existing saved team for line in text:gmatch("[^\n]+") do line = line:trim() -- watch for tabs local tabName = line:match("^__ (.+) __$") if tabName and tabMap and not tabMap[tabName] then tabMap[tabName] = {} currentTabName = tabName end local name,npcID = rematch:GetTeamStringNameAndNpcID(line) if name then if type(tabMap)=="table" then if currentTabName and tabMap[currentTabName] then tinsert(tabMap[currentTabName],line) end end if type(gather)=="table" then tinsert(gather,line) else if numTeams==0 then -- if this is the first team, sideline it rematch:ConvertStringToSideline(line) rematch.Dialog:SetContext("plain",nil) end numTeams = numTeams + 1 if isKeyUsed(name,npcID) then numUsedKeys = numUsedKeys + 1 end end end end if numTeams>0 then return numTeams,numUsedKeys,tabMap end end -- returns name and npcID of the given line if it contains a teamstring -- (note npcID can be nil!) -- returns nil if line was not a team string function rematch:GetTeamStringNameAndNpcID(line) -- have to check legacy first because name:123:456:789:etc will match new team pattern local name,npcID = line:match(patterns.legacyTeam) if npcID then -- there was a match, this is a legacy team npcID = tonumber(npcID) if npcID==0 then -- if npcID is 0 then nil it npcID = nil end else -- legacy didn't match, check for new team pattern name,npcID = line:match(patterns.team) if npcID then npcID = tonumber(npcID,32) end end return name,npcID end -- returns the sidelined team in plain-text format (or nil if it can't) function rematch:ConvertSidelineToPlainText() local result = {} local team,key = rematch:GetSideline() if not key then return end -- no team sidelined, leave if type(key)=="number" then local npcName = rematch:GetNameFromNpcID(key) if npcName==team.teamName then -- name of team is same as npcID: "Name of Team (NPC#123)" tinsert(result,format(exportStubs.title_npcID_only,npcName,key)) else -- name of team is different from npcID: "Name of Team (Name of NPC (NPC#123)" tinsert(result,format(exportStubs.title_npcID_name,team.teamName,npcName,key)) end else -- named team, no npcID; first line is just the name of the team tinsert(result,key) end tinsert(result,"") -- blank line separating title from rest -- pets and abilities (1: Name of Pet (2/2/1) for i=1,3 do local speciesID, ability1, ability2, ability3 = rematch:ExportPet(team[i],3) if speciesID and not rematch:GetSpecialPetIDType(speciesID) then local name = C_PetJournal.GetPetInfoBySpeciesID(speciesID) if name then if ability3 then tinsert(result,format(exportStubs.pet_with_abilities,i,name,ability1,ability2,ability3)) else tinsert(result,format(exportStubs.pet_basic,i,name)) end end else local petName = L["Leveling Pet"] if rematch:GetSpecialPetIDType(speciesID) then petName = rematch:GetSpecialTooltip(speciesID) or petName end tinsert(result,format(exportStubs.pet_basic,i,petName)) end end -- Preferred leveling pets: at least 700 health (or any magic/mechanical, dragonkin damage expected); at most 1200 health; at least level 5; at most level 24.5. if rematch:HasPreferences(team) and not RematchSettings.DontIncludePreferences then tinsert(result,"") local subprefs = {} if team.minHP then local ddType = team.expectedDD and _G["BATTLE_PET_NAME_"..team.expectedDD] if team.allowMM and team.expectedDD then tinsert(subprefs,format(exportStubs.minHP_allowMM_expectedDD,team.minHP,ddType)) elseif team.allowMM and not team.expectedDD then tinsert(subprefs,format(exportStubs.minHP_allowMM,team.minHP)) elseif not team.allowMM and team.expectedDD then tinsert(subprefs,format(exportStubs.minHP_expectedDD,team.minHP,ddType)) else tinsert(subprefs,format(exportStubs.minHP_basic,team.minHP)) end end if team.maxHP then tinsert(subprefs,format(exportStubs.maxHP,team.maxHP)) end if team.minXP then tinsert(subprefs,format(exportStubs.minXP,team.minXP)) end if team.maxXP then tinsert(subprefs,format(exportStubs.maxXP,team.maxXP)) end -- combine all of the above to "Preferred leveling pets: (stubs separated by a ;)." tinsert(result,format(exportStubs.prefs,table.concat(subprefs,"; "))) end -- notes if any if team.notes and not RematchSettings.DontIncludeNotes then tinsert(result,"") tinsert(result,team.notes:trim()) end return table.concat(result,"\n") end