Anonymous

Module:Township: Difference between revisions

From Melvor Idle
Fix township building sticky header and add cell borders
No edit summary
(Fix township building sticky header and add cell borders)
 
(56 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Monsters = require('Module:Monsters')
local Shop = require('Module:Shop')
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local Constants = require('Module:Constants')
local Num = require('Module:Number')


-- Data Module
local p = {}
local DataDemo = require('Module:GauTest/DataDemo')
 
local DataFull = require('Module:GauTest/DataFull')
local Township = GameData.getSkillData('melvorD:Township')
local DataTotH = require('Module:GauTest/DataTotH')
p.Township = Township
 
-- Gets a Township building by ID, e.g. melvorF:Hunters_Cabin
function p._getBuildingByID(id)
-- Check for the special statue case
if id == 'melvorF:Statues' then
local building = Shared.clone(GameData.getEntityByID(Township.buildings, id))
building.name = 'Statue of Worship'
return building
else
return GameData.getEntityByID(Township.buildings, id)
end
end
 
-- Gets a Township building by name, e.g. Hunters Cabin
function p._getBuildingByName(name)
-- Check for the special statue case
if name == 'Statues' then
name = 'Statue of Worship'
end
local STATUE_OF = 'Statue of '
if string.sub(name, 1, string.len(STATUE_OF)) == STATUE_OF then
local building = Shared.clone(GameData.getEntityByID(Township.buildings, 'melvorF:Statues'))
building.name = name
return building
else
return GameData.getEntityByName(Township.buildings, name)
end
end


local Namespaces = {
-- Gets a resource from id
melvorD = DataDemo,
function p._getResourceByID(id)
melvorF = DataFull,
return GameData.getEntityByID(Township.resources, id)
melvorTotH = DataTotH
end
}


local Data = {}
-- Given a building, find the next building upgrade
function p._getBuildingUpgrade(building)
local function checkFunc(entity)
return entity.upgradesFrom ~= nil and entity.upgradesFrom == building.id
end
local upgradesTo = GameData.getEntities(Township.buildings, checkFunc)
if #upgradesTo > 0 then
return upgradesTo[1]
end
return nil
end


-- Given a building, find the building's downgrade
function p._getBuildingDowngrade(building)
if building.upgradesFrom ~= nil then
return p._getBuildingByID(building.upgradesFrom)
end
return nil
end


-- For a table that is indexed but uses a key, use this function to find the correct element
-- Given a building and biome ID, returns the cost of constructing the building
function Data.tableMatch(tabl, property, value)
-- within that biome as a human readable text string. Returns nil if the building
for _, elem in ipairs(tabl) do
-- cannot be built within that biome.
if elem[property] == value then
function p._getBuildingCostText(building, biomeID, delimiter)
return elem
-- Basic validation of inputs
if type(building) == 'table' and building.cost ~= nil and biomeID ~= nil then
local delim = delimiter
if delim == nil then
delim = ', '
end
for i, costDef in ipairs(building.cost) do
if costDef.biomeID == biomeID then
local resultPart = {}
for j, cost in ipairs(costDef.cost) do
local resData = p._getResourceByID(cost.id)
if resData ~= nil then
table.insert(resultPart, Icons.Icon({resData.name, type='resource', notext=true, nolink=true, qty=cost.quantity}))
end
end
return table.concat(resultPart, delim)
end
end
end
end
end
return nil
end
end


-- For a table that is indexed but uses a key, use this function to find the correct element, when there are several duplicates elements
-- Given a building, groups biomes for which that building has a common cost
function Data.tableMatches(tabl, property, value)
function p._getBuildingGroupedCosts(building)
local matches = {}
local biomeGroups = {}
for _, elem in ipairs(tabl) do
for i, biomeID in ipairs(building.biomes) do
if elem[property] == value then
local currentBiomeCost = p._getBuildingCostText(building, biomeID)
table.insert(matches, elem)
local found = false
for j, biomeGroup in ipairs(biomeGroups) do
if biomeGroup.cost == currentBiomeCost then
-- Another biome exists with this cost
table.insert(biomeGroup.biomeIDs, biomeID)
found = true
break
end
end
if not found then
table.insert(biomeGroups, { biomeIDs = { biomeID }, cost = currentBiomeCost})
end
end
end
end
return matches
return biomeGroups
end
end


-- Separates the namespace and id of a string
-- Given a building, returns a text string repesenting the building costs for all biomes
-- e.g. 'melvorD:Coal_Ore' will return {namespace='melvorD', id='Coal_Ore'}
function p._getBuildingGroupedCostText(building)
-- e.g. 'Coal_Ore' will return {namespace=nil, id='Coal_Ore'}
local resultPart = {}
function Data.splitID(text)
local biomeGroups = p._getBuildingGroupedCosts(building)
local split = Shared.splitString(text, ':')
if Shared.tableCount(biomeGroups) == 1 then
local target_namespace = nil
-- If only one entry then simply output the cost
local target_id = nil
table.insert(resultPart, biomeGroups[1].cost)
if #split == 2 then
return {namespace=split[1], id=split[2]}
elseif #split == 1 then
return {id=split[1]}
else
else
return nil
-- Otherwise, split by biome group
for i, biomeGroup in ipairs(biomeGroups) do
local biomeText = {}
for j, biomeID in ipairs(biomeGroup.biomeIDs) do
local biome = GameData.getEntityByID(Township.biomes, biomeID)
table.insert(biomeText, Icons.Icon({biome.name, type='biome', notext=true, nolink=true, alt=biome.name}))
end
table.insert(resultPart, table.concat(biomeText, ', ') .. ': ' .. biomeGroup.cost)
end
end
end
return table.concat(resultPart, '<br/>')
end
end


-- Returns the namespace name (eventually we should use an icon?)
-- Given a building and biome ID, returns a string displaying the building's benefits,
function Data.PLACEHOLDER_NAMESPACE_ICON(namespace)
-- or nil if no benefits
local namespaces = {
function p._getBuildingBenefitText(building, biomeID, includeModifiers, delimiter)
melvorD = 'Demo',
-- Basic validation of inputs
melvorF = 'Full',
if type(building) == 'table' and building.provides ~= nil and biomeID ~= nil then
melvorTotH = 'TotH'
local delim = delimiter
}
if delim == nil then
return namespaces[namespace]
delim = ', '
end
local includeMods = includeModifiers
if includeMods == nil then
includeMods = false
end
 
local providesData = nil
for i, provides in ipairs(building.provides) do
if provides.biomeID == biomeID then
providesData = provides
break
end
end
 
if providesData ~= nil then
local resultPart = {}
local stats = {
population = 'Population',
happiness = 'Happiness',
education = 'Education',
storage = 'Storage',
worship = 'Worship'
}
local resourceText = function(resName, resType, quantity)
local elemClass = (quantity < 0 and 'text-negative') or 'text-positive'
local resIcon = Icons.Icon({resName, type=resType, notext=true})
return resIcon .. '&nbsp;<span class="' .. elemClass .. '">' .. Shared.numStrWithSign(quantity) .. '</span>'
end
 
-- Resources
if providesData.resources ~= nil then
for i, resource in ipairs(providesData.resources) do
local resData = p._getResourceByID(resource.id)
if resData ~= nil and resource.quantity ~= 0 then
table.insert(resultPart, resourceText(resData.name, 'resource', resource.quantity))
end
end
end
 
-- Other stats
for key, stat in pairs(stats) do
local quantity = providesData[key]
if quantity ~= nil and quantity ~= 0 then
table.insert(resultPart, resourceText(stat, 'township', quantity))
end
end
 
-- Modifiers
if includeMods and building.modifiers ~= nil then
table.insert(resultPart, Constants.getModifiersText(building.modifiers))
end
 
if not Shared.tableIsEmpty(resultPart) then
return table.concat(resultPart, delim)
end
end
end
end
end


Data.Item = {}
-- Given a building, groups biomes for which that building has a common benefit/provides
function p._getBuildingGroupedBenefits(building, includeModifiers)
if includeModifiers == nil then
includeModifiers = true
end
local biomeGroups = {}
for i, biomeID in ipairs(building.biomes) do
local currentBiomeBenefit = p._getBuildingBenefitText(building, biomeID, includeModifiers)
local found = false
for j, biomeGroup in ipairs(biomeGroups) do
if biomeGroup.benefit == currentBiomeBenefit then
-- Another biome exists with this cost
table.insert(biomeGroup.biomeIDs, biomeID)
found = true
break
end
end
if not found then
table.insert(biomeGroups, { biomeIDs = { biomeID }, cost = currentBiomeBenefit})
end
end
return biomeGroups
end


-- Get all the items with a property equal to value
-- Given a building, returns a text string repesenting the building benefits for all biomes
function Data.Item.Match(property, value)
function p._getBuildingGroupedBenefitText(building, includeModifiers)
local items = {}
if includeModifiers == nil then
for namespace, data in pairs(Namespaces) do
includeModifiers = true
for k, item in pairs(data.data.items) do
end
if item[property] == value then
local resultPart = {}
local itemcopy = Shared.clone(item)
local biomeGroups = p._getBuildingGroupedBenefits(building, includeModifiers)
itemcopy._namespace = namespace
if Shared.tableCount(biomeGroups) == 1 then
table.insert(items, itemcopy)
-- If only one entry then simply output the cost
table.insert(resultPart, biomeGroups[1].cost)
else
-- Otherwise, split by biome group
for i, biomeGroup in ipairs(biomeGroups) do
local biomeText = {}
for j, biomeID in ipairs(biomeGroup.biomeIDs) do
local biome = GameData.getEntityByID(Township.biomes, biomeID)
table.insert(biomeText, Icons.Icon({biome.name, type='biome', notext=true, nolink=true, alt=biome.name}))
end
end
table.insert(resultPart, table.concat(biomeText, ', ') .. ': ' .. biomeGroup.cost)
end
end
end
end
return items
return table.concat(resultPart, '<br/>')
end
 
-- Returns a sorted list of all Township buildings
function p._sortedBuildings(keepUnsorted)
local ku = true
if keepUnsorted ~= nil then
ku = keepUnsorted
end
return GameData.sortByOrderTable(Township.buildings, Township.buildingDisplayOrder, ku)
end
 
-- Gets the Township level and population requirements for a tier
-- Returns {population=X, level=X}
function p._getTierRequirements(tier)
return Township.populationForTier[tier]
end
end


-- Get item by id
-- Returns a string containing the Township level and population requirements for a tier
function Data.Item.ByID(id)
function p._getTierText(tier)
local target = Data.splitID(id)
local tierData = p._getTierRequirements(tier)
for namespace, data in pairs(Namespaces) do
if tierData ~= nil then
if target.namespace == nil or namespace == target.namespace then
local tierText = Icons._SkillReq('Township', tierData.level, false)
for k, item in pairs(data.data.items) do
if tierData.population > 0 then
if item.id == target.id then
tierText = tierText .. '<br/>' .. Icons.Icon({'Population', type='township', notext=true}) .. '&nbsp;' .. Shared.formatnum(tierData.population)
local itemcopy = Shared.clone(item)
end
itemcopy._namespace = namespace
return tierText
return itemcopy
end
end
end
end
 
-- Generates a table of all seasons, their type/requirements, and modifiers
function p.getSeasonTable(frame)
-- Manual data specifying the worship requirement for those rare seasons
local seasonReqs = {
["Nightfall"] = Icons.Icon({'Township%23Worship', 'Bane Worship', img='Statue of Bane', type='building'}),
["SolarEclipse"] = Icons.Icon({'Township%23Worship', 'The Herald Worship', img='Statue of The Herald', type='building'}),
["Lemon"] = Icons.Icon({'Ancient_Relics', 'Ancient Relics', img='Ancient Relics'})
}
 
local seasons = Shared.shallowClone(Township.seasons)
table.sort(seasons, function(a, b) return a.order < b.order end)
 
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2" | Season\n!Type\n!Modifiers')
 
for i, season in ipairs(seasons) do
local ns, localSeasonID = Shared.getLocalID(season.id)
local reqs = seasonReqs[localSeasonID]
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img"| ' .. Icons.Icon({season.name, type='township', size=50, nolink=true, notext=true}))
table.insert(resultPart, '\n| ' .. Icons.Icon({season.name, type='township', nolink=true, noicon=true}))
table.insert(resultPart, '\n| ' .. (season.order <= 3 and 'Regular' or 'Rare'))
if reqs ~= nil then
table.insert(resultPart, '<br/>Requires ' .. reqs)
end
end
table.insert(resultPart, '\n| ' .. Constants.getModifiersText(season.modifiers))
end
table.insert(resultPart, '\n|}')
return table.concat(resultPart)
end
-- Generates a table listing all biomes and their associated requirements
function p.getBiomeTable(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!rowspan="2" colspan="2"| Biome\n!colspan="2"| Requirements')
table.insert(resultPart, '\n|- class="headerRow-1"')
table.insert(resultPart, '\n! ' .. Icons.Icon({'Township', 'Level', type='skill', nolink=true}))
table.insert(resultPart, '\n! ' .. Icons.Icon({'Township', 'Population', img='Population', type='township', section='Population' }))
for i, biome in ipairs(Township.biomes) do
local reqs = p._getTierRequirements(biome.tier)
table.insert(resultPart, '\n|-\n|class="table-img"| ' .. Icons.Icon({biome.name, type='biome', size=50, nolink=true, notext=true}))
table.insert(resultPart, '\n| ' .. biome.name)
table.insert(resultPart, '\n|style="text-align:right"| ' .. reqs.level)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. reqs.population .. '"| ' .. Shared.formatnum(reqs.population))
end
end
return nil
table.insert(resultPart, '\n|}')
 
return table.concat(resultPart)
end
end


-- Returns the recipe for the item of a desired skill.
-- Generates a table showing which buildings can be built in which biomes
function Data.Item.FindRecipes(itemid, skill)
-- Skips upgraded buildings
function p.getBuildingBiomeTable(frame)
local tbl = mw.html.create('table')
:addClass('wikitable sortable stickyHeader')
:css('text-align', 'center')
 
local header = mw.html.create('tr'):addClass('headerRow-0')
local level = mw.html.create('tr'):addClass('sorttop')
local pop = mw.html.create('tr'):addClass('sorttop')
 
header:tag('th')
:css('z-index', '2')
:wikitext('Building')
level:tag('th')
:wikitext(Icons.Icon({'Township', 'Level', type='skill', nolink=true}))
pop:tag('th')
:wikitext(Icons.Icon({'Township', 'Population', img='Population', type='township', section='Population' }))
-- No skill? No recipes
for _, biome in ipairs(Township.biomes) do
if skill == nil then
local reqs = p._getTierRequirements(biome.tier)
return {}
header:tag('th')
:wikitext(Icons.Icon({biome.name, type='biome', notext=true, nolink=true}).. '<br/>' .. biome.name)
level:tag('td')
:wikitext(Num.formatnum(reqs.level))
pop:tag('td')
:wikitext(Num.formatnum(reqs.population))
end
end
-- the key name for each skill in the json file
tbl:node(header)
local skill_recipe_keys = {
tbl:node(level)
['melvorD:Woodcutting'] = {recipes='trees', productID='productId'}, -- lowercase "d"
tbl:node(pop)
['melvorD:Fishing'] = {recipes='fish', productID='productId'}, -- lowercase "d"
 
['melvorD:Cooking'] = {recipes='recipes', productID='productID'},
for _, _building in ipairs(p._sortedBuildings(false)) do
['melvorD:Mining'] = {recipes='rockData', productID='productId'}, -- lowercase "d"
-- Fix melvorF:Statues
['melvorD:Smithing'] = {recipes='recipes', productID='productID'},
local building = p._getBuildingByID(_building.id)
['melvorD:Farming'] = {recipes='recipes', productID='productId'}, -- lowercase "d"
-- Skip upgraded buildings
['melvorD:Summoning'] = {recipes='recipes', productID='productID'},
if p._getBuildingDowngrade(building) == nil then
['melvorD:Fletching'] = {recipes='recipes', productID='productID'},
-- Populate the biome habitability data
['melvorD:Crafting'] = {recipes='recipes', productID='productID'},
local buildingBiomes = {}
['melvorD:Runecrafting'] = {recipes='recipes', productID='productID'},
-- Set all valid biomes to true
['melvorD:Herblore'] = {recipes='recipes', productID='potionIDs'} -- Special case potions I-IV
for _, biomeid in ipairs(building.biomes) do
--[[ Excluded skills:
buildingBiomes[biomeid] = true
Attack, Strength, Defence, Magic, Ranged, Prayer, Slayer
end
Thieving, Agility, Astrology, Firemaking, Township (not items)]]
 
}
local trow = tbl:tag('tr')
trow:tag('th')
:css('text-align', 'left')
:attr('data-sort-value', building.name)
:wikitext(Icons.Icon({building.name, type='building'}))


local results = {}
for _, biome in ipairs(Township.biomes) do
if buildingBiomes[biome.id] then
local SkillData = GameData.getSkillData(skill)
trow:tag('td')
local recipes = skill_recipe_keys[skill].recipes
:addClass('table-positive')
local productID = skill_recipe_keys[skill].productID
:wikitext('')
else
if SkillData[recipes] ~= nil then
trow:tag('td')
for _, recipe in ipairs(SkillData[recipes]) do
-- Special case for Herblore
if skill == 'Herblore' then
-- Iterate over the 4 potion tiers
for _, potion in ipairs(recipe[productID]) do
if itemid == potion.id then
table.insert(results, Shared.clone(recipe))
end
end
end
-- Base case
end
end
end
 
return tostring(tbl)
end
 
-- Generates a table contaning each building plus their relevant information
function p.getBuildingTable(frame)
local resultPart = {}
 
-- Change structure of biomes data for ease of use later
local biomesByID = {}
for i, biome in ipairs(Township.biomes) do
biomesByID[biome.id] = biome
end
 
-- Generate table header
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2"|Building\n!Requirements\n!Max Built')
table.insert(resultPart, '\n!Biomes\n!Cost\n!Provides')
 
local buildings = p._sortedBuildings(false)
 
for i, building in ipairs(buildings) do
-- Number of rows per building is dictated by number of biomes
local buildingName = (building.id == 'melvorF:Statues' and 'Statue of Worship') or building.name
local firstRow = true
local rowCount = Shared.tableCount(building.biomes)
local rowSpan = (rowCount > 1 and ' rowspan="' .. rowCount .. '"') or ''
local rowSpanOnly = (rowCount > 1 and '|' .. rowSpan) or ''
for j, biomeID in ipairs(building.biomes) do
local biome = biomesByID[biomeID]
if firstRow then
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img"' .. rowSpan .. '| ' .. Icons.Icon({buildingName, type='building', notext=true, size=50}))
table.insert(resultPart, '\n' .. rowSpanOnly .. '| ' .. Icons.getExpansionIcon(building.id) .. Icons.Icon({buildingName, type='building', noicon=true}))
table.insert(resultPart, '\n|' .. 'data-sort-value="' .. building.tier .. '"' .. rowSpan .. '| ' .. (p._getTierText(building.tier) or ''))
table.insert(resultPart, '\n|style="text-align:right"' .. rowSpan .. '| ' .. building.maxUpgrades)
firstRow = false
else
else
if itemid == recipe[productID] then
table.insert(resultPart, '\n|-')
table.insert(results, Shared.clone(recipe))
end
-- This section generates by biome rows
table.insert(resultPart, '\n| ' .. Icons.Icon({biome.name, type='biome', nolink=true}))
table.insert(resultPart, '\n| ' .. p._getBuildingCostText(building, biomeID))
local providesText = p._getBuildingBenefitText(building, biomeID)
if building.modifiers ~= nil then
local modText = Constants.getModifiersText(building.modifiers)
if providesText == nil then
providesText = modText
else
providesText = providesText .. '<br/>' .. modText
end
end
end
end
table.insert(resultPart, '\n| ' .. (providesText or ''))
end
end
end
end
table.insert(resultPart, '\n|}')
return results
 
return table.concat(resultPart)
end
 
-- Builds the table of trader items
function p.getTraderTable(frame)
local resultPart = {}
 
-- Build table header
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2"| Item\n!Description\n!style="min-width:60px"| Cost\n!Requirements')
 
for i, tsResource in ipairs(Township.itemConversions.fromTownship) do
local res = GameData.getEntityByID(Township.resources, tsResource.resourceID)
for j, tradeDef in ipairs(tsResource.items) do
local item = Items.getItemByID(tradeDef.itemID)
local itemDesc = item.customDescription
if itemDesc == nil then
if item.modifiers ~= nil then
itemDesc = Constants.getModifiersText(item.modifiers, false, true)
else
itemDesc = ''
end
end
local resQty = math.max(item.sellsFor, 2)
local costSort = i * 10000 + resQty
 
table.insert(resultPart, '\n|-\n| ' .. Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\n| ' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\n| ' .. itemDesc)
table.insert(resultPart, '\n|data-sort-value="' .. costSort ..'" style="text-align:right"| ' .. Icons.Icon({res.name, type='resource', qty=resQty, notext=true}))
table.insert(resultPart, '\n| ' .. Shop.getRequirementString(tradeDef.unlockRequirements))
end
end
table.insert(resultPart, '\n|}')
 
return table.concat(resultPart)
end
end


Data.Township = {}
-- Generates a table showing all the worship options
-- Returns a list of all the Township resources
function p.getWorshipTable()
function Data.Township.Resources()
local function getCheckpointCell(checkpoint)
return '\n|-\n!' .. checkpoint .. '%<br/>' .. Shared.formatnum(checkpoint * Township.maxWorship / 100) .. '/' .. Shared.formatnum(Township.maxWorship)
-- Get a sorted list of all the resources
end
local Township = GameData.getSkillData('melvorD:Township')
 
local resources = GameData.sortByOrderTable(Township.resources, Township.resourceDisplayOrder[1].ids)
local worships = GameData.getEntities(Township.worships, function(w) return not w.isHidden end)
resources = Shared.clone(resources)
local ret = {}
 
-- Append the icon reference to each resource, as well as the associated skill for the recipes
table.insert(ret, '{| class="wikitable stickyHeader"')
-- From https://melvoridle.com/assets/data/melvorFull.json -> data.skillData -> Township.data.resources.media
table.insert(ret, '\n!' .. Icons.Icon({'Worship', type='township', nolink=true}))
local resource_data = {
-- Names
['melvorF:GP'] = {_icon = {'Coins'}, _skill = nil},
for _, worship in ipairs(worships) do
['melvorF:Food'] = {_icon = {'Raw Beef', type='item'}, _skill = 'melvorD:Cooking'},
table.insert(ret, '\n!' .. Icons.Icon({worship.name, type='monster', size=50}) .. Icons.Icon({'Statue of ' .. worship.name, type='building', size=50, notext=true}))
['melvorF:Wood'] = {_icon = {'Wood', type='resource'}, _skill = 'melvorD:Woodcutting'},
end
['melvorF:Stone'] = {_icon = {'Stone', type='resource'}, _skill = 'melvorD:Mining'},
 
['melvorF:Ore'] = {_icon = {'Iron Ore', type='rock'}, _skill = 'melvorD:Mining'},
-- Requirements
['melvorF:Coal'] = {_icon = {'Coal', type='resource'}, _skill = 'melvorD:Mining'},
table.insert(ret, '\n|-\n!Requirements')
['melvorF:Bar'] = {_icon = {'Iron Bar', type='item'}, _skill = 'melvorD:Mining'},
for _, worship in ipairs(worships) do
['melvorF:Herbs'] = {_icon = {'Garum Herb', type='item'}, _skill = 'melvorD:Farming'},
local cellStyle = (Shared.tableIsEmpty(worship.unlockRequirements) and 'class="table-na"') or 'style="text-align:center"'
['melvorF:Rune_Essence'] = {_icon = {'Rune Essence', type='item'}, _skill = 'melvorD:Mining'},
table.insert(ret, '\n|' .. cellStyle ..'| ' .. Shop.getRequirementString(worship.unlockRequirements))
['melvorF:Leather'] = {_icon = {'Leather', type='item'}, _skill = nil},
end
['melvorF:Potions'] = {_icon = {'Potion', type='resource'}, _skill = 'melvorD:Herblore'},
 
['melvorF:Planks'] = {_icon = {'Planks', type='resource'}, _skill = 'melvorD:Woodcutting'},
-- Season multipliers
['melvorF:Clothing'] = {_icon = {'Leather Body', type='item'}, _skill = 'melvorD:Crafting'}
table.insert(ret, '\n|-\n!Bonus Seasons')
}
for _, worship in ipairs(worships) do
for _, resource in ipairs(resources) do
local bonusPart = {}
resource._skill = resource_data[resource.id]._skill
local cellStyle = 'style="text-align:center"'
resource._icon = resource_data[resource.id]._icon
if Shared.tableIsEmpty(worship.seasonMultiplier) then
resource._icon.notext = true
bonusPart, cellStyle = {'None'}, 'class="table-na"'
resource._icon.nolink = true
end
for i, seasonMult in ipairs(worship.seasonMultiplier) do
local season = GameData.getEntityByID(Township.seasons, seasonMult.seasonID)
if season ~= nil then
table.insert(bonusPart, Icons.Icon({season.name, type='township', nolink=true}) .. ' (' .. seasonMult.multiplier .. 'x)')
end
end
table.insert(ret, '\n|' .. cellStyle .. '| ' .. table.concat(bonusPart, '<br/>'))
end
 
-- Base modifiers
table.insert(ret, getCheckpointCell(0))
for _, worship in ipairs(worships) do
table.insert(ret, '\n| ' .. Constants.getModifiersText(worship.modifiers))
end
 
-- Checkpoint modifiers
for i, checkpoint in ipairs(Township.worshipCheckpoints) do
table.insert(ret, getCheckpointCell(checkpoint))
for _, worship in ipairs(worships) do
table.insert(ret, '\n| ' .. Constants.getModifiersText(worship.checkpoints[i]))
end
end
 
-- Total sum
table.insert(ret, '\n|-\n!Total')
for _, worship in ipairs(worships) do
local modifiers = Shared.clone(worship.modifiers)
for _, checkpoint in ipairs(worship.checkpoints) do
for modifier, magnitude in pairs(checkpoint) do
local swappedModifier = string.sub(modifier, 1, string.len('increased')) == 'increased' and string.gsub(modifier, 'increased', 'decreased') or string.gsub(modifier, 'decreased', 'increased')
-- The modifier already exists, so we add the two modifiers together
if modifiers[modifier] ~= nil then
modifiers[modifier] = modifiers[modifier] + magnitude
-- The inverse modifier already exists, so we subtract the negative value of the new modifier
elseif modifiers[swappedModifier] ~= nil then
modifiers[swappedModifier] = modifiers[swappedModifier] - magnitude
-- The modifier does not exist, so create the modifier
else
modifiers[modifier] = magnitude
end
end
end
table.insert(ret, '\n|' .. Constants.getModifiersText(modifiers))
end
end
table.insert(ret, '\n|}')
return resources
 
return table.concat(ret)
end
end


-- Returns a list of all the Township resources along with the Trader's trade ratios
-- Gets a building and prepares all the relevant stats for the building, presented as an infobox
function Data.Township.Trader()
function p.getBuildingInfoBox(frame)
-- Get the list of resources
local name = frame.args ~= nil and frame.args[1] or frame
local resources = Data.Township.Resources()
local building = p._getBuildingByName(name)
if building == nil then
return Shared.printError('No building named "' .. name .. '" exists in the data module')
end
 
local ret = {}
-- Header
table.insert(ret, '{| class="wikitable infobox"')
-- Name
table.insert(ret, '\n|-\n! ' .. Icons.getExpansionIcon(building.id) .. building.name)
-- Icon
table.insert(ret, '\n|-\n|style="text-align:center"| ' .. Icons.Icon({building.name, type='building', size='250', notext=true}))
-- ID
table.insert(ret, '\n|-\n| <b>Building ID:</b> ' .. building.id)
-- Tier
local tier = p._getTierText(building.tier)
table.insert(ret, '\n|-\n| <b>Requirements:</b><br/>' .. tier)
 
-- Upgrades From
table.insert(ret, '\n|-\n| <b>Base Cost:</b>')
local upgradesFrom = p._getBuildingDowngrade(building)
if upgradesFrom ~= nil then
table.insert(ret, '<br/>' .. Icons.Icon({upgradesFrom.name, type='building'}))
end
-- Cost
--table.insert(ret, '<br/>' .. p._getBuildingGroupedCostText(building))
local function getGroupedText(building, groupFunc)
local biomeGroups = groupFunc(building)
if Shared.tableCount(biomeGroups) == 1 then
-- If only one entry then simply output the cost
return biomeGroups[1].cost
else
-- Otherwise, split by biome group
local resultPart = {}
table.insert(resultPart, '{| class="wikitable" style="text-align:center; margin: 0.25em 0 0 0"')
for i, biomeGroup in ipairs(biomeGroups) do
local biomeText = {}
for j, biomeID in ipairs(biomeGroup.biomeIDs) do
local biome = GameData.getEntityByID(Township.biomes, biomeID)
table.insert(biomeText, Icons.Icon({biome.name, type='biome', notext=true, nolink=true, alt=biome.name}))
end
table.insert(resultPart, '\n|-\n| ' .. table.concat(biomeText, '<br/>'))
table.insert(resultPart, '\n| ' .. biomeGroup.cost)
end
table.insert(resultPart, '\n|}')
return table.concat(resultPart)
end
end
 
table.insert(ret, '\n' .. getGroupedText(building, p._getBuildingGroupedCosts))
 
-- Upgrades To
local upgradesTo = p._getBuildingUpgrade(building)
if upgradesTo ~= nil then
table.insert(ret, '\n|-\n| <b>Upgrades To:</b>')
table.insert(ret, '<br/>' .. Icons.Icon({upgradesTo.name, type='building'}))
table.insert(ret, '\n' .. getGroupedText(upgradesTo, p._getBuildingGroupedCosts))
end
 
-- Maximum built
local biomeCount = Shared.tableCount(building.biomes)
local maxText = Shared.formatnum(building.maxUpgrades)
if biomeCount > 1 then
maxText = maxText .. ' per biome, ' .. Shared.formatnum(biomeCount * building.maxUpgrades) .. ' total'
end
table.insert(ret, '\n|-\n| <b>Maximum Built:</b><br/>' .. maxText)
-- Get the list of tradeable items
-- Benefits
-- See township.js -> TownshipResource.buildResourceItemConversions for the calculation of valid items
local benefits = p._getBuildingGroupedBenefitText(building)
local function matchNone(item)
if benefits ~= nil and benefits ~= '' then
return false
table.insert(ret, '\n|-\n| <b>Provides:</b><br/>' .. benefits)
end
end
local function matchFood(item)
 
return item.type == 'Food' and (not string.match(item.id, '_Perfect')) and item.category ~= 'Farming' and (not item.ignoreCompletion)
-- Biomes
table.insert(ret, '\n|-\n| <b>Biomes:</b>')
for _, biomeid in ipairs(building.biomes) do
local biome = GameData.getEntityByID(Township.biomes, biomeid)
table.insert(ret, '<br/>' .. Icons.Icon({biome.name, type='biome', nolink=true}))
end
end
local function matchLogs(item)
 
return item.type == 'Logs'
-- End
table.insert(ret, '\n|}')
return table.concat(ret)
end
 
-- Returns an upgrade table of a building
function p.getBuildingUpgradeTable(frame)
local buildingname = frame.args ~= nil and frame.args[1] or frame
local building = p._getBuildingByName(buildingname)
if building == nil then
return Shared.printError('No building named "' .. buildingname .. '" exists in the data module')
end
end
local function matchOre(item)
 
return item.type == 'Ore' and item.id ~= 'melvorTotH:Meteorite_Ore'
-- Let's find the base building
local baseBuilding = building
while true do
local previousBuilding = p._getBuildingDowngrade(baseBuilding)
if previousBuilding ~= nil then
baseBuilding = previousBuilding
else
break
end
end
end
local function matchCoal(item)
 
return item.id == 'melvorD:Coal_Ore'
-- Let's make a list of all the buildings
-- Return empty string if there is only 1 building in the upgrade chain (i.e. no upgrades/downgrades)
local buildingList = {}
local _curBuilding = baseBuilding
while true do
table.insert(buildingList, _curBuilding)
_curBuilding = p._getBuildingUpgrade(_curBuilding)
if _curBuilding == nil then
break
end
end
end
local function matchBar(item)
if #buildingList == 1 then
return item.type == 'Bar' and item.id ~= 'melvorTotH:Meteorite_Bar'
return ''
end
end
local function matchHerb(item)
 
return item.type == 'Herb'
local ret = {}
table.insert(ret, '\n== Upgrade Chart ==')
table.insert(ret, '\n{| class="wikitable" style="text-align:center"')
 
-- Name
table.insert(ret, '\n|-\n!colspan="2"| Name')
for _, building in ipairs(buildingList) do
table.insert(ret, '\n!' .. Icons.getExpansionIcon(building.id) .. Icons.Icon({building.name, type='building'}))
end
end
local function matchEssence(item)
 
return item.id == 'melvorD:Rune_Essence' or item.id == 'melvorTotH:Pure_Essence'
-- Tier
table.insert(ret, '\n|-\n!colspan="2"| Requirements')
for _, building in ipairs(buildingList) do
table.insert(ret, '\n|' .. p._getTierText(building.tier))
end
end
local function matchLeather(item)
 
return item.id == 'melvorD:Leather'
-- Cost
local biomeCount = Shared.tableCount(baseBuilding.biomes)
table.insert(ret, '\n|-\n!rowspan="' .. biomeCount .. '"| Cost')
local firstBiome = true
for _, biomeID in ipairs(baseBuilding.biomes) do
local biome = GameData.getEntityByID(Township.biomes, biomeID)
table.insert(ret, (firstBiome and '' or '\n|-') .. '\n! ' .. Icons.Icon({biome.name, type='biome', nolink=true}))
for _, building in ipairs(buildingList) do
local cost = p._getBuildingCostText(building, biomeID)
table.insert(ret, '\n| ' .. cost)
end
firstBiome = false
end
end
local function matchPotion(item)
 
return item.type == 'Potion' and string.match(item.id, '_IV')
-- Benefits
end
local benefitText = {}
local function matchClothing(item)
table.insert(benefitText, '\n|-\n!rowspan="' .. biomeCount .. '"| Benefits')
local valid_tiers = {'Leather', 'Hard Leather', 'Dragonhide', 'Elderwood', 'Revenant', 'Carrion'}
firstBiome = true
for _, tier in ipairs(valid_tiers) do
local hasText = false
if item.tier == tier then
for _, biomeID in ipairs(baseBuilding.biomes) do
return true
local biome = GameData.getEntityByID(Township.biomes, biomeID)
table.insert(benefitText, (firstBiome and '' or '\n|-') .. '\n! ' .. Icons.Icon({biome.name, type='biome', nolink=true}))
for _, building in ipairs(buildingList) do
local benefit = p._getBuildingBenefitText(building, biomeID, true) or ''
if not hasText and benefit ~= '' then
hasText = true
end
end
table.insert(benefitText, '\n| ' .. benefit)
end
end
return false
firstBiome = false
end
if hasText then
-- Only add benefits rows if the building has benefits to display
table.insert(ret, table.concat(benefitText))
end
 
-- End
table.insert(ret, '\n|}')
 
return table.concat(ret)
end
 
-- Returns a row containing a task given a title and a task table
function p._getTaskRow(title, task, isDailyTask)
local ret = {}
 
-- If has description, we will need to rowspan the title by 2, and insert a description with colspan 2
local hasDescription = false
if task.description ~= nil then
hasDescription = true
end
end
local titlespan = hasDescription == true and 'rowspan="2"|' or ''


local resource_data = {
-- Title
['melvorF:GP'] = {traderMatches = matchNone},
table.insert(ret, '\n|-')
['melvorF:Food'] = {traderMatches = matchFood},
table.insert(ret, '\n!' .. titlespan .. title)
['melvorF:Wood'] = {traderMatches = matchLogs},
-- Description
['melvorF:Stone'] = {traderMatches = matchOre},
if hasDescription then
['melvorF:Ore'] = {traderMatches = matchOre},
table.insert(ret, '\n|colspan="2"|' .. task.description)
['melvorF:Coal'] = {traderMatches = matchCoal},
table.insert(ret, '\n|-')
['melvorF:Bar'] = {traderMatches = matchBar},
end
['melvorF:Herbs'] = {traderMatches = matchHerb},
-- Requirements
['melvorF:Rune_Essence'] = {traderMatches = matchEssence},
table.insert(ret, '\n|')
['melvorF:Leather'] = {traderMatches = matchLeather},
-- Determines order of requirements output
['melvorF:Potions'] = {traderMatches = matchPotion},
local reqOrder = {
['melvorF:Planks'] = {traderMatches = matchLogs},
["items"] = 10,
['melvorF:Clothing'] = {traderMatches = matchClothing}
["monsters"] = 20,
["monsterWithItems"] = 30,
["skillXP"] = 40,
["buildings"] = 50,
["numPOIs"] = 60,
["numRefinements"] = 70
}
}
local reqTextPart = {}


for _, resource in ipairs(resources) do
local function getItemText(itemID)
resource.itemConversions = Shared.clone(GameData.getEntities('items', resource_data[resource.id].traderMatches))
local item = Items.getItemByID(itemID)
if item == nil then
return Shared.printError('Unknown item: ' .. (itemID or 'nil'))
else
return Icons.Icon({item.name, type='item'})
end
end
local function getMonsterText(monsterID)
local monster = Monsters.getMonsterByID(monsterID)
if monster == nil then
return Shared.printError('Unknown monster: ' .. (monsterID or 'nil'))
else
return Icons.Icon({Monsters.getMonsterName(monster), type='monster'})
end
end
end
 
-- Calculate the trader's conversion ratios
for goalType, goalData in pairs(task.goals) do
-- See township.js TownshipResource.getBaseConvertToTownshipRatio and TownshipResource.getBaseConvertFromTownshipRatio for the conversion prices
local typeOrder = reqOrder[goalType] or 0
for _, resource in ipairs(resources) do
local goalText = nil
if resource.id == 'melvorF:Food' then
if type(goalData) == 'table' then
for _, item in ipairs(resource.itemConversions) do
-- Goal data is a table
item.toTownship = math.max(math.floor(1000/(item.healsFor*10)), 2)
for goalIdx, goalObj in ipairs(goalData) do
item.fromTownship = item.healsFor*5*6
if goalType == 'items' then
goalText = Shared.formatnum(goalObj.quantity) .. ' ' .. getItemText(goalObj.id)
elseif goalType == 'monsters' then
goalText = Shared.formatnum(goalObj.quantity) .. ' ' .. getMonsterText(goalObj.id)
elseif goalType == 'monsterWithItems' then
local itemsText = {}
for i, itemID in ipairs(goalObj.itemIDs) do
table.insert(itemsText, getItemText(itemID))
end
goalText = Shared.formatnum(goalObj.quantity) .. ' ' .. getMonsterText(goalObj.monsterID) .. ' with ' .. table.concat(itemsText, ', ') .. ' equipped'
elseif goalType == 'skillXP' then
local skillName = GameData.getSkillData(goalObj.id).name
goalText = Shared.formatnum(goalObj.quantity) .. ' ' .. Icons.Icon({skillName, type='skill'}) .. ' XP'
elseif goalType == 'buildings' then
local buildingName = p._GetBuildingByID(goalObj.id).name
goalText = Shared.formatnum(goalObj.quantity) .. ' ' .. Icons.Icon({buildingName, type='building'})
elseif goalType == 'numPOIs' then
local mapName = GameData.getEntityByID(GameData.skillData.Cartography.worldMaps, goalObj.worldMapID).name
goalText = 'Discover ' .. Shared.formatnum(goalObj.quantity) .. ' Points of Interest in ' .. Icons.Icon({'Cartography', type='skill'}) .. ' world map of ' .. mapName
else
goalText = Shared.printError('Unknown goal type: ' .. (goalType or 'nil'))
end
table.insert(reqTextPart, {
["goalOrder"] = typeOrder,
["subOrder"] = goalIdx,
["text"] = goalText
})
end
end
elseif resource.id == 'melvorF:Planks' then
else
for _, item in ipairs(resource.itemConversions) do
-- Goal data is another value of some type
item.toTownship = math.max(math.floor(3000/math.max(item.sellsFor, 1)), 2)
if goalType == 'numRefinements' then
item.fromTownship = math.max(math.ceil(item.sellsFor/2)*6, 1);
goalText = 'Refine dig site maps in ' .. Icons.Icon({'Cartography', type='skill'}) .. ' ' .. Shared.formatnum(goalData) .. ' times'
else
goalText = Shared.printError('Unknown goal type: ' .. (goalType or 'nil'))
end
end
elseif resource.id == 'melvorF:Rune_Essence' then
table.insert(reqTextPart, {
for _, item in ipairs(resource.itemConversions) do
["goalOrder"] = typeOrder,
item.toTownship = 5
["subOrder"] = 0,
item.fromTownship = (item.sellsFor+1)*10*6
["text"] = goalText
end
})
elseif resource.id == 'melvorF:Leather' then
end
for _, item in ipairs(resource.itemConversions) do
end
item.toTownship = 20
 
item.fromTownship = 20*6
table.sort(reqTextPart,
function(a, b)
if a.goalOrder == b.goalOrder then
return a.subOrder < b.subOrder
else
return a.goalOrder < b.goalOrder
end
end
end
)
local requirements = {}
for i, req in ipairs(reqTextPart) do
table.insert(requirements, req.text)
end
-- We don't check tasks.requirements (so far it's only used to enumerate the Tutorial tasks so you only see 1 at a time)
table.insert(ret, table.concat(requirements, '<br/>'))
-- Rewards
table.insert(ret, '\n|')
local rewards = {}
local rewardsVariableQty = {}
if task.rewards.gp > 0 and not isDailyTask then
table.insert(rewards, Icons.GP(task.rewards.gp))
end
if task.rewards.slayerCoins > 0 then
if isDailyTask then
table.insert(rewardsVariableQty, Icons.SC())
else
else
for _, item in ipairs(resource.itemConversions) do
table.insert(rewards, Icons.SC(task.rewards.slayerCoins))
        item.toTownship = math.max(math.floor(1000/math.max(item.sellsFor, 1)), 2)
