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
362 lines
14 KiB
|
3 years ago
|
--[[
|
||
|
|
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
|