Module:Common: Difference between revisions

From Melvor Idle
(New module, attempting to avoid module requirement loops when modules which require many modules also have functions which are desired by other modules)
 
mNo edit summary
 
(24 intermediate revisions by 4 users not shown)
Line 14: Line 14:
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Num = require('Module:Number')


-- getSkillName: Given a valid namespaced skill ID, returns that skill's name
-- getSkillName: Given a valid namespaced skill ID, returns that skill's name
Line 29: Line 30:
return skillData.skillID
return skillData.skillID
end
end
end
end
-- getSkillRecipeKey: Given a skill ID, returns the key under which all recipes
-- for that skill reside. If the returned value is nil, then the skill has
-- no recipes (that is, the skill does not produce any items)
function p.getSkillRecipeKey(skillID)
-- Convert skillID to local ID if not already
local ns, localSkillID = Shared.getLocalID(skillID)
local recipeIDs = {
["Woodcutting"] = 'trees',
["Fishing"] = 'fish',
["Firemaking"] = 'logs',
["Mining"] = 'rockData',
["Thieving"] = 'npcs',
["Agility"] = 'obstacles',
["Cooking"] = 'recipes',
["Smithing"] = 'recipes',
["Farming"] = 'recipes',
["Summoning"] = 'recipes',
["Fletching"] = 'recipes',
["Crafting"] = 'recipes',
["Runecrafting"] = 'recipes',
["Herblore"] = 'recipes',
["Astrology"] = 'recipes',
["Harvesting"] = 'veinData',
["Township"] = 'buildings'
}
return recipeIDs[localSkillID]
end
-- getEquipmentSlotPage: Given a valid equipment slot ID, returns the page name for that slot's
-- equipment. If the slot is not recognized, then nil is returned
function p.getEquipmentSlotPage(equipSlot)
if type(equipSlot) == 'string' then
local slotLinkMap = {
["Helmet"] = 'Helmets',
["Platebody"] = 'Platebodies',
["Platelegs"] = 'Platelegs',
["Boots"] = 'Boots',
["Gloves"] = 'Gloves',
["Cape"] = 'Capes',
["Amulet"] = 'Amulets',
["Ring"] = 'Rings',
["Gem"] = 'Gems (Equipment)',
["Weapon"] = 'Weapons',
["Shield"] = 'Shields',
["Quiver"] = 'Ammunition',
["Consumable"] = 'Consumables',
["Passive"] = 'Combat Passive Slot',
["Summon1"] = 'Summoning',
["Summon2"] = 'Summoning'
}
return slotLinkMap[equipSlot]
end
end
-- getEquipmentSlotLink: As with getEquipmentSlotPage(), except returns wikitext to link to the
-- relevant page.
function p.getEquipmentSlotLink(equipSlot)
local pageName = p.getEquipmentSlotPage(equipSlot)
if pageName ~= nil then
return '[[' .. pageName .. '|' .. equipSlot .. ']]'
else
return equipSlot
end
end
end
end
Line 71: Line 137:
else
else
return 'Item'
return 'Item'
end
end
-- getPurchaseIconType: Given a purchase from shop dtaa, returns the icon type to be used within
-- Icons.Icon() to retrieve the purchase's icon
function p.getPurchaseIconType(purchase)
local purchType = p.getPurchaseType(purchase)
if purchType == 'Item Bundle' then
local upgBundles = {
'melvorAoD:Summoners_Pack_I',
'melvorAoD:Summoners_Pack_II',
'melvorAoD:Summoners_Pack_III',
'melvorAoD:Combat_Supply_I',
'melvorAoD:Combat_Supply_II',
'melvorAoD:Combat_Supply_III',
'melvorItA:Abyssal_Resupply',
'melvorItA:Blighted_Resupply',
'melvorItA:Withering_Resupply'
}
if Shared.contains(upgBundles, purchase.id) then
return 'upgrade'
else
return 'item'
end
else
return string.lower(purchType)
end
end
end
end
Line 79: Line 171:
local purchase = iconArgs[1]
local purchase = iconArgs[1]
local purchaseName = p.getPurchaseName(purchase)
local purchaseName = p.getPurchaseName(purchase)
local purchType = p.getPurchaseType(purchase)
local iconType = p.getPurchaseIconType(purchase)
 
