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.

362 lines
14 KiB

--[[
This module handles the conversion of sidelined teams into strings and vice versa.
Old string format: <name>:<npcID>:<speciesID>:<abilityID>:<abilityID>:<abilityID>:
<speciesID>:<abilityID>:<abilityID>:<abilityID>:<speciesID>:<abilityID>:
<abilityID>:<abilityID>:
New string format: <name>:<npcID>:<petTag>:<petTag>:<petTag>:
Preferences format: P:<minHP>:<allowMM>:<expectedDD>:<maxHP>:<minXP>:<maxXP>:
(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:<notes text to end of line>
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