Module:Pets: Difference between revisions

From Melvor Idle
m (TODOs)
m (Add Superior Tier only note to stronghold pet drops)
 
(78 intermediate revisions by 5 users not shown)
Line 1: Line 1:
--This module contains all sorts of functions for getting data on pets
--This module contains all sorts of functions for getting data on pets
--[[ TODOs
    Add getPetBySkill function
        acquiredBy field will work for all skills but Slayer
        May need to do skill look up by ID
            Ty has skill ID of 0, which is Woodcutting, for some reason
        Or maybe just take the first word
    Use the getPetBySkill function to build pet info table
        Similar to {{SkillCapeInfo}}
--]]


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 GameData = require('Module:GameData')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
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)
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", "'")
end
  name = string.gsub(name, "'", "'")
 
  name = string.gsub(name, "'", "'")
function p.getPets(checkFunc)
  for i, pet in pairs(PetData.Pets) do
return GameData.getEntities('pets', checkFunc)
    local PetName = string.gsub(pet.name, '#', '')
end
    if(name == PetName) then
 
      result = Shared.clone(pet)
--Returns the expansion icon for the pet if it has one
      --Make sure every pet has an id, and account for Lua being 1-index
function p.getExpansionIcon(frame)
      result.id = i - 1
local petName = frame.args ~= nil and frame.args[1] or frame
      break
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
     end
  end
 
  return result
    return tostring(html)
end
end


function p.getPetTable(frame)
function p.getDungeonBoxPetText(frame)
  local petName = frame.args ~= nil and frame.args[1] or frame
local dungeonName = frame.args ~= nil and frame.args[1] or frame
  local pet = p.getPet(petName)
local dung = nil
  local result = '{| class="wikitable"\r\n'
for i, key in ipairs(areaDataKeys) do
  result = result..'!Pet!!Name!!Effect'
dung = GameData.getEntityByName(key, dungeonName)
  result = result..'\r\n|-\r\n|'..Icons.Icon({pet.name, type='pet', size='60', notext=true})
if dung ~= nil then
  --result = result..'\r\n|-\r\n|[[File:'..pet.name..'_(pet).png|60px]]'
break
  result = result..'||[['..pet.name..']]||'..pet.description
end
  result = result..'\r\n|}'
end
  return result
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)
if dung.type == 'stronghold' then
result = result..' - Superior Tier only'
end
return result
end
end
end
end
return p

Latest revision as of 22:43, 7 August 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)
			if dung.type == 'stronghold' then
				result = result..' - Superior Tier only'
			end
			return result
		end
	end
end

return p