-- Amend iconArgs before passing to Icons.Icon()
-- Amend iconArgs before passing to Icons.Icon()
iconArgs[1] = purchaseName
iconArgs[1] = purchaseName
iconArgs['type'] = (purchType == 'Item Bundle' and 'item') or string.lower(purchType)
iconArgs['type'] = iconType


return Icons.Icon(iconArgs)
return Icons.Icon(iconArgs)
end
-- getCostString: Given item & currency costs for something, returns human readable wikitext
-- for those costs. If there are no costs, returns the value specified by valueIfNone instead.
-- Costs are in the format:
-- { items = { ... }, currencies = { ... } }
function p.getCostString(costs, valueIfNone, entryDecorator, entrySeparator)
local function formatLine(text)
if entryDecorator == nil then
return text
else
return entryDecorator(text)
end
end
local entrySep = entrySeparator
if type(entrySeparator) ~= 'string' then
entrySep = '<br>'
end
local costArray = {}
if type(costs.currencies) == 'table' and not Shared.tableIsEmpty(costs.currencies) then
for i, currCost in ipairs(costs.currencies) do
local currID = currCost.id or currCost.currencyID or currCost.currency
if currCost.min ~= nil then
-- Cost is a range
table.insert(costArray, formatLine(Icons._Currency(currID, currCost.min, currCost.max)))
else
table.insert(costArray, formatLine(Icons._Currency(currID, (currCost.quantity or currCost.cost))))
end
end
end
if type(costs.items) == 'table' and not Shared.tableIsEmpty(costs.items) then
for i, itemCost in ipairs(costs.items) do
local item = GameData.getEntityByID('items', itemCost.id)
if item ~= nil then
table.insert(costArray, formatLine(Icons.Icon({item.name, type='item', qty=itemCost.quantity})))
end
end
end
if Shared.tableIsEmpty(costArray) then
return valueIfNone
else
return table.concat(costArray, entrySep)
end
end
end


