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

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 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, ('%s Pet'):format(variables[r].petname or pet)) end if table.Set(images).size > 1 then return table.concat(images, ';') .. (aprilFools and ';Jerry Pet' or '') end return pet .. ' Pet' .. (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 _, 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._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] local stats, heldItem = data.stats, data.heldItem if not stats then return string.wrapHtml(('Missing pet stats (Pet: %s)'):format(pet), 'span', { class = 'error' }) end 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'):format(pet, level), '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 stats, variables = data.stats, data.variables local startlv = tonumber(baselv) + 1 if not stats then return error(string.format('Missing pet stats (Pet: %s)', pet), 2) end local tabs = {} for _, raritykey in ipairs(p._discloseRarities(pet, true)) do		- -- 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).name, ' = ',			string.wrapHtml(('%s %s Pet'):format(tostring(rarityTier._link(raritykey)), petname), '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 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) -- 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 amount = variables[rarity].ability_count local list = {} for i = 1, tonumber(amount), 1 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)				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) 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 .. '➜' .. 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 stats, heldItem, variables = data.stats, data.heldItem, data.variables local raritykey = rarityAliases[rarity:lower] local raritynum = rarityOrder(raritykey) + 1 local petname = variables[raritykey] and variables[raritykey].petname or pet 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 amount = variables[raritykey].ability_count if not tonumber(amount) then error(('Missing ability count. Pet: %s; Rarity: %s'):format(pet, rarity)) end 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 = 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 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, petname, 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 pet = args[1] or args['p'] or args['pet'] or args['lvl'] or args['level'] or (isPetPage and fullpagename) or nil local tabledata = {} local rarities = 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