Module:Pets: Difference between revisions

From Melvor Idle
m (Amend odd indentation)
No edit summary
(30 intermediate revisions by 3 users not shown)
Line 2: Line 2:


local p = {}
local p = {}
local PetData = mw.loadData('Module:Pets/data')


local Shared = require( "Module:Shared" )
local Shared = require( "Module:Shared" )
local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local GameData = require('Module:GameData')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local CombatAreas = require('Module:CombatAreas')
local Num = require('Module:Number')


local areaDataKeys = { 'combatAreas', 'slayerAreas', 'dungeons', 'strongholds', 'abyssDepths' }
-- Compute combat pet sources once for use later
local function getCombatPetSources()
local result = {}
for _, key in ipairs(areaDataKeys) do
local areas = GameData.getEntities(key,
function(area)
-- Lazy exclusion of event namespaces
local areaNS, areaLocalID = Shared.getLocalID(area.id)
if areaNS == 'melvorBirthday2023' or areaNS == 'melvorAprilFools2024' then
return false
end
return area.pet ~= nil
end
)
for i, area in ipairs(areas) do
result[area.pet.petID] = {
id = area.id,
name = area.name,
type = area.type,
isCombat = true,
weight = area.pet.weight,
fixedChance = (area.fixedPetClears ~= nil and area.fixedPetClears) or area.pet.weight == 1
}
end
end
return result
end
local CombatPetSources = getCombatPetSources()


function p.getPetByID(ID)
function p.getPetByID(ID)
  local result = Shared.clone(PetData.Pets[ID + 1])
return GameData.getEntityByID('pets', ID)
  if result ~= nil then
    result.id = ID
  end
  return result
end
end


function p.getPet(name)
function p.getPet(name)
  local result = nil
return GameData.getEntityByName('pets', Shared.fixPagename(name))
  name = string.gsub(name, "%%27", "'")
  name = string.gsub(name, "'", "'")
  name = string.gsub(name, "'", "'")
  for i, pet in pairs(PetData.Pets) do
    local PetName = string.gsub(pet.name, '#', '')
    if(name == PetName) then
      result = Shared.clone(pet)
      --Make sure every pet has an id, and account for Lua being 1-index
      result.id = i - 1
      result.skill = p._getPetSkill(result)
      break
    end
  end
  return result
end
end


function p.getPets(checkFunc)
function p.getPets(checkFunc)
  local result = {}
return GameData.getEntities('pets', checkFunc)
  for i, pet in Shared.skpairs(PetData.Pets) do
end
    if checkFunc(pet) then
 
      local newPet = Shared.clone(pet)
--Returns the expansion icon for the pet if it has one
      newPet.id = i - 1
function p.getExpansionIcon(frame)
      newPet.skill = p._getPetSkill(newPet)
local petName = frame.args ~= nil and frame.args[1] or frame
      table.insert(result, newPet)
local pet = p.getPet(petName)
    end
if pet == nil then
  end
return Shared.printError('No pet named "' .. petName .. '" exists in the data module')
  return result
end
return Icons.getExpansionIcon(pet.id)
end
end


function p._getPetSource(pet)
function p._getPetSource(pet)
  local sourceOverrides = {
if CombatPetSources ~= nil and CombatPetSources[pet.id] ~= nil then
    -- Format: ['PetName'] = {'Source', UseIcon}
return CombatPetSources[pet.id]
    -- UseIcon = true if Source has an associated icon, false otherwise
else
    ['Asura'] = {'Slayer', true},
local skillID = p._getPetSkill(pet)
    ['Ripper the Reindeer'] = {'[[Events#Christmas_Event_2020|Christmas Event 2020]]', false},
if skillID ~= nil then
    ['Jerry the Giraffe'] = {'[[Golbin Raid|Golbin Raid Shop]]', false},
local skillName = Constants.getSkillName(skillID)
    ['Preston the Platypus'] = {'[[Golbin Raid|Golbin Raid Shop]]', false},
return { id = skillID, name = skillName, type = 'skill', isCombat = false }
    ['Mark'] = {'Summoning', true},
end
    ['Bone'] = {'Impending Darkness Event', true}
end
  }