Line 97: Line 234:
local reqArray = {}
local reqArray = {}
for i, req in ipairs(reqs) do
for i, req in ipairs(reqs) do
if req.type == 'AllSkillLevels' then
if req.type == 'AbyssalLevel' then
local skillName = p.getSkillName(req.skillID)
if skillName ~= nil then
table.insert(reqArray, Icons._SkillReq(skillName, req.level, nil, 'melvorItA:Abyssal'))
end
elseif req.type == 'AbyssDepthCompletion' then
local depth = GameData.getEntityByID('abyssDepths', req.depthID)
if depth ~= nil then
local depthStr = 'Complete ' .. Icons.Icon({depth.name, type='combatArea'})
if req.count > 1 then
depthStr = depthStr .. ' ' .. Num.formatnum(req.count) .. ' times'
end
table.insert(reqArray, depthStr)
end
elseif req.type == 'AllSkillLevels' then
local reqText = 'Level ' .. req.level .. ' in all skills'
local reqText = 'Level ' .. req.level .. ' in all skills'
if req.exceptions ~= nil and not Shared.tableIsEmpty(req.exceptions) then
if req.exceptions ~= nil and not Shared.tableIsEmpty(req.exceptions) then
Line 111: Line 262:
table.insert(reqArray, reqText)
table.insert(reqArray, reqText)
elseif req.type == 'ArchaeologyItemsDonated' then
elseif req.type == 'ArchaeologyItemsDonated' then
table.insert(reqArray, 'Donate ' .. Shared.formatnum(req.count) .. ' Artefacts to the Museum in ' .. Icons.Icon({'Archaeology', type='skill'}))
table.insert(reqArray, 'Donate ' .. Num.formatnum(req.count) .. ' Artefacts to the [[Museum]]')
elseif req.type == 'CartographyPOIDiscovery' then
elseif req.type == 'CartographyPOIDiscovery' then
local map = GameData.getEntityByID(GameData.skillData.Cartography.worldMaps, req.worldMapID)
local map = GameData.getEntityByID(GameData.skillData.Cartography.worldMaps, req.worldMapID)
Line 136: Line 287:
local dungStr = 'Complete ' .. Icons.Icon({dung.name, type='dungeon'})
local dungStr = 'Complete ' .. Icons.Icon({dung.name, type='dungeon'})
if req.count > 1 then
if req.count > 1 then
dungStr = dungStr .. ' ' .. Shared.formatnum(req.count) .. ' times'
dungStr = dungStr .. ' ' .. Num.formatnum(req.count) .. ' times'
end
end
table.insert(reqArray, dungStr)
table.insert(reqArray, dungStr)
end
end
elseif req.type == 'ItemFound' then
elseif req.type == 'ItemFound' then
local item = GameData.getObjectByID('items', req.itemID)
local item = GameData.getEntityByID('items', req.itemID)
if item ~= nil then
if item ~= nil then
table.insert(reqArray, 'Find ' .. Icons.Icon({item.name, type='item'}))
table.insert(reqArray, 'Find ' .. Icons.Icon({item.name, type='item'}))
end
elseif req.type == 'MasteryLevel' then
local skill = GameData.getSkillData(req.skillID)
local recipeKey = p.getSkillRecipeKey(req.skillID)
if skill ~= nil then
local action = GameData.getEntityByID(skill[recipeKey], req.actionID)
if action ~= nil then
table.insert(reqArray, Icons._MasteryReq(action.name, req.level, true))
end
end
elseif req.type == 'MonsterKilled' then
local monster = GameData.getEntityByID('monsters', req.monsterID)
if monster ~= nil then
table.insert(reqArray, Icons.Icon({monster.name, type='monster', qty=req.count, notext=true}) .. ' Kills')
end
end
elseif req.type == 'ShopPurchase' then
elseif req.type == 'ShopPurchase' then
Line 161: Line 326:
end
end
elseif req.type == 'SlayerTask' then
elseif req.type == 'SlayerTask' then
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' ' .. req.tier .. ' Slayer Tasks')
local taskCategory = GameData.getEntityByID('slayerTaskCategories', req.category)
if taskCategory ~= nil then
table.insert(reqArray, 'Complete ' .. Num.formatnum(req.count) .. ' ' .. taskCategory.name .. ' or higher Slayer Tasks')
end
elseif req.type == 'TownshipBuilding' then
elseif req.type == 'TownshipBuilding' then
local tsData = GameData.getSkillData('melvorD:Township')
local tsData = GameData.getSkillData('melvorD:Township')
Line 167: Line 335:
local building = GameData.getEntityByID(tsData.buildings, req.buildingID)
local building = GameData.getEntityByID(tsData.buildings, req.buildingID)
if building ~= nil then
if building ~= nil then
table.insert(reqArray, 'Have ' .. Shared.formatnum(req.count) .. ' ' .. building.name .. ' actively built in Township')
table.insert(reqArray, 'Have ' .. Num.formatnum(req.count) .. ' ' .. building.name .. ' actively built in Township')
end
end
end
end
elseif req.type == 'TownshipTask' then
elseif req.type == 'TownshipTask' then
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' Township Tasks')
table.insert(reqArray, 'Complete ' .. Num.formatnum(req.count) .. ' Township Tasks')
else
else
table.insert(reqArray, Shared.printError('Unknown requirement: ' .. (req.type or 'nil')))
table.insert(reqArray, Shared.printError('Unknown requirement: ' .. (req.type or 'nil')))

Latest revision as of 07:18, 10 December 2024

Documentation for this module may be created at Module:Common/doc

-- This module contains common functions which interact with or interpret game data
-- in some form. Functions here may be applicable to various aspects of the game
-- (e.g. monsters, shop, dungeons, pets, items, and so on) and are contained here
-- to avoid loops when requiring modules.

-- Any functions which have no reliance upon, or relevance to game data are better suited
-- for [[Module:Shared]].

-- This module should _never_ require other modules which rely upon [[Module:GameData]]

local p = {}

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

-- getSkillName: Given a valid namespaced skill ID, returns that skill's name
function p.getSkillName(skillID)
	local skill = GameData.getSkillData(skillID)
	if skill ~= nil then
		return skill.name
	end
end

-- getSkillID: Given a valid skill name, returns that skill's namespaced ID
function p.getSkillID(skillName)
	for i, skillData in ipairs(GameData.rawData.skillData) do
		if skillData.data.name == skillName then
			return skillData.skillID
		end
	end
end