end
    item.fromTownship = math.max(item.sellsFor*6, 1)
end
end
for _, item in ipairs(task.rewards.items) do
local itemname = GameData.getEntityByID('items', item.id).name
table.insert(rewards, Shared.formatnum(item.quantity)..' '..Icons.Icon({itemname, type='item'}))
end
for _, skill in ipairs(task.rewards.skillXP) do
if not (isDailyTask and skill.id == 'melvorD:Township') then
local skillname = GameData.getSkillData(skill.id).name
table.insert(rewards, Shared.formatnum(skill.quantity)..' '..Icons.Icon({skillname, type='skill'})..' XP')
end
end
end
end
return resources
for _, townshipResource in ipairs(task.rewards.townshipResources) do
local resourcename = p._getResourceByID(townshipResource.id).name
table.insert(rewards, Shared.formatnum(townshipResource.quantity)..' '..Icons.Icon({resourcename, type='resource'}))
end
if not Shared.tableIsEmpty(rewardsVariableQty) then
table.insert(ret, '[[Township#Casual Tasks|Variable]] ' .. table.concat(rewardsVariableQty, ', ') .. '<br/>')
end
table.insert(ret, table.concat(rewards, '<br/>'))
 
-- Unlock requirements, daily task specific
if isDailyTask then
table.insert(ret, '\n|' .. Shop.getRequirementString(task.requirements))
end
return table.concat(ret)
end
end