end
  local petSource = ''
 
  local useIcon = true
function p._getPetSourceText(pet)
  local override = sourceOverrides[pet.name]
local sourceOverrides = {
  if override ~= nil then
-- useIcon: true if Source has an associated icon, false otherwise
    petSource = override[1] ~= nil and override[1] or pet.acquiredBy
['Ripper the Reindeer'] = { text = '[[Events#Christmas Event 2020|Christmas Event 2020]]', useIcon = false },
    useIcon = override[2]
['Festive Chio'] = { text = '[[Holiday Event 2021]]', useIcon = false },
  elseif pet.acquiredBy ~= nil then
['Festive Cool Rock'] = { text = '[[Holiday Event 2021]]', useIcon = false },
    petSource = pet.acquiredBy
['Jerry the Giraffe'] = { text = '[[Golbin Raid|Golbin Raid Shop]]', useIcon = false },
  end
['Preston the Platypus'] = { text = '[[Golbin Raid|Golbin Raid Shop]]', useIcon = false },
['Ty'] = { text = 'Mastery', useIcon = true },
['Golden Golbin'] = { text = Icons.Icon({'Golbin', type='monster'}) .. ' kills', useIcon = false},
['Saki'] = { text = 'Mastery', useIcon = true }
}
local petSourceText = nil
local iconType = nil
local useIcon = true
local override = sourceOverrides[pet.name]
if override ~= nil then
petSourceText = override.text
if override.useIcon ~= nil then
useIcon = override.useIcon
end
end
 
if petSourceText == nil then
local petSource = p._getPetSource(pet)
if petSource ~= nil then
if petSource.isCombat then
iconType = (petSource.type == 'dungeon' and 'dungeon') or 'combatArea'
else
iconType = petSource.type
end
petSourceText = petSource.name
else
useIcon = false
petSourceText = ''
end
end


  -- Determine icon type (if any)
if useIcon then
  local iconType = nil
return Icons.Icon({petSourceText, type=iconType})
  if Constants.getSkillID(petSource) ~= nil then
else
    iconType = 'skill'
return petSourceText
  else
end
    local combatArea = CombatAreas.getArea(petSource)
    if combatArea ~= nil then
      iconType = combatArea.type
    end
  end
 
  if useIcon then
    petSource = Icons.Icon({petSource, type=iconType})
  end
  return petSource
end
end


function p._getPetEffect(pet)
function p._getPetEffect(pet)
  local effectOverrides = {
local modKeys = {'modifiers', 'enemyModifiers'}
    ['Ripper the Reindeer'] = 'None',
local effects = {}
  }
for i, key in ipairs(modKeys) do
  if effectOverrides[pet.name] ~= nil then
if pet[key] ~= nil and not Shared.tableIsEmpty(pet[key]) then
    return effectOverrides[pet.name]
local preText = (key == 'enemyModifiers' and 'All enemies have: ' or '')
  else
table.insert(effects, preText .. Modifiers.getModifiersText(pet[key], false))
  local modKeys = {'modifiers', 'enemyModifiers'}
end
  local effects = {}
end
  for i, key in ipairs(modKeys) do
if Shared.tableIsEmpty(effects) then
    if pet[key] ~= nil and Shared.tableCount(pet[key]) > 0 then
return 'None'
    for effectName, effectValue in pairs(pet[key]) do
else
      local preText = (key == 'enemyModifiers' and 'All enemies have: ' or '')
return table.concat(effects, '<br/>')
      table.insert(effects, preText .. Constants._getModifierText(effectName, effectValue, false))
end
    end
end
    end
 
  end
function p._getPetChance(pet)
  if Shared.tableCount(effects) > 0 then
