Module:Pet

-- local p = {} -- Basics local loader = require('Module:Loader') local getArgs = require('Module:Arguments').getArgs

local string, table, yesno, color, currency, ability, libraryutil, uitext, rarityTier, statModule, slotutils = loader.require('String', 'Table', 'Yesno', 'Color', 'Currency', 'Ability', 'LibraryUtil', 'UIText', 'RarityTier',	'Statname', 'Inventory slot/Utils') local data, aliases, skindata, levelingdata, uitextData, colorAliases, rarityAliases, statAliases = loader.loadData('Pet/Data', 'Pet/Aliases', 'Pet/Skins', 'Pet/LevelingData', 'UIText/Data', 'Color/Aliases',	'RarityTier/Aliases', 'Statname/Aliases')

local makeColor = color._colorTemplates local makeCoins = currency._coins local makeAbility = ability._ability

local lang = mw.getContentLanguage local curtitle = mw.title.getCurrentTitle local pagename = curtitle.prefixedText local fullpagename = curtitle.fullText local isPetPage = curtitle.namespace == 0 and fullpagename:find('Pet') local reverseConv = table.invert(uitextData.conversions)

local aprilFools = false local infosymbol = 'ⓘ' local arrowsymbol = '➡'

local scale_functions = { linear = function(lv, base, per) return (base or 0) + lv * per end, Parrot = function(lv, base, per) local maxed = 15 return math.ceil(.01 * lv * (maxed - 1) + 1) end }

