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 checkType = libraryutil.checkType local getFormatting = uitext.getFormatting

local lang = mw.getContentLanguage local curtitle = mw.title.getCurrentTitle local pagename = curtitle.prefixedText local fullpagename = curtitle.fullText local reverseConv = table.invert(uitextData.conversions)

local infosymbol = 'ⓘ' local arrowsymbol = '➜'

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

local function getPet(pet) local pet_ = pet:lower:gsub(' pet$','') pet_ = aliases[pet_] or string.ucfirst(pet_) -- returns the data and the pet name return data[pet_], 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 rarities = rarities:upper:gsub('[^%a]', '') local ret = {} for i = 1, #rarities, 1 do		local r = rarities:sub(i,i) local p = pet:gsub(' ','_') table.push(ret, ('%s;%d'):format(string.upper(p), rarityOrder(r))) end return table.concat(ret, ' ') 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 rarities = rarities:upper:gsub('[^%a]', '') local ret = {} for i = 1, #rarities, 1 do		local r = rarities:sub(i,i) table.push(ret, (''):format(pet, rarityTier._getTier(r).short:upper)) end return table.concat(ret, '') end

function p.getSkinsList local petSkinsTable = {} for _, petName in ipairs(table.keys(data)) do		local skins = data[petName].skins; if skins then for _,v in ipairs(skins) do table.push(petSkinsTable, v[1]) end 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._rarityExists(pet, rarity) local data, pet = getPet(pet) local petRarities = (data.rarities):upper:gsub('[^%a]', '') for i = 1, #petRarities do		local r = petRarities:sub(i,i) if rarityOrder(r) == rarityOrder(rarity) then return true end end return false end