local source = p._getPetSource(pet)
    return table.concat(effects, '<br/>')
if source ~= nil and source.weight ~= nil then
  elseif pet.skill ~= nil and pet.skill >= 0 then
-- Pet is from a dungeon or combat/slayer area
    -- Attempt to remove skill prefix from description
if source.fixedChance then
    local newDesc, subIdx = string.gsub(pet.description, '^' .. Constants.getSkillName(pet.skill) .. '<br>', '')
return 'Guaranteed after ' .. Num.formatnum(source.weight) .. (source.weight == 1 and ' clear' or ' clears')
    return newDesc
else
  else
return '1 in ' .. Num.formatnum(source.weight) .. ' (' .. Num.round(100 / source.weight, 2, 2) .. '%)'
    return pet.description
end
  end
else
  end
-- Skill pet or other
return 'See: [[Pets#Acquiring Pets|Acquiring Pets]]'
end
end
end


function p._getPetSkill(pet)
function p._getPetSkill(pet)
  local skillOverrides = {
local skillOverrides = {
    ['Ty'] = -1,
['melvorD:Ty'] = nil,
    ['Mark'] = 21
['melvorF:Mark'] = 'melvorD:Summoning'
  }
}


  local skillID = pet.skill
if skillOverrides[pet.id] ~= nil then
  if skillOverrides[pet.name] ~= nil then
return skillOverrides[pet.id]
    skillID = skillOverrides[pet.name]
else
  end
return pet.skillID
  if skillID < 0 then
end
    return nil
  else
    return skillID
  end
end
end


function p._getPetTable(pets)
function p._getPetTable(pets)
  if pets == nil or Shared.tableCount(pets) == 0 then return nil end
if type(pets) ~= 'table' or Shared.tableIsEmpty(pets) then
 
return nil
  local resultPart = {}
end
  table.insert(resultPart, '{| class="wikitable"\r\n!Pet!!Name!!Effect')


  for i, pet in pairs(pets) do
local html = mw.html.create('table')
    table.insert(resultPart, '|-')
:addClass('wikitable')
    table.insert(resultPart, '|style="text-align: center;"|' .. Icons.Icon({pet.name, type='pet', size=60, notext=true}))
    table.insert(resultPart, '|' .. Icons.Icon({pet.name, type='pet', noicon=true}))
html:tag('tr')
    table.insert(resultPart, '| ' .. p._getPetEffect(pet))
:tag('th'):wikitext('Pet')
  end
:tag('th'):wikitext('Name')
  table.insert(resultPart, '|}')
:tag('th'):wikitext('[[DLC]]')
:tag('th'):wikitext('Effect')


  return table.concat(resultPart, '\r\n')
for i, pet in ipairs(pets) do
html:tag('tr')
:tag('td'):wikitext(Icons.Icon({pet.name, type='pet', notext=true}))
  :css('text-align', 'center')
:tag('td'):wikitext('[[' .. pet.name .. ']]')
:tag('td'):wikitext(Icons.getDLCColumnIcon(pet.id))
  :css('text-align', 'center')
:tag('td'):wikitext(p._getPetEffect(pet))
end
return tostring(html)
end
end


function p.getPetTableBySkill(frame)
function p.getPetTableBySkill(frame)
  local skillName = frame.args ~= nil and frame.args[1] or frame
local skillName = frame.args ~= nil and frame.args[1] or frame
  local skillID = Constants.getSkillID(skillName)
local skillID = Constants.getSkillID(skillName)


  if skillID == nil then
if skillID == nil then
    return ''
error("SkillID not found for skill: " .. skillName)
  else
else
    local pets = p.getPets(function(pet) return p._getPetSkill(pet) == skillID end)
local pets = p.getPets(function(pet) return p._getPetSkill(pet) == skillID end)
    if pets == nil then
if pets == nil or Shared.tableIsEmpty(pets) then
      return ''