local function range(n) local t = {} for i = 1, tonumber(n) do t[#t + 1] = i end return t end

local function rarityOrder(rarity) return rarityTier._getTier(rarity).order end

local mycache = {} local function getPet(pet, getAdditionalInfo) local cached = mycache[pet] if cached then return cached.d, cached.pet_, cached.baselv, cached.maxlv end getAdditionalInfo = yesno(getAdditionalInfo, false) local pet_ = pet:lower:gsub(' pet$', '') pet_ = aliases[pet_] or string.ucfirst(pet_) local d = data[pet_] -- returns the data, the pet name[, the base level, the max level] if getAdditionalInfo and d then local baselv, maxlv = p._getBaseMax(pet) return d, pet_, baselv, maxlv end return d, pet_ end

function p.inquirerarities(pet) if type(pet) ~= 'string' then return '' end local dt, pet = getPet(pet) local rarities = dt and dt.rarities if not (dt and rarities) then return '' end local ret = {} for _, r in ipairs(p._discloseRarities(pet, true)) do		local pet_ = pet:gsub(' ', '_') table.push(ret, ('%s;%d'):format(string.upper(pet_), rarityOrder(r))) end return table.concat(ret, ' ') end

function p.inquireimages(pet_) if type(pet_) ~= 'string' then return end local dt, pet = getPet(pet_) local rarities, variables = dt and dt.rarities, dt and dt.variables if not (pet and dt and rarities and variables) then return end local images = {} for _, r in ipairs(p._discloseRarities(pet, true)) do		table.push(images, variables[r].petimage or ('%s Pet'):format(variables[r].petname or pet)) end local i = 1 -- remove duplicates while i <= #images do		local rm = false for j = i - 1, 1, -1 do			if images[i] == images[j] then table.remove(images, i)				rm = true end if rm then break end end i = i + (rm and 0 or 1) end return table.concat(images, ';') .. (aprilFools and ';Jerry Pet' or '') end

function p.inquireslots(pet) if type(pet) ~= 'string' then return '' end local dt, pet = getPet(pet) local rarities = dt and dt.rarities if not (dt and rarities) then return '' end local ret = {} for _, r in ipairs(p._discloseRarities(pet, true)) do		table.push(ret, (''):format(pet, rarityTier._getTier(r).short:upper)) end return table.concat(ret, '') end

function p.getSkinsList local petSkinsTable = {} for pet, skins in pairs(skindata) do for _, v in ipairs(skins) do table.push(petSkinsTable, v[1] .. ' ' .. pet .. ' Skin') end end return petSkinsTable end

function p.getPetList(prepend, append) return table.map(table.keys(data), function(petname)		return (prepend or ) .. petname .. (append or )	end) end

function p._getBaseMax(pet) local data, pet = getPet(pet) local baselv, maxlv if pet and data.levels then baselv, maxlv = data.levels:match('(%d+)[^%d](%d+)') end return tonumber(baselv) or 0, tonumber(maxlv) or 100 end

function p._discloseRarities(pet, returnAsTable) local data, pet = getPet(pet)

local petRarities = (data.rarities):upper:gsub('[^%a]', '') local ret = string.split(petRarities, '')

if yesno(returnAsTable, false) then return table.map(ret, function(v)			return rarityAliases[v:lower] or v		end) end

return table.concat(table.map(ret, function(v) return (''):format(v) end), ' / ') end

-- Template:Pet level stat -- -- Calculator that makes an HTML table showing information about a pet at a certain level and rarity

function p.requestStat(frame) local args = getArgs(frame) local pet = args[1] or args.p or args.pet or (isPetPage and fullpagename) or 'ERROR' local level = args[2] or args.level local rarity = args[3] or args.rarity

return mw.getCurrentFrame:preprocess(p._requestStat(pet, level, rarity)) end

-- Template:Pet slot -- -- Creates a slot showing a pet at a certain level and rarity

function p.petSlot(frame) local args = getArgs(frame) local pet = args[1] or args.p or args.pet or (isPetPage and fullpagename) or 'ERROR' local level = args[2] or args.level local rarity = args[3] or args.rarity

return mw.getCurrentFrame:preprocess(p._petSlot(pet, level, rarity)) end

-- Module Access Point function p._specificPetHandler(mode, pet, level, rarity) local data, pet, baselv, maxlv = getPet(pet, true) if not data then return string.wrapHtml('Unrecognized Pet: ' .. pet, 'span', { class = 'error' }) end

level = tonumber(level) or 1

local petRarities = p._discloseRarities(pet, true) rarity = rarity or petRarities[1]

local raritykey = rarityAliases[rarity:lower]

if not table.includes(petRarities, raritykey) then return string.wrapHtml(('%s Pet does not have such rarity: %s Rarities available: %s')			:format(pet, rarity, p._discloseRarities(pet)), 'span', { class = 'error' }) end if level maxlv then return string.wrapHtml(('%s Pet cannot have such level: %s Valid levels: %s to %s'):format(pet, level, baselv + 1, maxlv), 'span', { class = 'error' }) end

local stats, heldItem = data.variables[raritykey].stats or data.stats, data.heldItem if not stats then return string.wrapHtml(('Missing pet stats (Pet: %s; Rarity: %s)'):format(pet, raritykey), 'span',			{ class = 'error' }) end

local function tableview local wikitable = mw.html.create('table'):addClass('wikitable article-petlevelstats') wikitable:tag('tr') :tag('th'):wikitext((' %s Pet'):format( pet, fullpagename:match(pet) and '' or (pet .. ' Pet'), pet )):addClass('article-petlevelstats-title'):attr('rowspan', 2):done :tag('th'):wikitext(('Lv.%d'):format(level)):addClass('article-petlevelstats-level'):done :done

wikitable:tag('tr') :tag('th'):wikitext(rarityTier._link(rarity)):addClass('article-petlevelstats-tier'):done :done

wikitable:tag('tr') :tag('td'):wikitext(table.concat {				string.wrapHtml(p._petSlot(pet, level, rarity), 'div', { class = 'article-petlevelstats-slot' }),				p.constructStats(stats, level, raritykey, baselv),				p.processAbilities(pet, raritykey, level),				p.processHeldItems(pet, raritykey, level)			}) :addClass('article-petlevelstats-main') :attr('colspan', 3) :done :done

return tostring(wikitable) end

local function slotview local slot = slotutils._useTemplate(			{ p.petTemplateCreator(pet, level, rarity, 'THIS') }, 'T:Pet'		)

local params = '' for k, v in pairs(slot['THIS']) do			params = ('%s|%s = %s'):format(params, k, v)		end if (fullpagename:match(pet)) then params = params .. '|link=none' end

return (''):format(pet, params) end

mode = mode and tostring(mode):lower or 'table' if mode == 'table' then return tableview(pet, level, rarity) elseif mode == 'slot' then return slotview(pet, level, rarity) else return string.wrapHtml(('No such mode "%s"'):format(mode), 'span', { class = 'error' }) end end

p._requestStat = bind(p._specificPetHandler, 'table') p._petSlot = bind(p._specificPetHandler, 'slot')

-- Template:PetStats -- -- Makes an HTML table showing information about a pet.

function p.petStatsTable(frame) local args = getArgs(frame) local pet = args[1] or args['p'] or args['pet'] or (isPetPage and fullpagename) or 'ERROR'

if pet == 'ERROR' then return string.error('Pet name expected, got none') end

return mw.getCurrentFrame:preprocess(p._petStatsTable(pet)) end

-- (Template:PetStats) Module Access Point function p._petStatsTable(pet) local data, pet, baselv, maxlv = getPet(pet, true) if not data then error('Unrecognized Pet: ' .. pet) end

local startlv = tonumber(baselv) + 1

local tabs = {}

for _, raritykey in ipairs(p._discloseRarities(pet, true)) do		local stats, variables = data.variables[raritykey].stats or data.stats, data.variables if not stats then return error(string.format('Missing pet stats (Pet: %s)', pet), 2) end -		-- Headers -		local ved_buttons = string.wrapHtml(			'VED'			,			'span', { class = 'table-vedbuttons' }		) local wikitable = mw.html.create('table'):addClass('wikitable full-width') wikitable:tag('tr') :tag('th'):wikitext(('Starting Stats (Level %d) %s '):				format(startlv, infosymbol)) :addClass('half-width'):done :tag('th'):wikitext(('Max Stats (Level %d) %s %s'):format(maxlv , infosymbol, ved_buttons)) :addClass('half-width'):done :done

local petname = variables[raritykey] and variables[raritykey].petname or pet

wikitable:tag('tr') -			-- Base stats -			:tag('td'):wikitext(table.concat {				p.constructStats(stats, startlv, raritykey, baselv),				p.processAbilities(pet, raritykey, startlv),				p.processHeldItems(pet, raritykey, startlv)			}):done -			-- Max level stats -			:tag('td'):wikitext(table.concat {				p.constructStats(stats, maxlv, raritykey, baselv),				p.processAbilities(pet, raritykey, maxlv),				p.processHeldItems(pet, raritykey, maxlv)			}):done :done -		-- Bonus headers -		wikitable:tag('tr') :tag('th'):attr({ colspan = 2 }):wikitext(( 'Leveling Bonuses%s '):format(infosymbol)): done :done wikitable:tag('tr') -			-- Bonus stats -			:tag('td'):attr({ colspan = 2 }):wikitext(table.concat {				p.constructStats(stats, 'bonus', raritykey, baselv),				p.processAbilities(pet, raritykey, 'bonus')			}):done tabs[#tabs + 1] = table.concat { '|-|',			rarityTier._getTier(raritykey).category, ' = ',			string.wrapHtml((rarityTier._link(raritykey) .. ' ' .. petname .. ' Pet'), 'div',				{ class = 'article-petstats-title' }), tostring(wikitable), (raritykey == 'mythic' and  or ) }	end

return table.concat { ' ', table.concat(tabs), ' ' } end

--	Helper Functions for p._petStatsTable: --	├── 1.1 p.constructStats --	│	└── 1.2 p._getStat --	├── 1.3 p.processAbilities --	│	└── 1.4 p.processVars --	│		└── 1.5 p._getVariable --	└── 1.6 p.processHeldItems --		└── (Same as p.processAbilities)

--	Helper Functions for p._petStatsTable: 1.1

function p.constructStats(stats, petlevel, rarity, baselv) rarity = rarityAliases[rarity] or rarity

local list = {} for key, value in pairs(stats) do		local number = p._getStat(value, petlevel, rarity, baselv, true) local rawnumber = p._getStat(value, petlevel, rarity, baselv, false)

if number then local sdt = statModule._getstatdata(key) table.push(list, string.wrapHtml({ statModule.stat(sdt.name), (rawnumber < 0 and ': ' or ': +'), number, (sdt.percent and '%' or ''), (petlevel == 'bonus' and ' per level' or '') }, 'li')) end end

return table.concat { string.wrapHtml(petlevel == 'bonus' and 'Bonus stats:' or 'Stats:', 'span',			{ class = 'color-turquoise hsw-gamefont bold' }		), string.wrapHtml(list, 'ul') } end

--	Helper Functions for p._petStatsTable: 1.2 -- Calculates the value of a specific stat for a specific level/rarity -- Returns nil if pet level is lower than that the required level for such stat

function p._getStat(statValues, petlevel, rarity, baselv, includeExactValue) local reqRarity = statValues.req and rarityAliases[statValues.req:lower] or nil

if not statValues.req or rarityOrder(rarity) >= rarityOrder(reqRarity) then local lvl = statValues.level or statValues.lvl or statValues.bonus or statValues[1] local base = statValues.base or statValues[2] or 0 local num if tonumber(petlevel) then num = base + (tonumber(petlevel) - baselv) * lvl elseif petlevel == 'bonus' then num = lvl end

local rounded = math.floor(num) -- round DOWN to integer includeExactValue = includeExactValue and (num ~= rounded)

if petlevel == 'bonus' then return num end -- return exact value for leveling bonuses

return includeExactValue and string.makeTitle(tostring(rounded),				('Rounded down to the nearest integer;&#10;The exact value is %s.'):format(num)) or rounded else return nil end end

--	Helper Functions for p._petStatsTable: 1.3

function p.processAbilities(pet, rarity, petlevel) local data, pet, baselv, maxlv = getPet(pet, true) local ab, variables = data.abilities, data.variables local abilityList = variables[rarity].ability_indices or range(variables[rarity].ability_count)

local list = {} for _, i in ipairs(abilityList) do		if tonumber(petlevel) then desc = p.processVars(pet, rarity, petlevel, ab.desc[i]) table.push(list,				string.wrapHtml({ makeAbility(ab.name[i], false, 'abilityName'), ' - ', desc }, 'li')			) elseif petlevel == 'bonus' then desc = p.processVars(pet, rarity, petlevel, ab.bonus_desc[i] or 'No leveling bonus') table.push(list,				string.wrapHtml({ makeAbility(ab.name[i], false, 'abilityName'), ': ', desc }, 'li')			) end end

return table.concat { makeAbility('Abilities:'), string.wrapHtml(list, '', { class = 'article-petstats-abilities' }) } end

--	Helper Functions for p._petStatsTable: 1.4

function p.processVars(pet, raritykey, petlevel, desc) local data, pet, baselv, maxlv = getPet(pet, true) local variable = data.variables[raritykey]

local function xmlColorConvert(s) return s:gsub(			'(.-)',			function(tag, str, endTag)				tag = string.trim(tag):lower				endTag = string.trim(endTag):lower				if tag and (not endTag or endTag == '')				then error(string.format('XML color tags syntax error: No closing tag found for tag ', tag), 4)				elseif tag:lower ~= endTag:lower				then error( string.format(							'XML color tags syntax error: Expected closing tag for \'\' is \'\', got ',							tag,							tag,							endTag						), 4)				end				local c = colorAliases[tag] or reverseConv[tag]				return c and makeColor(c, str) or '<' .. tag .. '>' .. str .. ''			end		) end

local function processSTAT(str) if str:find('STAT_[%u-]+') then str = str:gsub('STAT_([%u-]+)', function(s)				return statModule._getStatName(s)			end) end if str:find('STATs_([%u-]+)') then str = str:gsub('STATs_([%u-]+)', function(s)				return statModule._getStatName(s, nil, true)			end) end return str end

local function processRARITY(str) if str:find('RARITY_%u+') then str = str:gsub('RARITY_(%u+)', function(s)				local dt = rarityTier._getTier(s)				return rarityTier._link(s)			end) end return str end

local function replaceCoins(str) return str:gsub('COINS', tostring(makeCoins)) end

local function formatFakeAbility(s) return s:gsub('{ABILITY}(.+){/ABILITY}', function(s)			return makeAbility(s, false, 'abilityName')		end) end

local function processVariables(index, petlevel) local num = p._getVariable(pet, raritykey, index, petlevel, true)

--add suffix num = tostring(num) .. (variable[index].suffix or '')

--colorize return makeColor(variable[index].color, num) end

desc = formatFakeAbility(replaceCoins(processRARITY(processSTAT(xmlColorConvert(desc)))))

if variable then for i in desc:gmatch('{(%d+)}') do			local j = tonumber(i) local subst = processVariables(j, petlevel, baselv) desc = subst and desc:gsub(('{%d}'):format(j), subst) or desc end end

return desc end

--	Helper Functions for p._petStatsTable: 1.5

--	Also a helper function for p.getPetsTemplate

function p._getVariable(pet, raritykey, index, petlevel, includeExactValue) local data, pet, baselv, maxlv = getPet(pet, true) local variable = data.variables[raritykey][index]

local this = (' (Pet: %s; Rarity: %s; Index: %s)'):format(pet, raritykey, index) if not variable then error('Variable table missing.' .. this) end if not variable.color then error('Variable arg missing: "color"' .. this) end if not (variable.per_lvl or variable.eval) then return error('Variable arg missing: "per_lvl"/"eval"' .. this, 2) end

local eval = scale_functions[variable.eval or 'linear']

local num if tonumber(petlevel) then local levels_increased = tonumber(petlevel) - baselv num = eval(levels_increased, variable.base, variable.per_lvl) elseif petlevel == 'bonus' then num = variable.per_lvl end

local rounded = variable.round_down and math.floor(num) or math.floor(num * 10000 + 0.5) / 10000 -- round OFF to 4 d.p.	includeExactValue = (not variable.round_down and includeExactValue) and (num ~= rounded) or false

rounded = variable.is_roman and string._toRoman(rounded) or rounded num = variable.is_roman and string._toRoman(num) or num

if petlevel == 'bonus' then return num end -- return exact value for leveling bonuses

return includeExactValue and string.makeTitle(tostring(rounded), ('Rounded off to 4 decimal place;&#10;The exact value is %s.'):format(num)) or rounded end

--	Helper Functions for p._petStatsTable: 1.6

function p.processHeldItems(pet, rarity, petlevel) local data, pet, baselv, maxlv = getPet(pet, true) local heldItem = data.heldItem

local list, desc if heldItem and (tonumber(petlevel)) and rarity == heldItem.req then desc = p.processVars(pet, rarity, petlevel, heldItem.desc) list = string.wrapHtml(desc, 'li') else return end

return table.concat { makeAbility('Held Item: ' .. heldItem.name, false, 'abilityName'), string.wrapHtml { list, '', { class = 'article-petstats-helditem' } }	} end

--- -- Generates table for Module:Inventory slot/Templates --- function p.getPetsTemplate local allPackages = table.map(table.keys(data), function(pet)		local returnPackage = {}		local data, pet, baselv, maxlv = getPet(pet, true)

if not data then return end if not data.rarities then return end if not data.variables then return end

local leveldisplay = tonumber(baselv) + 1 .. arrowsymbol .. maxlv local allrarities = p._discloseRarities(pet, true)

for i, raritykey in ipairs(allrarities) do			local raritykey = rarityAliases[raritykey] local petname = data.variables[raritykey] and data.variables[raritykey].petname

local id = ('%s Pet (%s)'):format(pet, rarityTier._getTier(raritykey).short:upper)

local res = p.petTemplateCreator(pet, { baselv + 1, maxlv }, raritykey, id)

if petname then local res2 = table.deepCopy(res, true) res2['id'] = petname .. ' Pet' table.push(returnPackage, res2) end if i == #allrarities then local res2 = table.deepCopy(res, true) res2['id'] = '{o} Pet' table.push(returnPackage, res2) end

table.push(returnPackage, res) end return returnPackage end)

local ret = {} table.each(allPackages, function(pack)		table.each(pack, function(t) table.push(ret, t)		end)	end)

return ret end

function p.petTemplateCreator(pet, level, rarity, id) -- level can be a table of two values; id can be custom. local function numericOutput(min, max) if (not min or not max or min == max) then return min or max else return min .. arrowsymbol .. max end end

local data, pet, baselv, maxlv = getPet(pet, true)

local min_, max_ = level, nil if type(level) == 'table' then min_, max_ = level[1], level[2] end min_, max_ = tonumber(min_), tonumber(max_) local twovals = not not min_ and not not max_ and (min_ ~= max_)

local leveldisplay = twovals and ('%s%s%s'):format(min_, arrowsymbol, max_) or min_

local raritykey = rarityAliases[rarity:lower] local stats, heldItem, variables = data.variables[raritykey].stats or data.stats, data.heldItem, data.variables local raritynum = rarityOrder(raritykey) + 1 local petname = variables[raritykey] and variables[raritykey].petname or pet local petimage = variables[raritykey] and variables[raritykey].petimage

local statStr = {} for key, value in pairs(stats) do		local min = p._getStat(value, min_, raritykey, baselv, false) local max = twovals and p._getStat(value, max_, raritykey, baselv, false) if min then -- if the stat is available to that level local sdt = statModule._getstatdata(key) table.push(statStr, ('&7%s: &a%s%s%s'):format( sdt.name, (min < 0 and '' or '+'), numericOutput(min, max) or '', sdt.percent and '%' or '' ))		end end statStr = table.concat(statStr, '/') .. '//'

local ab_names, ab_tooltips = data.abilities.name, data.abilities.tooltip or data.abilities.tooltips

local abilityStr = {} if ab_tooltips and (#ab_names == #ab_tooltips) then local abilityList = variables[raritykey].ability_indices or range(variables[raritykey].ability_count)

for _, i in ipairs(abilityList) do			local name = ab_names[i] if not ab_tooltips[i] then error(pet .. ' Pet: desc and tooltip length mismatch') end local desc = ab_tooltips[i]

local vars = variables[raritykey] if vars then for k in desc:gmatch('{(%d+)}') do					local j = tonumber(k) if not vars[j] then error(('Variable for {%s} missing value (Pet: %s; Rarity: %s)'):format(i, pet, raritykey)) end local min = p._getVariable(pet, raritykey, j, min_, false) local max = twovals and p._getVariable(pet, raritykey, j, max_, false) local subst = numericOutput(min, max) desc = subst and desc:gsub(('{%d}'):format(j), subst) or desc end end

table.push(abilityStr, ('&6%s/&r%s'):format(name, desc:gsub('^/+(.*)', '%1'):gsub('(.-)/+$', '%1'))) end end abilityStr = table.concat(abilityStr, '//') .. '//'

local heldItemTooltip = heldItem and (heldItem.tooltip or heldItem.tooltips) local extraStr = (heldItem and heldItemTooltip and (raritykey == rarityAliases[heldItem.req:lower])) and ('&6Held Item: %s/&r%s//'):format(heldItem.name, heldItemTooltip:gsub('^/+(.*)', '%1'):gsub('(.-)/+$', '%1')) or '' extraStr = extraStr .. (data.isPassive		and '&8This pet\'s perks are active/&8even when the pet is not/&8summoned!//'		or '')

return { pet, petname, data.petType, leveldisplay, statStr, abilityStr, extraStr, r = raritykey, id = id, image = petimage } end

--- -- Generates table for Module:Inventory slot/Templates --- function p.getMysteryTemplate local ls = table.map(table.keys(data), function(petname)		local petData = data[petname]		local baselv, maxlv = p._getBaseMax(petname)		local stats = {}		local rarities = p._discloseRarities(petname, true)		local highestrarity = rarities[#rarities]		for stat, value in pairs(petData.variables[highestrarity].stats or petData.stats) do			local num = p._getStat(value, 1, highestrarity, baselv, false)			local sdt = statModule._getstatdata(stat)			table.push(stats, ('&r%s: &a+%s%s'):format(sdt.name, num, sdt.percent and '%' or ''))		end		return { petname, petData.petType, table.concat(stats, '/') }	end)

return table.merge(ls, { id = 'Mystery {o} Pet' }) end

--- -- Generates table for Module:Inventory slot/Templates --- function p.getSkinsTemplate local petSkinsTable = {} for petname, skins in pairs(skindata) do		for _, v in ipairs(skins) do			local attrs = string.split(v[2], '%s+') local r = rarityTier._getTier(attrs[1]).name table.remove(attrs, 1) local animated, daynight, factionadapt = false, false, false for _, attr in ipairs(attrs) do				if attr:lower == 'animated' then animated = true elseif attr:lower == 'daynight' then daynight = true elseif attr:lower == 'factionadapt' then factionadapt = true end end local suppStr = string.format('%s%s%s',				animated and '&5This skin is animated!//' or ,				daynight and '&5This skin adapts to the/&5day-night cycle!//' or ,				factionadapt and '&5This skin adapts to your/&5Crimson Isle Faction!//' or '') table.push(petSkinsTable, { ('%s %s Skin'):format(v[1], petname), petname, suppStr, r = r }) end end return petSkinsTable end

--- -- Template:Pet XP Table -- -- Outputs the pet xp per level table -- Accepts a pet base level or pet name (used to determine the base level) as input. -- Without such input, the page name will be used to determine the base level. --- function p.xpTable(frame) local args = getArgs(frame) local pet = args[1] or args['p'] or args['pet'] or args['lvl'] or args['level'] or (isPetPage and fullpagename) or nil local data, pet_ = pet and getPet(pet)

local tabledata = {} local rarities = (data and data.discloseXP) or (pet and p._discloseRarities(pet, true)) or {} local higherLevels = (pet and p._getBaseMax(pet) or 0) > 99 -- if base level is large, display xp for pet lv.101-200 if not pet then for k in rarityTier._getTierIterable do			table.push(rarities, k)		end end

for _, k in ipairs(rarities) do		local t = levelingdata[k] if t then local cur = #tabledata + 1 tabledata[cur] = table.map(t, function(n)				return string.wrapHtml(string._formatNum(n), 'span', { class = 'color-green' })			end) tabledata[cur].title = rarityTier._link(k) end end

local wikitable = mw.html.create('table'):attr('id', 'mw-customcollapsible-xp-table') :addClass('article-table'):addClass('sortable'):addClass('mw-collapsible'):addClass('mw-collapsed') :tag('tr'):tag('th'):attr('colspan', 25) :wikitext('  XP Needed to Upgrade This Pet from its Previous Level   ') :done:done

local row = wikitable:tag('tr'):addClass('table-nocollapse'):tag('th'):wikitext('Level'):done for _, v in ipairs(tabledata) do		row:tag('th'):wikitext(v.title) end if higherLevels then row:tag('th'):wikitext('Level') for _, v in ipairs(tabledata) do			row:tag('th'):wikitext(v.title) end end

for n = 1, 100, 1 do		row = wikitable:tag('tr'):tag('td'):addClass('bold') :attr('data-sort-value', n):wikitext(('Lvl %s'):format(n)):done for _, v in ipairs(tabledata) do			row:tag('td'):wikitext(v[n] or '') end if higherLevels then local n2 = n + 100 row:tag('td'):addClass('bold') :attr('data-sort-value', n2):wikitext(('Lvl %s'):format(n2)):done for _, v in ipairs(tabledata) do				row:tag('td'):wikitext(v[n2] or '') end end end

return tostring(wikitable:allDone) end

--- -- Template:Calculator/Pet_Leveling_XP -- -- Outputs the pet xp per level table using the base level, the initial and final levels --- function p.calcXP(frame) local args = getArgs(frame) local rarity, lvl_o, lvl_f = args.rarity, tonumber(args.initial), tonumber(args.final)

if not (rarity and lvl_o and lvl_f) then return string.wrapHtml('Input is invalid', 'span', { class = 'color-red' }) end

local rtable, rkey = rarityTier._getTier(rarity)

if lvl_o < 1 or lvl_o > 200 or lvl_f < 1 or lvl_f > 200 or lvl_f < lvl_o then return string.wrapHtml('Input is invalid', 'span', { class = 'color-red' }) end

if (lvl_f > 100 and rkey ~= 'legendary') then return string.wrapHtml(('No %s pet can be upgraded beyond level 100'):format(rkey), 'span',			{ class = 'color-red' }) end

local levels = levelingdata[rkey]

local tot = 0 for i = lvl_o + 1, lvl_f, 1 do		tot = tot + levels[i] end

return ('Total XP required: %d'):format(tot) end

return p