-- Builds the table of trader items
-- Returns all the tasks of a given category
function Data.Township.getTraderTable(frame)
-- TODO: Support casual tasks
-- Get the resources data with associated trader data
function p.getTaskTable(frame)
local resources = Data.Township.Trader()
local category = frame.args ~= nil and frame.args[1] or frame
local categoryData = GameData.getEntityByID(Township.taskCategories, category)
local taskData, categoryName, isDailyTask = nil, nil, false
if category == 'Daily' then
isDailyTask = true
taskData = Township.casualTasks
categoryName = 'Casual'
elseif categoryData ~= nil then
taskData = Township.tasks
categoryName = categoryData.name
else
return Shared.printError('Invalid task category specified: ' .. (tostring(category) or 'nil'))
end
 
local taskcount = 0
local ret = {}
table.insert(ret, '{| class="wikitable lighttable stickyHeader" style="text-align:left"')
table.insert(ret, '\n|- class="headerRow-0"')
table.insert(ret, '\n!Task')
table.insert(ret, '\n!Requirements')
table.insert(ret, '\n!Rewards')
if isDailyTask then
table.insert(ret, '<br/>(In addition to [[Township#Casual Tasks|Variable]] ' .. Icons.GP() .. ' & ' .. Icons.Icon({'Township', type='skill', notext=true}) .. ' XP)')
end
if isDailyTask then
table.insert(ret, '\n!Unlock Requirements')
end
-- Build the text
for _, task in ipairs(taskData) do
local ret = {}
-- Filter out other categories
for _, resource in ipairs(resources) do
if task.category == category then
if #resource.itemConversions ~= 0 then -- Skips GP
taskcount = taskcount + 1
local ret_resource = {}
local title = categoryName .. ' ' .. taskcount
table.insert(ret, p._getTaskRow(title, task, isDailyTask))
-- Header
end
table.insert(ret_resource, '\r\n==='..resource.name..'===')
end
table.insert(ret_resource, '\r\n{| class="wikitable sortable stickyHeader"')
table.insert(ret, '\n|}')
table.insert(ret_resource, '\r\n|- class="headerRow-0"')
return table.concat(ret)
table.insert(ret_resource, '\r\n!Item')
end
table.insert(ret_resource, '\r\n!Name')
 