return ''
    else
else
      return p._getPetTable(pets)
return p._getPetTable(pets)
    end
end
  end
end
end
end


function p.getPetSidebar(frame)
function p.getPetSidebar(frame)
  local args = frame.args ~= nil and frame.args or frame
local args = frame.args ~= nil and frame.args or frame
  local result = nil
local result = nil
  local name = (args.name ~= nil and args.name ~= '') and args.name or args[1]
local name = (args.name ~= nil and args.name ~= '') and args.name or args[1]
  local pet = p.getPet(name)
local pet = p.getPet(name)
  local effect = (args.effect ~= nil and args.effect ~= '') and args.effect or p._getPetEffect(pet)
if pet == nil then
  local completionReq = (pet.ignoreCompletion ~= nil and pet.ignoreCompletion) and 'No' or 'Yes'
return Shared.printError('No pet named "' .. (name or 'Unknown') .. '" exists in the data module')
end
local effect = (args.effect ~= nil and args.effect ~= '') and args.effect or p._getPetEffect(pet)
local completionReq = (pet.ignoreCompletion ~= nil and pet.ignoreCompletion) and 'No' or 'Yes'
local dropChance = p._getPetChance(pet)


  local dropChance = nil
result = '{| class="wikitable infobox"\r\n|-\r\n'
  if pet.obtained ~= nil and pet.obtained.dungeonCompletion ~= nil then
result = result..'! ' .. Icons.getExpansionIcon(pet.id) .. name .. '\r\n|-\r\n| '
    local odds = pet.obtained.dungeonCompletion[1][2]
result = result..'style="text-align: center;"|' .. Icons.Icon({name, type='pet', size='250', notext=true})
    -- Special handling for Into the Mist/Pablo
result = result.."\r\n|-\r\n|'''Pet ID:''' "..pet.id
    if pet.obtained.dungeonCompletion[1][1] == 15 then
result = result.."\r\n|-\r\n|'''Source:''' "..p._getPetSourceText(pet)
      dropChance = 'Guaranteed after ' .. odds .. ' completions'
if dropChance ~= nil then
    else
result = result.."\r\n|-\r\n|'''Drop Chance:''' "..dropChance
      dropChance = '1 in '..odds..' ('..Shared.round(100 / odds, 2, 2)..'%)'
end
    end
result = result.."\r\n|-\r\n| style =\"width: 250px;\"|'''Effect:''' "..effect
  end
result = result .. "\r\n|-\r\n|'''Part of 100% Completion:''' " .. completionReq .. "\r\n|}"
 
  result = '{| class="wikitable" style="float:right; clear:right;"\r\n|-\r\n'
  result = result..'! '..name..'\r\n|-\r\n| '
  result = result..'style="text-align: center;"|' .. Icons.Icon({name, type='pet', size='250', notext=true})
  result = result.."\r\n|-\r\n|'''Pet ID:''' "..pet.id
  result = result.."\r\n|-\r\n|'''Source:''' "..p._getPetSource(pet)
  if dropChance ~= nil then
    result = result.."\r\n|-\r\n|'''Drop Chance:''' "..dropChance
  end
  result = result.."\r\n|-\r\n| style =\"width: 250px;\"|'''Effect:''' "..effect
  result = result .. "\r\n|-\r\n|'''Part of 100% Completion:''' " .. completionReq .. "\r\n|}"


  return result
return result
end
end