function p._discloseRarities(pet) local data, pet = getPet(pet) local petRarities = (data.rarities):upper:gsub('[^%a]', '') local ret = string.split(petRarities, '') 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 (curtitle.namespace == 0 and (fullpagename:find('Pet') and fullpagename or 'ERROR') 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 (curtitle.namespace == 0 and (fullpagename:find('Pet') and fullpagename or 'ERROR') 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 = getPet(pet) if not data then return string.wrapHtml('Unrecognized Pet: '..pet, 'span', { class = 'error' }) end level = tonumber(level) or 1 local petRarities = (data.rarities):upper:gsub('[^%a]', '') rarity = rarity or petRarities:sub(1,1) local stats, heldItem = data.stats, data.heldItem local baselv, maxlv = p._getBaseMax(pet) if not stats then return string.wrapHtml(('Missing pet stats (Pet: %s)'):format(pet), 'span', { class = 'error' }) end if not p._rarityExists(pet, rarity) 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'):format(pet, level), 'span', { class = 'error' }) end local raritykey = rarityAliases[rarity:lower] local function tableview local wikitable = mw.html.create('table'):addClass('wikitable') wikitable:tag('tr') :tag('th'):wikitext((' %s Pet'):format(pet, pet, pet)):css({				width = '70%',				['font-size'] = '3em',				['white-space'] = 'nowrap',				padding = '0 1em !important',			}):attr('rowspan', 2):done :tag('th'):wikitext(('Lv.%d'):format(level)):css('font-size', '1.6em'):done :done wikitable:tag('tr') :tag('th'):wikitext(('%s '):format(rarityTier._link(rarity))):done :done wikitable:tag('tr') :tag('td'):wikitext(table.concat{				string.wrapHtml(p._petSlot(pet, level, rarity), 'div', { style = 'float: right;' }),				p.constructStats(stats, level, raritykey, baselv),				p.processAbilities(data.abilities, level, raritykey, baselv),				p.processHeldItems(heldItem, level, raritykey),			}) :css('padding', '1em 2em 0 2em !important') :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 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 (curtitle.namespace == 0 and (fullpagename:find('Pet') and fullpagename or 'ERROR') or 'ERROR') if pet == 'ERROR' then return string.error('Pet name expected, got none') end return mw.getCurrentFrame:preprocess(string.pcall(p._petStatsTable, pet)) end

-- (Template:PetStats) Module Access Point function p._petStatsTable(pet) local data, pet = getPet(pet) if not data then error('Unrecognized Pet: '..pet) end --create various variables local stats, heldItem = data.stats, data.heldItem local baselv, maxlv = p._getBaseMax(pet) local startlv = tonumber(baselv) + 1 if not stats then return error(string.format('Missing pet stats (Pet: %s)', pet), 2) end local tabs = {} local rarities = (data.rarities):upper:gsub('[^%a]', '') for i = 1, #rarities, 1 do		- -- Headers -		local ved_buttons = string.wrapHtml(			'V • E • D', 'span',			{ style = 'float: right; font-size: 11px;' }		) local wikitable = mw.html.create('table'):addClass('wikitable'):css({ ['width'] = '100%' }) wikitable:tag('tr') :tag('th'):wikitext(('Starting Stats (Level %d) %s '):format(startlv, infosymbol)):css({ ['width'] = '50%' }):done :tag('th'):wikitext(('Max Stats (Level %d) %s %s'):format(maxlv, infosymbol, ved_buttons)):css({ ['width'] = '50%' }):done :done local rarityletter = rarities:sub(i,i) local raritykey = rarityAliases[rarityletter:lower] wikitable:tag('tr') -			-- Base stats -			:tag('td'):wikitext(table.concat{				p.constructStats(stats, startlv, raritykey, baselv),				p.processAbilities(data.abilities, startlv, raritykey, baselv),				p.processHeldItems(heldItem, startlv, raritykey),			}):done -			-- Max level stats -			:tag('td'):wikitext(table.concat{				p.constructStats(stats, maxlv, raritykey, baselv),				p.processAbilities(data.abilities, maxlv, raritykey, baselv),				p.processHeldItems(heldItem, maxlv, raritykey),			}):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(data.abilities, 'bonus', raritykey),			}):done tabs[#tabs+1] = table.concat{ '|-|',			rarityTier._getTier(rarityletter).name, ' = ',			string.wrapHtml(('%s %s Pet'):format(tostring(rarityTier._link(raritykey)), pet), 'div', { style = {				['font-size'] = '1.6em',				['text-align'] = 'center',				['margin'] = '.5em 0',			}}), 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) checkType('constructStats', 1, stats, 'table') checkType('constructStats', 2, petlevel, {'string', 'number'}) 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 table.push(list, string.wrapHtml({ tostring(statModule._getStatName(key)), (rawnumber < 0 and ': ' or ': +'), number, (petlevel == 'bonus' and ' per level' or '') }, 'li')) end end return table.concat{ petlevel == 'bonus' and 'Bonus stats:' or 'Stats:', 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) 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(ab, petlevel, rarity, baselv) checkType('processAbilities', 1, ab, 'table') checkType('processAbilities', 2, petlevel, {'string', 'number'}) checkType('processAbilities', 3, rarity, 'string') --if not ab.names[ab.amounts] then error(string.format('Missing ability name #%s', amount), 2) end --if not ab.descs[ab.amounts] then error(string.format('Missing ability description #%s', amount), 2) end local amounts = ab.amounts and ab.amounts:gsub('[^%d]', '') local raritynum = rarityOrder(rarity) + 1 local amount = amounts and amounts:sub(raritynum, raritynum) or table.length(ab.variables[rarity]) local list = {} for i = 1, tonumber(amount), 1 do		if tonumber(petlevel) then desc = p.processVars(ab.desc[i], petlevel, ab.variables[rarity][i], baselv) table.push(list,					string.wrapHtml({ makeAbility(ab.name[i], false, 'abilityName'), ' - ', desc }, 'li')			) elseif petlevel == 'bonus' then desc = p.processVars(ab.bonus_desc[i] or 'No leveling bonus', petlevel, ab.variables[rarity][i], baselv) table.push(list,				string.wrapHtml({ makeAbility(ab.name[i], false, 'abilityName'), ': ', desc }, 'li')			) end end return table.concat{ makeAbility('Abilities:'), string.wrapHtml(list, '', { style = 'list-style-type: circle; line-height: inherit;' }), } end

--	Helper Functions for p._petStatsTable: 1.4

function p.processVars(desc, petlevel, vars, baselv) local function xmlColorConvert(s) checkType('xmlColorConvert', 1, s, 'string') 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)				local dt = statModule._getstatdata(s)				return statModule._getStatName(s)			end) end if str:find('STATs_([%u-]+)') then str = str:gsub('STATs_([%u-]+)', function(s)				local dt = statModule._getstatdata(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) checkType('formatFakeAbility', 1, s, 'string') return s:gsub('{ABILITY}(.+){/ABILITY}', function(s)			return makeAbility(s, false, 'abilityName')		end) end local function processVariables(variable, petlevel, baselv) if not variable then return end if not variable.color then error(string.format('Variable argument missing (color) (Pet: %s)', pet), 2) end local num = p._getVariable(variable, petlevel, baselv, true) --add suffix num = tostring(num) .. (variable.suffix or '') --colorize return makeColor(variable.color, num) end desc = formatFakeAbility(replaceCoins(processRARITY(processSTAT(xmlColorConvert(desc))))) if vars then for i in desc:gmatch('{(%d+)}') do			local j = tonumber(i) local subst = processVariables(vars[j+1], 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(variable, petlevel, baselv, includeExactValue) if not variable.per_lvl then return error('Variable argument missing (per_lvl)', 2) end local num if tonumber(petlevel) then num = (variable.base or 0) + (tonumber(petlevel) - baselv) * variable.per_lvl elseif petlevel == 'bonus' then num = variable.per_lvl end local rounded = variable.round_to_full and math.floor(num) or math.floor(num*10) / 10 includeExactValue = includeExactValue and (num ~= rounded) 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 down to 1 decimal place;&#10;The exact value is %s.'):format(num)) or rounded end

--	Helper Functions for p._petStatsTable: 1.6

function p.processHeldItems(heldItem, petlevel, rarity) checkType('processHeldItems', 1, heldItem, 'table', true) checkType('processHeldItems', 2, petlevel, { 'string', 'number' }) checkType('processHeldItems', 3, rarity, 'string') local list, desc if heldItem and (tonumber(petlevel)) and rarity == heldItem.req then desc = p.processVars(heldItem.desc, petlevel, nil, nil) list = string.wrapHtml(desc, 'li') else return end return table.concat{ makeAbility('Held Item: '..heldItem.name, false, 'abilityName'), string.wrapHtml{ list, '', { style = 'list-style-type: none; line-height: inherit;' } }	} 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 = getPet(pet)		if not data then return end		if not data.rarities then return end		local baselv, maxlv = p._getBaseMax(pet)		local leveldisplay = tonumber(baselv) + 1 .. arrowsymbol .. maxlv		local rarities = (data.rarities):upper:gsub('[^%a]', '')		for i = 1, #rarities, 1 do			local rarityletter = rarities:sub(i,i)			local id = ('%s Pet (%s)'):format(pet, rarityTier._getTier(rarityletter).short:upper)			local res = p.petTemplateCreator(pet, { baselv + 1, maxlv }, rarityletter, id)			if i == #rarities 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 .. '➜' .. max end end local data, pet = getPet(pet) 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 stats, heldItem = data.stats, data.heldItem local baselv, maxlv = p._getBaseMax(pet) local raritykey = rarityAliases[rarity:lower] local raritynum = rarityOrder(raritykey) + 1 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 table.push(statStr,('&7%s: &a%s%s'):format( statModule._getstatdata(key).name, (min < 0 and '' or '+'), numericOutput(min, max) 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 amounts = data.abilities.amounts and data.abilities.amounts:gsub('[^%d]', '') local amount = amounts and amounts:sub(raritynum, raritynum) or table.length(data.abilities.variables[raritykey]) for i = 1, tonumber(amount), 1 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 = data.abilities.variables[raritykey][i] if vars then for k in desc:gmatch('{(%d+)}') do					local j = tonumber(k) if not vars[j+1].per_lvl then error(('Variable argument missing (per_lvl) (Pet: %s; Rarity: %s)'):format(pet, raritykey)) end local min = p._getVariable(vars[j+1], min_, baselv, false) local max = twovals and p._getVariable(vars[j+1], max_, baselv, 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 heldItemStr = (heldItem and heldItemTooltip and (raritykey == rarityAliases[heldItem.req:lower])) and ('&6Held Item: %s/&r%s//'):format( heldItem.name, heldItemTooltip:gsub('^/+(.*)','%1'):gsub('(.-)/+$','%1') ) or '' return { pet, data.petType, leveldisplay, statStr, abilityStr, heldItemStr, r = raritykey, id = id } 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 stats = {}		for stat, value in pairs(petData.stats) do			if not value.req then				value = tonumber(value[1])				table.push(stats, ('&r%s: &a%s'):format(string.ucfirst(statAliases[stat]), value < 0 and value or '+'..value))			end		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 = false, false for _,attr in ipairs(attrs) do				if attr:lower == 'animated' then animated = true elseif attr:lower == 'daynight' then daynight = true end end local suppStr = string.format('%s%s',				animated and getFormatting(r,'This skin is animated!//') or ,				daynight and getFormatting(r,'This skin adapts to the day-night cycle!//') or ) table.push(petSkinsTable, { v[1], petName, r:upper, getFormatting(r), suppStr }) 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 inp = args[1] or args['p'] or args['pet'] or args['lvl'] or args['level'] or curtitle.namespace == 0 and fullpagename:find('Pet') and fullpagename or nil local tabledata = {} local levels = levelingdata.pet_levels local base = tonumber(inp) or p._getBaseMax(inp) for k,v in rarityTier._getTierIterable do		local offset = levelingdata.pet_rarity_offset[k] if offset then local t = table.slice(levels,1+offset,99+offset) local cur = #tabledata+1 tabledata[cur] = table.map(table.merge({0}, t), function(n)				return string.wrapHtml(string._formatNum(n), 'font', {style = 'color:#55FF55'})			end) tabledata[cur].title = rarityTier._link(k) mw.log(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',('%d'):format(#tabledata + 1)) :wikitext('  XP Needed to Upgrade This Pet from its Previous Level   ') :done:done local row = wikitable:tag('tr'):tag('th'):wikitext('Level'):done for _,v in ipairs(tabledata) do		row:tag('th'):wikitext(v.title) end for n = 1, 100, 1 do		row = wikitable:tag('tr'):tag('td'):css('font-weight','bold') :attr('data-sort-value',n):wikitext(('Lvl %s'):format(n + base)):done for _,v in ipairs(tabledata) do			row:tag('td'):wikitext(v[n]) 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) local icon = ' ' if not (rarity and base and lvl_o and lvl_f) then return string.wrapHtml(icon .. 'Input is invalid', 'span', { class = 'color-red' }) end local rtable, rkey = rarityTier._getTier(rarity) local levels, offset = levelingdata.pet_levels, levelingdata.pet_rarity_offset[rkey] if lvl_o < 1 or lvl_o > 100 or lvl_f < 1 or lvl_f > 100 or lvl_f < lvl_o then return string.wrapHtml(icon .. 'Input is invalid', 'span', { class = 'color-red' }) end if (lvl_f > 100 and rkey ~= 'legendary') then return string.wrapHtml(icon .. 'Pet of this rarity cannot go above level 100', 'span', { class = 'color-red' }) end local t = table.merge({ 0 }, table.slice(levels, 1 + offset, 99 + offset)) local tot = 0 for i = lvl_o + 1, lvl_f, 1 do		tot = tot + t[i] end return (icon .. 'Total XP required: %d'):format(tot) end

return p