-- getSkillRecipeKey: Given a skill ID, returns the key under which all recipes
-- for that skill reside. If the returned value is nil, then the skill has
-- no recipes (that is, the skill does not produce any items)
function p.getSkillRecipeKey(skillID)
	-- Convert skillID to local ID if not already
	local ns, localSkillID = Shared.getLocalID(skillID)
	local recipeIDs = {
		["Woodcutting"] = 'trees',
		["Fishing"] = 'fish',
		["Firemaking"] = 'logs',
		["Mining"] = 'rockData',
		["Thieving"] = 'npcs',
		["Agility"] = 'obstacles',
		["Cooking"] = 'recipes',
		["Smithing"] = 'recipes',
		["Farming"] = 'recipes',
		["Summoning"] = 'recipes',
		["Fletching"] = 'recipes',
		["Crafting"] = 'recipes',
		["Runecrafting"] = 'recipes',
		["Herblore"] = 'recipes',
		["Astrology"] = 'recipes',
		["Harvesting"] = 'veinData',
		["Township"] = 'buildings'
	}
	return recipeIDs[localSkillID]
end

-- getEquipmentSlotPage: Given a valid equipment slot ID, returns the page name for that slot's
-- equipment. If the slot is not recognized, then nil is returned
function p.getEquipmentSlotPage(equipSlot)
	if type(equipSlot) == 'string' then
		local slotLinkMap = {
			["Helmet"] = 'Helmets',
			["Platebody"] = 'Platebodies',
			["Platelegs"] = 'Platelegs',
			["Boots"] = 'Boots',
			["Gloves"] = 'Gloves',
			["Cape"] = 'Capes',
			["Amulet"] = 'Amulets',
			["Ring"] = 'Rings',
			["Gem"] = 'Gems (Equipment)',
			["Weapon"] = 'Weapons',
			["Shield"] = 'Shields',
			["Quiver"] = 'Ammunition',
			["Consumable"] = 'Consumables',
			["Passive"] = 'Combat Passive Slot',
			["Summon1"] = 'Summoning',
			["Summon2"] = 'Summoning'
		}
		return slotLinkMap[equipSlot]
	end
end

-- getEquipmentSlotLink: As with getEquipmentSlotPage(), except returns wikitext to link to the
-- relevant page.
function p.getEquipmentSlotLink(equipSlot)
	local pageName = p.getEquipmentSlotPage(equipSlot)
	if pageName ~= nil then
		return '[[' .. pageName .. '|' .. equipSlot .. ']]'
	else
		return equipSlot
	end
end

-- getPurchaseName: Given a purchase from shop data, returns the name of that purchase
function p.getPurchaseName(purch)
	if purch.customName ~= nil then
		return purch.customName
	elseif purch.contains ~= nil then
		local item = nil
		if purch.contains.items ~= nil and not Shared.tableIsEmpty(purch.contains.items) then
			item = GameData.getEntityByID('items', purch.contains.items[1].id)
		elseif purch.contains.itemCharges ~= nil and not Shared.tableIsEmpty(purch.contains.itemCharges) then
			item = GameData.getEntityByID('items', purch.contains.itemCharges.id)
		end
		if item ~= nil then
			return item.name
		end
		if purch.contains.petID ~= nil then
			local pet = GameData.getEntityByID('pets', purch.contains.petID)
			if pet ~= nil then
				return pet.name
			end
		end
	end
	return ''
end