function p.getPetPageTable()
function p.getPetPageTable()
  local result = ''
    local html = mw.html.create('table')
  local petList = Shared.clone(PetData.Pets)
        :addClass('wikitable sortable lighttable stickyHeader')


  result = '{|class="wikitable sortable lighttable stickyHeader"'
    html:tag('tr'):addClass('headerRow-0')
  result = result..'\r\n|- class="headerRow-0"\r\n! Name !! Image !! Acquired From !! Effect'
        :tag('th'):attr('colspan', '2')
          :wikitext('Pet')
       
        :tag('th'):wikitext('[[DLC]]')
        :tag('th'):wikitext('Acquired From')
        :tag('th'):wikitext('Effect')


  table.sort(petList, function(a, b)
    for i, thisPet in ipairs(GameData.rawData.pets) do
                        return p.getPet(a.name).id < p.getPet(b.name).id
        html:tag('tr')
                      end)
            :tag('td'):addClass('table-img')
                  :attr('data-sort-value', thisPet.name)
                      :wikitext(Icons.Icon({thisPet.name, type='pet', notext=true}))
                      :css('text-align', 'center')
            :tag('td'):wikitext('[[' .. thisPet.name .. ']]')
            :tag('td'):wikitext(Icons.getDLCColumnIcon(thisPet.id))
              :css('text-align', 'center')
              :attr('data-sort-value', Icons.getExpansionID(thisPet.id))
            :tag('td'):wikitext(p._getPetSourceText(thisPet))
            :tag('td'):wikitext(p._getPetEffect(thisPet))
    end


  for i, thisPet in pairs(petList) do
     return tostring(html)
     result = result..'\r\n|-\r\n|'..Icons.Icon({thisPet.name, type='pet', noicon=true})
    result = result..'||style="text-align: center;"|'..Icons.Icon({thisPet.name, size='60', type='pet', notext=true})
    result = result..'||'..p._getPetSource(thisPet)
    result = result..'||'..p._getPetEffect(thisPet)
  end
  result = result..'\r\n|}'
 
  return result
end
end


function p.getDungeonBoxPetText(frame)
function p.getDungeonBoxPetText(frame)
  local dungeonName = frame.args ~= nil and frame.args[1] or frame
local dungeonName = frame.args ~= nil and frame.args[1] or frame
  local dung = CombatAreas.getArea(dungeonName)
local dung = nil
  if dung == nil then
for i, key in ipairs(areaDataKeys) do
    return 'ERROR: Invalid dungeon name '..dungeonName..'[[Category:Pages with script errors]]'
dung = GameData.getEntityByName(key, dungeonName)
  end
if dung ~= nil then
 
break
  local result = ''
end
  local pet = p.getPetByID(dung.petID)
end
  if pet ~= nil then
if dung == nil then
    result = "\r\n|-\r\n|'''[[Pets#Boss Pets|Pet]]:'''<br/>"
return Shared.printError('No dungeon named "' .. dungeonName .. '" exists in the data module')
    result = result..Icons.Icon({pet.name, type='pet'})
end
 
    local dropChance = ''
    if dung.name == 'Into the Mist' then
      dropChance = 'Guaranteed after 5 clears'
    elseif dung.name == 'Impending Darkness Event' then
      dropChance = 'Guaranteed after one clear'
    else
      local odds = pet.obtained.dungeonCompletion[1][2]
      dropChance = '1 in '..odds..' ('..Shared.round(100 / odds, 2, 2)..'%)'
    end
    result = result.."\r\n|-\r\n|'''Pet Drop Chance:'''<br/>"..dropChance
  end
 
  return result
end


function p.getPetNavbox()
if dung.pet ~= nil then
  --•
local pet = p.getPetByID(dung.pet.petID)
  local result = '{| class="wikitable" style="margin:auto; text-align:center; clear:both; width: 100%"'
if pet ~= nil then
  result = result..'\r\n|-\r\n!colspan="2"|[[Pets]]'
local result = "\r\n|-\r\n|'''[[Pets#Boss Pets|Pet]]:'''<br/>"
 
result = result..Icons.Icon({pet.name, type='pet'})
  local skillPetList = {}
result = result.."\r\n|-\r\n|'''Pet Drop Chance:'''<br/>"..p._getPetChance(pet)
  local bossPetList = {}
return result
  local otherPetList = {}
end
  for i, petData in Shared.skpairs(PetData.Pets) do