table.insert(ret_resource, '\r\n!DLC')
-- Returns a table containing all the tasks that reference an item or monster
table.insert(ret_resource, '\r\n!Level')
-- e.g. p.getTaskReferenceTable({'Chicken Coop', 'dungeon'})
table.insert(ret_resource, '\r\n!Give To')
-- name = item or monster name
table.insert(ret_resource, '\r\n!Take From')
-- type = 'item' or 'monster' or 'dungeon'
table.insert(ret_resource, '\r\n!Value')
function p.getTaskReferenceTable(frame)
table.insert(ret_resource, '\r\n!Value/Resource')
-- Returns a set containing all the desired IDs
if resource.id =='melvorF:Food' then
local function GetReferenceIDs(referenceName, referenceType)
table.insert(ret_resource, '\r\n!Heals')
local IDs = {}
table.insert(ret_resource, '\r\n!Heals/Resource')
if referenceType == 'dungeon' then
-- We get the tasks associated with all monsters in the dungeon
local monsters = GameData.getEntityByName('dungeons', referenceName).monsterIDs
for _, monster in ipairs(monsters) do
IDs[monster] = true
end
end
end
-- Each item
if referenceType == 'item' then
for _, item in ipairs(resource.itemConversions) do
IDs[GameData.getEntityByName('items', referenceName).id] = true
-- To indicate the skill level, we need to find the recipe of the item in the target skill
end
-- Unfortunately Module:Items/SourceTables.getItemSources does not provide parseable data
if referenceType == 'monster' then
local required_level = nil
IDs[Monsters.getMonster(referenceName).id] = true
local recipes = nil
end
return IDs
-- Get the skill based on the item.id or else use the resource's default skill
end
local skill_overrides = {
-- For a task, returns where to search for the desired IDs, given the type
['melvorD:Raw_Magic_Fish'] = 'melvorD:Fishing',
local function GetGetSearchTables(referenceType)
['melvorF:Apple'] = 'melvorD:Farming',
local function searchItems(task)
}
return {task.goals.items, task.rewards.items}
local skill = skill_overrides[item.id] or resource._skill
end
local function searchMonsters(task)
-- Check for upgraded Crafting items and downgrade them so we can display the crafting level for the base item
return {task.goals.monsters}
-- e.g. converts Black_Dhide_Body_U -> Black_Dhide_Body for the purposes of the lookup
end
local lookup_id = item.id
-- item -> searchItems; monster or dungeon -> searchMonsters
if string.match(item.id, '_U$') then
return referenceType == 'item' and searchItems or searchMonsters
lookup_id = string.sub(item.id, 1, #item.id - 2)
end
end
 
local args = frame.args ~= nil and frame.args or frame
-- Find the recipe's level
local referenceName = Shared.fixPagename(args[1])
local recipes = Data.Item.FindRecipes(lookup_id, skill)
local referenceType = args[2]
if #recipes == 1 then
local referenceIDs = GetReferenceIDs(referenceName, referenceType)
required_level = recipes[1].level
-- GetSearchTables = function searchItems/Monsters(task)
end
local GetSearchTables = GetGetSearchTables(referenceType)
 
-- Alright, now that we've found the required recipe and level, we can draw the item's row entry
local function checkTask(task)
table.insert(ret_resource, '\r\n|-')
local function checkID(entry)
-- Icon
return referenceIDs[entry.id] ~= nil
table.insert(ret_resource, '\r\n|style="text-align:center"|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
end
-- Name
for _, searchTable in ipairs(GetSearchTables(task)) do
table.insert(ret_resource, '\r\n|style="text-align:left"|'..Icons.Icon({item.name, type='item', noicon=true}))
-- Check to see if the table contains any of the IDs in referenceIDs
-- DLC
if searchTable[1] ~= nil then -- Make sure table is not empty
table.insert(ret_resource, '\r\n|style="text-align:center"|'..'XXX')
if #GameData.getEntities(searchTable, checkID) ~= 0 then -- Make sure we have at least 1 match
-- Level
return true
if required_level == nil then
-- Recipe not found, or multiple recipes found
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="0"|N/A')
else
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. required_level .. '"|'..Icons.Icon({GameData.getLocalID(skill), type="skill", notext=true})..' '..required_level)
end
-- Give To
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.toTownship .. '"|'..Icons.Icon({item.name, type='item', notext=true})..' '..Shared.formatnum(item.toTownship))
-- Take From
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.fromTownship .. '"|'..Icons.Icon(resource._icon)..' '..Shared.formatnum(item.fromTownship))
-- Value
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.sellsFor .. '"|'..Icons.GP(item.sellsFor))
-- Value/Resource
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.sellsFor/item.fromTownship .. '"|'..Icons.GP(Shared.round(item.sellsFor/item.fromTownship, 2, 2)))
if resource.id =='melvorF:Food' then
-- Heals
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.healsFor*10 .. '"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..Shared.formatnum(item.healsFor*10))
-- Heals/Resource
table.insert(ret_resource, '\r\n|style="text-align:center" data-sort-value="' .. item.healsFor*10/item.fromTownship .. '"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..Shared.round(item.healsFor*10/item.fromTownship, 2, 2))
end
end
end
end
table.insert(ret_resource, '\r\n|}')
table.insert(ret, table.concat(ret_resource))
end
end
return false
end
end
-- Find all tasks that contain the desired ids
local tasks = GameData.getEntities(Township.tasks, checkTask)
if #tasks == 0 then
return ''
end
-- Build the table
local ret = {}
table.insert(ret, '==Tasks==')
table.insert(ret, '\n{| class="wikitable" style="text-align:left"')
table.insert(ret, '\n!Task')
table.insert(ret, '\n!Requirements')
table.insert(ret, '\n!Rewards')
for _, task in ipairs(tasks) do
local categoryname = GameData.getEntityByID(Township.taskCategories, task.category).name
local title = '[[Township/Tasks#'..categoryname..'|'..categoryname..']]'
table.insert(ret, p._getTaskRow(title, task, false))
end
table.insert(ret, '\n|}')
return table.concat(ret)
return table.concat(ret)
end
end
local p = {}
p.getTraderTable = Data.Township.getTraderTable


return p
return p
1,048

edits