-- getPurchaseType: Given a purchase from shop data, returns the type of that purchase (based on
-- the purchase's contents)
function p.getPurchaseType(purchase)
	if purchase.contains == nil then
		return 'Unknown'
	elseif purchase.contains.petID ~= nil then
		return 'Pet'
	elseif purchase.contains.itemCharges ~= nil then
		return 'Item'
	elseif purchase.contains.modifiers ~= nil or purchase.contains.items == nil or Shared.tableCount(purchase.contains.items) == 0 then
		return 'Upgrade'
	elseif purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then
		return 'Item Bundle'
	else
		return 'Item'
	end
end

-- getPurchaseIconType: Given a purchase from shop dtaa, returns the icon type to be used within
-- Icons.Icon() to retrieve the purchase's icon
function p.getPurchaseIconType(purchase)
	local purchType = p.getPurchaseType(purchase)
	if purchType == 'Item Bundle' then
		local upgBundles = {
			'melvorAoD:Summoners_Pack_I',
			'melvorAoD:Summoners_Pack_II',
			'melvorAoD:Summoners_Pack_III',
			'melvorAoD:Combat_Supply_I',
			'melvorAoD:Combat_Supply_II',
			'melvorAoD:Combat_Supply_III',
			'melvorItA:Abyssal_Resupply',
			'melvorItA:Blighted_Resupply',
			'melvorItA:Withering_Resupply'
		}
		if Shared.contains(upgBundles, purchase.id) then
			return 'upgrade'
		else
			return 'item'
		end
	else
		return string.lower(purchType)
	end
end

-- getPurchaseIcon: Accepts the same arguments as Icons.Icon(), except the first parameter is a
-- shop purchase rather than the icon/linked page name
function p.getPurchaseIcon(iconArgs)
	local purchase = iconArgs[1]
	local purchaseName = p.getPurchaseName(purchase)
	local iconType = p.getPurchaseIconType(purchase)

	-- Amend iconArgs before passing to Icons.Icon()
	iconArgs[1] = purchaseName
	iconArgs['type'] = iconType

	return Icons.Icon(iconArgs)
end

-- getCostString: Given item & currency costs for something, returns human readable wikitext
-- for those costs. If there are no costs, returns the value specified by valueIfNone instead.
-- Costs are in the format:
-- { items = { ... }, currencies = { ... } }
function p.getCostString(costs, valueIfNone, entryDecorator, entrySeparator)
	local function formatLine(text)
		if entryDecorator == nil then
			return text
		else
			return entryDecorator(text)
		end
	end
	local entrySep = entrySeparator
	if type(entrySeparator) ~= 'string' then
		entrySep = '<br>'
	end

	local costArray = {}
	if type(costs.currencies) == 'table' and not Shared.tableIsEmpty(costs.currencies) then
		for i, currCost in ipairs(costs.currencies) do
			local currID = currCost.id or currCost.currencyID or currCost.currency
			if currCost.min ~= nil then
				-- Cost is a range
				table.insert(costArray, formatLine(Icons._Currency(currID, currCost.min, currCost.max)))
			else
				table.insert(costArray, formatLine(Icons._Currency(currID, (currCost.quantity or currCost.cost))))
			end
		end
	end
	if type(costs.items) == 'table' and not Shared.tableIsEmpty(costs.items) then
		for i, itemCost in ipairs(costs.items) do
			local item = GameData.getEntityByID('items', itemCost.id)
			if item ~= nil then
				table.insert(costArray, formatLine(Icons.Icon({item.name, type='item', qty=itemCost.quantity})))
			end
		end
	end
	if Shared.tableIsEmpty(costArray) then
		return valueIfNone
	else
		return table.concat(costArray, entrySep)
	end
end

-- getRequirementString: Given requirements from something such as a shop purchase or dungeon,
-- returns human readable wikitext for those requirements. If there are no requirements, returns
-- the value specified by valueIfNone instead
function p.getRequirementString(reqs, valueIfNone)
	if reqs == nil or Shared.tableIsEmpty(reqs) then
		return valueIfNone
	end

	local reqArray = {}
	for i, req in ipairs(reqs) do
		if req.type == 'AbyssalLevel' then
			local skillName = p.getSkillName(req.skillID)
			if skillName ~= nil then
				table.insert(reqArray, Icons._SkillReq(skillName, req.level, nil, 'melvorItA:Abyssal'))
			end
		elseif req.type == 'AbyssDepthCompletion' then
			local depth = GameData.getEntityByID('abyssDepths', req.depthID)
			if depth ~= nil then
				local depthStr = 'Complete ' .. Icons.Icon({depth.name, type='combatArea'})
				if req.count > 1 then
					depthStr = depthStr .. ' ' .. Num.formatnum(req.count) .. ' times'
				end
				table.insert(reqArray, depthStr)
			end
		elseif req.type == 'AllSkillLevels' then
			local reqText = 'Level ' .. req.level .. ' in all skills'
			if req.exceptions ~= nil and not Shared.tableIsEmpty(req.exceptions) then
				local exceptSkills = {}
				for i, skillID in ipairs(req.exceptions) do
					local skillName = p.getSkillName(skillID)
					if skillName ~= nil then
						table.insert(exceptSkills, Icons.Icon({skillName, type='skill'}))
					end
				end
				reqText = reqText .. ' except for ' .. table.concat(exceptSkills, ', ')
			end
			table.insert(reqArray, reqText)
		elseif req.type == 'ArchaeologyItemsDonated' then
			table.insert(reqArray, 'Donate ' .. Num.formatnum(req.count) .. ' Artefacts to the [[Museum]]')
		elseif req.type == 'CartographyPOIDiscovery' then
			local map = GameData.getEntityByID(GameData.skillData.Cartography.worldMaps, req.worldMapID)
			if map ~= nil then
				local poiPart = {}
				for j, poiID in ipairs(req.poiIDs) do
					local poi = GameData.getEntityByID(map.pointsOfInterest, poiID)
					if poi ~= nil then
						table.insert(poiPart, Icons.Icon({poi.name, type='poi'}))
					else
						table.insert(poiPart, Shared.printError('Could not find POI with ID ' .. poiID))
					end
				end
				table.insert(reqArray, 'Discover ' .. table.concat(poiPart, ', '))
			end
		elseif req.type == 'Completion' then
			local ns = GameData.getEntityByName('namespaces', req.namespace)
			if ns ~= nil then
				table.insert(reqArray, req.percent .. '% ' .. ns.displayName .. ' Completion')
			end
		elseif req.type == 'DungeonCompletion' then
			local dung = GameData.getEntityByID('dungeons', req.dungeonID)
			if dung ~= nil then
				local dungStr = 'Complete ' .. Icons.Icon({dung.name, type='dungeon'})
				if req.count > 1 then
					dungStr = dungStr .. ' ' .. Num.formatnum(req.count) .. ' times'
				end
				table.insert(reqArray, dungStr)
			end
		elseif req.type == 'ItemFound' then
			local item = GameData.getEntityByID('items', req.itemID)
			if item ~= nil then
				table.insert(reqArray, 'Find ' .. Icons.Icon({item.name, type='item'}))
			end
		elseif req.type == 'MasteryLevel' then
			local skill = GameData.getSkillData(req.skillID)
			local recipeKey = p.getSkillRecipeKey(req.skillID)
			if skill ~= nil then
				local action = GameData.getEntityByID(skill[recipeKey], req.actionID)
				if action ~= nil then
					table.insert(reqArray, Icons._MasteryReq(action.name, req.level, true))
				end
			end
		elseif req.type == 'MonsterKilled' then
			local monster = GameData.getEntityByID('monsters', req.monsterID)
			if monster ~= nil then
				table.insert(reqArray, Icons.Icon({monster.name, type='monster', qty=req.count, notext=true}) .. ' Kills')
			end
		elseif req.type == 'ShopPurchase' then
			local shopPurch = GameData.getEntityByID('shopPurchases', req.purchaseID)
			if shopPurch ~= nil then
				table.insert(reqArray, p.getPurchaseIcon({ shopPurch }) .. ' Purchased')
			end
		elseif req.type == 'SkillLevel' then
			local skillName = p.getSkillName(req.skillID)
			if skillName ~= nil then
				table.insert(reqArray, Icons._SkillReq(skillName, req.level))
			end
		elseif req.type == 'SlayerItem' then
			local item = GameData.getEntityByID('items', req.itemID)
			if item ~= nil then
				table.insert(reqArray, Icons.Icon({item.name, type='item'}) .. ' Equipped')
			end
		elseif req.type == 'SlayerTask' then
			local taskCategory = GameData.getEntityByID('slayerTaskCategories', req.category)
			if taskCategory ~= nil then
				table.insert(reqArray, 'Complete ' .. Num.formatnum(req.count) .. ' ' .. taskCategory.name .. ' or higher Slayer Tasks')
			end
		elseif req.type == 'TownshipBuilding' then
			local tsData = GameData.getSkillData('melvorD:Township')
			if tsData ~= nil and tsData.buildings ~= nil then
				local building = GameData.getEntityByID(tsData.buildings, req.buildingID)
				if building ~= nil then
					table.insert(reqArray, 'Have ' .. Num.formatnum(req.count) .. ' ' .. building.name .. ' actively built in Township')
				end
			end
		elseif req.type == 'TownshipTask' then
			table.insert(reqArray, 'Complete ' .. Num.formatnum(req.count) .. ' Township Tasks')
		else
			table.insert(reqArray, Shared.printError('Unknown requirement: ' .. (req.type or 'nil')))
		end
	end

	if Shared.tableIsEmpty(reqArray) then
		return valueIfNone
	else
		return table.concat(reqArray, '<br/>')
	end
end

return p