end
    if p._getPetSkill(petData) ~= nil then
      table.insert(skillPetList, Icons.Icon({petData.name, type='pet'}))
    elseif petData.obtained ~= nil and petData.obtained.dungeonCompletion ~= nil then
      table.insert(bossPetList, Icons.Icon({petData.name, type='pet'}))
    else
      table.insert(otherPetList, Icons.Icon({petData.name, type='pet'}))
    end
  end
  table.sort(skillPetList, function(a, b) return a < b end)
  table.sort(bossPetList, function(a, b) return a < b end)
  table.sort(otherPetList, function(a, b) return a < b end)
  result = result..'\r\n|-\r\n!Skill Pets\r\n|'..table.concat(skillPetList, ' ')
  result = result..'\r\n|-\r\n!Boss Pets\r\n|'..table.concat(bossPetList, ' • ')
  result = result..'\r\n|-\r\n!Other Pets\r\n|'..table.concat(otherPetList, ' • ')
  result = result..'\r\n|}'
  return result
end
end


return p
return p

Revision as of 18:14, 4 July 2024

Data for this page is stored in Module:GameData/data


--This module contains all sorts of functions for getting data on pets

local p = {}

local Shared = require( "Module:Shared" )
local Constants = require('Module:Constants')
local GameData = require('Module:GameData')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Num = require('Module:Number')

local areaDataKeys = { 'combatAreas', 'slayerAreas', 'dungeons', 'strongholds', 'abyssDepths' }
-- Compute combat pet sources once for use later
local function getCombatPetSources()
	local result = {}
	for _, key in ipairs(areaDataKeys) do
		local areas = GameData.getEntities(key,
			function(area)
				-- Lazy exclusion of event namespaces
				local areaNS, areaLocalID = Shared.getLocalID(area.id)
				if areaNS == 'melvorBirthday2023' or areaNS == 'melvorAprilFools2024' then
					return false
				end
				return area.pet ~= nil
			end
		)
		for i, area in ipairs(areas) do
			result[area.pet.petID] = {
				id = area.id,
				name = area.name,
				type = area.type,
				isCombat = true,
				weight = area.pet.weight,
				fixedChance = (area.fixedPetClears ~= nil and area.fixedPetClears) or area.pet.weight == 1
			}
		end
	end
	return result
end
local CombatPetSources = getCombatPetSources()

function p.getPetByID(ID)
	return GameData.getEntityByID('pets', ID)
end

function p.getPet(name)
	return GameData.getEntityByName('pets', Shared.fixPagename(name))
end

function p.getPets(checkFunc)
	return GameData.getEntities('pets', checkFunc)
end

--Returns the expansion icon for the pet if it has one
function p.getExpansionIcon(frame)
	local petName = frame.args ~= nil and frame.args[1] or frame
	local pet = p.getPet(petName)
	if pet == nil then
		return Shared.printError('No pet named "' .. petName .. '" exists in the data module')
	end
	
	return Icons.getExpansionIcon(pet.id)
end

function p._getPetSource(pet)
	if CombatPetSources ~= nil and CombatPetSources[pet.id] ~= nil then
		return CombatPetSources[pet.id]
	else
		local skillID = p._getPetSkill(pet)
		if skillID ~= nil then
			local skillName = Constants.getSkillName(skillID)
			return { id = skillID, name = skillName, type = 'skill', isCombat = false }
		end
	end
end

function p._getPetSourceText(pet)
	local sourceOverrides = {
		-- useIcon: true if Source has an associated icon, false otherwise
		['Ripper the Reindeer'] = { text = '[[Events#Christmas Event 2020|Christmas Event 2020]]', useIcon = false },
		['Festive Chio'] = { text = '[[Holiday Event 2021]]', useIcon = false },
		['Festive Cool Rock'] = { text = '[[Holiday Event 2021]]', useIcon = false },
		['Jerry the Giraffe'] = { text = '[[Golbin Raid|Golbin Raid Shop]]', useIcon = false },
		['Preston the Platypus'] = { text = '[[Golbin Raid|Golbin Raid Shop]]', useIcon = false },
		['Ty'] = { text = 'Mastery', useIcon = true },
		['Golden Golbin'] = { text = Icons.Icon({'Golbin', type='monster'}) .. ' kills', useIcon = false},
		['Saki'] = { text = 'Mastery', useIcon = true }
	}
	local petSourceText = nil
	local iconType = nil
	local useIcon = true
	local override = sourceOverrides[pet.name]
	if override ~= nil then
		petSourceText = override.text
		if override.useIcon ~= nil then
			useIcon = override.useIcon
		end
	end

	if petSourceText == nil then
		local petSource = p._getPetSource(pet)
		if petSource ~= nil then
			if petSource.isCombat then
				iconType = (petSource.type == 'dungeon' and 'dungeon') or 'combatArea'
			else
				iconType = petSource.type
			end
			petSourceText = petSource.name
		else
			useIcon = false
			petSourceText = ''
		end
	end

	if useIcon then
		return Icons.Icon({petSourceText, type=iconType})
	else
		return petSourceText
	end
end

function p._getPetEffect(pet)
	local modKeys = {'modifiers', 'enemyModifiers'}
	local effects = {}
	for i, key in ipairs(modKeys) do
		if pet[key] ~= nil and not Shared.tableIsEmpty(pet[key]) then
			local preText = (key == 'enemyModifiers' and 'All enemies have: ' or '')
			table.insert(effects, preText .. Modifiers.getModifiersText(pet[key], false))
		end
	end
	if Shared.tableIsEmpty(effects) then
		return 'None'
	else
		return table.concat(effects, '<br/>')
	end
end

function p._getPetChance(pet)
	local source = p._getPetSource(pet)
	if source ~= nil and source.weight ~= nil then
		-- Pet is from a dungeon or combat/slayer area
		if source.fixedChance then
			return 'Guaranteed after ' .. Num.formatnum(source.weight) .. (source.weight == 1 and ' clear' or ' clears')
		else
			return '1 in ' .. Num.formatnum(source.weight) .. ' (' .. Num.round(100 / source.weight, 2, 2) .. '%)'
		end
	else
		-- Skill pet or other
		return 'See: [[Pets#Acquiring Pets|Acquiring Pets]]'
	end
end

function p._getPetSkill(pet)
	local skillOverrides = {
		['melvorD:Ty'] = nil,
		['melvorF:Mark'] = 'melvorD:Summoning'
	}

	if skillOverrides[pet.id] ~= nil then
		return skillOverrides[pet.id]
	else
		return pet.skillID
	end
end

function p._getPetTable(pets)
	if type(pets) ~= 'table' or Shared.tableIsEmpty(pets) then
		return nil
	end

	local html = mw.html.create('table')
		:addClass('wikitable')
		
	html:tag('tr')
			:tag('th'):wikitext('Pet')
			:tag('th'):wikitext('Name')
			:tag('th'):wikitext('[[DLC]]')
			:tag('th'):wikitext('Effect')

	for i, pet in ipairs(pets) do
		html:tag('tr')
				:tag('td'):wikitext(Icons.Icon({pet.name, type='pet', notext=true}))
						  :css('text-align', 'center')
				:tag('td'):wikitext('[[' .. pet.name .. ']]')
				:tag('td'):wikitext(Icons.getDLCColumnIcon(pet.id))
						  :css('text-align', 'center')
				:tag('td'):wikitext(p._getPetEffect(pet))
	end
	
	return tostring(html)
end

function p.getPetTableBySkill(frame)
	local skillName = frame.args ~= nil and frame.args[1] or frame
	local skillID = Constants.getSkillID(skillName)

	if skillID == nil then
		error("SkillID not found for skill: " .. skillName)
	else
		local pets = p.getPets(function(pet) return p._getPetSkill(pet) == skillID end)
		if pets == nil or Shared.tableIsEmpty(pets) then
			return ''
		else
			return p._getPetTable(pets)
		end
	end
end

function p.getPetSidebar(frame)
	local args = frame.args ~= nil and frame.args or frame
	local result = nil
	local name = (args.name ~= nil and args.name ~= '') and args.name or args[1]
	local pet = p.getPet(name)
	if pet == nil then
		return Shared.printError('No pet named "' .. (name or 'Unknown') .. '" exists in the data module')
	end
	local effect = (args.effect ~= nil and args.effect ~= '') and args.effect or p._getPetEffect(pet)
	local completionReq = (pet.ignoreCompletion ~= nil and pet.ignoreCompletion) and 'No' or 'Yes'
	local dropChance = p._getPetChance(pet)

	result = '{| class="wikitable infobox"\r\n|-\r\n'
	result = result..'! ' .. Icons.getExpansionIcon(pet.id) .. name .. '\r\n|-\r\n| '
	result = result..'style="text-align: center;"|' .. Icons.Icon({name, type='pet', size='250', notext=true})
	result = result.."\r\n|-\r\n|'''Pet ID:''' "..pet.id
	result = result.."\r\n|-\r\n|'''Source:''' "..p._getPetSourceText(pet)
	if dropChance ~= nil then
		result = result.."\r\n|-\r\n|'''Drop Chance:''' "..dropChance
	end
	result = result.."\r\n|-\r\n| style =\"width: 250px;\"|'''Effect:''' "..effect
	result = result .. "\r\n|-\r\n|'''Part of 100% Completion:''' " .. completionReq .. "\r\n|}"

	return result
end

function p.getPetPageTable()
    local html = mw.html.create('table')
        :addClass('wikitable sortable lighttable stickyHeader')

    html:tag('tr'):addClass('headerRow-0')
        	:tag('th'):attr('colspan', '2')
        			  :wikitext('Pet')
        	
        	:tag('th'):wikitext('[[DLC]]')
        	:tag('th'):wikitext('Acquired From')
        	:tag('th'):wikitext('Effect')

    for i, thisPet in ipairs(GameData.rawData.pets) do
        html:tag('tr')
            	:tag('td'):addClass('table-img')
                		  :attr('data-sort-value', thisPet.name)
                	      :wikitext(Icons.Icon({thisPet.name, type='pet', notext=true}))
                	      :css('text-align', 'center')
            	:tag('td'):wikitext('[[' .. thisPet.name .. ']]')
            	:tag('td'):wikitext(Icons.getDLCColumnIcon(thisPet.id))
            			  :css('text-align', 'center')
            			  :attr('data-sort-value', Icons.getExpansionID(thisPet.id))
            	:tag('td'):wikitext(p._getPetSourceText(thisPet))
            	:tag('td'):wikitext(p._getPetEffect(thisPet))
    end

    return tostring(html)
end

function p.getDungeonBoxPetText(frame)
	local dungeonName = frame.args ~= nil and frame.args[1] or frame
	local dung = nil
	for i, key in ipairs(areaDataKeys) do
		dung = GameData.getEntityByName(key, dungeonName)
		if dung ~= nil then
			break
		end
	end
	if dung == nil then
		return Shared.printError('No dungeon named "' .. dungeonName .. '" exists in the data module')
	end

	if dung.pet ~= nil then
		local pet = p.getPetByID(dung.pet.petID)
		if pet ~= nil then
			local result = "\r\n|-\r\n|'''[[Pets#Boss Pets|Pet]]:'''<br/>"
			result = result..Icons.Icon({pet.name, type='pet'})
			result = result.."\r\n|-\r\n|'''Pet Drop Chance:'''<br/>"..p._getPetChance(pet)
			return result
		end
	end
end

return p