572
edits
(Reset for STATUE fix) |
No edit summary |
||
(40 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
local GameData = require('Module:GameData') | local GameData = require('Module:GameData') | ||
local Constants = require('Module:Constants') | local Constants = require('Module:Constants') | ||
local p = {} | local p = {} | ||
Line 10: | Line 9: | ||
p.Township = Township | p.Township = Township | ||
-- Returns the recipe for the item of a desired skill. | -- Returns the recipe for the item of a desired skill. | ||
Line 79: | Line 69: | ||
-- Get a sorted list of all the resources | -- Get a sorted list of all the resources | ||
local resources = GameData.sortByOrderTable(Township.resources, Township.resourceDisplayOrder | local resources = GameData.sortByOrderTable(Township.resources, Township.resourceDisplayOrder) | ||
resources = Shared.clone(resources) | resources = Shared.clone(resources) | ||
return resources | return resources | ||
end | |||
-- Returns a sorted list of all Township buildings | |||
function p._SortedBuildings() | |||
return GameData.sortByOrderTable(Township.buildings, Township.buildingDisplayOrder) | |||
end | end | ||
Line 123: | Line 118: | ||
end | end | ||
local function matchClothing(item) | local function matchClothing(item) | ||
return item.id == 'melvorD:Green_Dragonhide' or item.id == 'melvorD:Blue_Dragonhide' or item.id == 'melvorD:Red_Dragonhide' or item.id == 'melvorD:Black_Dragonhide' or item.id == 'melvorF:Elder_Dragonhide' | |||
end | end | ||
Line 158: | Line 147: | ||
for _, item in ipairs(resource.itemConversions) do | for _, item in ipairs(resource.itemConversions) do | ||
item.toTownship = math.max(math.floor(1000/(item.healsFor*10)), 2) | item.toTownship = math.max(math.floor(1000/(item.healsFor*10)), 2) | ||
item.fromTownship = item.healsFor*5*6 | item.fromTownship = item.healsFor*5*6*5 | ||
end | end | ||
elseif resource.id == 'melvorF:Planks' then | elseif resource.id == 'melvorF:Planks' then | ||
Line 202: | Line 191: | ||
table.insert(ret_resource, '\r\n!Item') | table.insert(ret_resource, '\r\n!Item') | ||
table.insert(ret_resource, '\r\n!Name') | table.insert(ret_resource, '\r\n!Name') | ||
table.insert(ret_resource, '\r\n!Level') | table.insert(ret_resource, '\r\n!Level') | ||
table.insert(ret_resource, '\r\n!Give To') | table.insert(ret_resource, '\r\n!Give To') | ||
Line 246: | Line 234: | ||
table.insert(ret_resource, '\r\n|style="text-align:center"|'..Icons.Icon({item.name, type='item', size='50', notext=true})) | table.insert(ret_resource, '\r\n|style="text-align:center"|'..Icons.Icon({item.name, type='item', size='50', notext=true})) | ||
-- Name | -- Name | ||
table.insert(ret_resource, '\r\n|style="text-align:left"|'..Icons.Icon({item.name, type='item', noicon=true} | table.insert(ret_resource, '\r\n|style="text-align:left"|'..Icons.getExpansionIcon(item.id)..Icons.Icon({item.name, type='item', noicon=true})) | ||
-- Level | -- Level | ||
if required_level == nil then | if required_level == nil then | ||
Line 295: | Line 280: | ||
['melvorF:Potions'] = {skill = 'melvorD:Herblore'}, | ['melvorF:Potions'] = {skill = 'melvorD:Herblore'}, | ||
['melvorF:Planks'] = {skill = 'melvorD:Woodcutting'}, | ['melvorF:Planks'] = {skill = 'melvorD:Woodcutting'}, | ||
['melvorF:Clothing'] = {skill = | ['melvorF:Clothing'] = {skill = nil} | ||
} | } | ||
function p._GetResourceSkill(id) | function p._GetResourceSkill(id) | ||
Line 303: | Line 288: | ||
-- Gets a Township building by ID, e.g. melvorF:Hunters_Cabin | -- Gets a Township building by ID, e.g. melvorF:Hunters_Cabin | ||
function p._GetBuildingByID(id) | function p._GetBuildingByID(id) | ||
return GameData.getEntityByID(Township.buildings, 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 | end | ||
-- Gets a Township building by name, e.g. Hunters Cabin | -- Gets a Township building by name, e.g. Hunters Cabin | ||
function p._GetBuildingByName(name) | function p._GetBuildingByName(name) | ||
return GameData.getEntityByName(Township.buildings, 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 | end | ||
Line 382: | Line 385: | ||
-- Biomes | -- Biomes | ||
table.insert(ret, '\r\n|-\r\n| <b>Biomes:</b | table.insert(ret, '\r\n|-\r\n| <b>Biomes:</b>') | ||
for _, biomeid in ipairs(building.biomes) do | for _, biomeid in ipairs(building.biomes) do | ||
local biomename = GameData.getEntityByID(Township.biomes, biomeid).name | local biomename = GameData.getEntityByID(Township.biomes, biomeid).name | ||
Line 393: | Line 396: | ||
local color = modifier.value < 0 and 'red' or 'green' | local color = modifier.value < 0 and 'red' or 'green' | ||
local modifier_value = Shared.numStrWithSign(modifier.value) | local modifier_value = Shared.numStrWithSign(modifier.value) | ||
table.insert(ret, '< | table.insert(ret, '<br>'..Icons.Icon({biomename, type='biome', notext=true, nolink=true})..' <span style="color:'..color..'"><b>'..biomename..' ('..modifier_value..'%)</b></span>') | ||
else | else | ||
table.insert(ret, '< | table.insert(ret, '<br>'..Icons.Icon({biomename, type='biome', notext=true, nolink=true})..' <span>'..biomename..'</span>') | ||
end | end | ||
end | end | ||
Line 450: | Line 453: | ||
education = 'Education', | education = 'Education', | ||
storage = 'Storage', | storage = 'Storage', | ||
deadStorage = 'Dead Storage' | deadStorage = 'Dead Storage', | ||
worship = 'Worship' | |||
} | } | ||
for key, stat in pairs(stats) do | for key, stat in pairs(stats) do | ||
Line 512: | Line 516: | ||
local color = biome.value < 0 and 'red' or 'green' | local color = biome.value < 0 and 'red' or 'green' | ||
local biome_value = Shared.numStrWithSign(biome.value) | local biome_value = Shared.numStrWithSign(biome.value) | ||
table.insert(biomeRet, '< | table.insert(biomeRet, Icons.Icon({biomename, type='biome', notext=true, nolink=true})..' <span style="color:'..color..'">'..biomename..' ('..biome_value..'%)</span>') | ||
end | end | ||
if #biomeRet == 0 then | if #biomeRet == 0 then | ||
return nil | return nil | ||
end | end | ||
return | return table.concat(biomeRet, '<br>') | ||
end | end | ||
Line 644: | Line 648: | ||
table.insert(ret, '\r\n|-\r\n|'..i..'\r\n|'..Icons.GP(p._GetLandCost(i))..'\r\n|'..Icons.GP(p._GetCumulativeLandCost(i))) | table.insert(ret, '\r\n|-\r\n|'..i..'\r\n|'..Icons.GP(p._GetLandCost(i))..'\r\n|'..Icons.GP(p._GetCumulativeLandCost(i))) | ||
end | end | ||
table.insert(ret, '\r\n|}') | |||
return table.concat(ret) | |||
end | |||
-- Generates a table showing which buildings can be built in which biomes | |||
-- Skips upgraded buildings | |||
function p.GetBuildingBiomeTable() | |||
-- Setup the table | |||
local ret = {} | |||
table.insert(ret, '\r\n{| class="wikitable sortable" style="text-align:center"') | |||
table.insert(ret, '\r\n!Building') | |||
-- Make a biomeModifiers table that will keep track of the bonus of each building | |||
-- At the same time, make the output table header | |||
local biomeModifiersMaster = {} | |||
for _, biome in ipairs(Township.biomes) do | |||
table.insert(ret, '\r\n!'..Icons.Icon({biome.name, type='biome', notext=true, nolink=true})..'<br>'..biome.name) | |||
biomeModifiersMaster[biome.id] = false | |||
end | |||
for _, _building in ipairs(p._SortedBuildings()) do | |||
-- Fix melvorF:Statues | |||
local building = p._GetBuildingByID(_building.id) | |||
-- Skip upgraded buildings | |||
local downgrade = p._GetBuildingDowngrade(building) | |||
if downgrade == nil then | |||
-- Let's populate the biome habitability data | |||
local biomeModifiers = Shared.clone(biomeModifiersMaster) | |||
-- Set all valid biomes to 0 | |||
for _, biomeid in ipairs(building.biomes) do | |||
biomeModifiers[biomeid] = 0 | |||
end | |||
-- Then add the biome modifier values | |||
for _, biomeModifier in ipairs(building.biomeModifiers) do | |||
biomeModifiers[biomeModifier.biomeID] = biomeModifier.value | |||
end | |||
-- Let's build the row | |||
table.insert(ret, '\r\n|-') | |||
table.insert(ret, '\r\n!data-sort-value="'..building.name..'" style="text-align:left"|'..Icons.Icon({building.name, type='building'})) | |||
for _, biome in ipairs(Township.biomes) do | |||
local modifier = biomeModifiers[biome.id] | |||
if modifier then | |||
if modifier == 0 then | |||
-- Buildable but no bonuses | |||
table.insert(ret, '\r\n|class="table-na"|+0%') | |||
else | |||
-- Bonus or penalty | |||
local class = modifier < 0 and 'table-negative' or 'table-positive' | |||
local modifier_value = Shared.numStrWithSign(modifier) | |||
table.insert(ret, '\r\n|class="'..class..'"|<b>'..modifier_value..'%</b>') | |||
end | |||
else | |||
-- Invalid biome | |||
table.insert(ret, '\r\n|style="border:0px"|') | |||
end | |||
end | |||
end | |||
end | |||
table.insert(ret, '\r\n|}') | |||
return table.concat(ret) | |||
end | |||
-- Generates a table showing all the maps and the number of biomes | |||
-- Skips upgraded buildings | |||
function p.GetMapTable() | |||
-- Setup the table | |||
local ret = {} | |||
table.insert(ret, '\r\n{| class="wikitable sortable" style="text-align:center"') | |||
table.insert(ret, '\r\n!Map') | |||
-- Make two table that will keep track of the max/min amount of land for each biome | |||
-- At the same time, make the output table header | |||
local biomeMax = {} | |||
local biomeMin = {} | |||
for _, biome in ipairs(Township.biomes) do | |||
table.insert(ret, '\r\n!'..Icons.Icon({biome.name, type='biome', notext=true, nolink=true})..'<br>'..biome.name) | |||
biomeMax[biome.id] = -1 | |||
biomeMin[biome.id] = Township.maxTownSize + 1 | |||
end | |||
-- Find the min and max amount for each biome | |||
for _, map in ipairs(Township.maps) do | |||
for _, biome in ipairs(map.biomes) do | |||
biomeMax[biome.biomeID] = math.max(biomeMax[biome.biomeID], biome.count) | |||
biomeMin[biome.biomeID] = math.min(biomeMin[biome.biomeID], biome.count) | |||
end | |||
end | |||
-- Draw all the map rows | |||
for _, map in ipairs(Township.maps) do | |||
table.insert(ret, '\r\n|-') | |||
table.insert(ret, '\r\n!style="text-align:left"|'..map.name) | |||
for _, biome in ipairs(map.biomes) do | |||
-- Color the cell if min or max value | |||
local max = biomeMax[biome.biomeID] | |||
local min = biomeMin[biome.biomeID] | |||
local count = biome.count | |||
local class = count == max and 'table-positive' or count == min and 'table-negative' or '' | |||
-- Insert cell | |||
table.insert(ret, '\r\n|class="'..class..'"|'..count) | |||
end | |||
end | |||
table.insert(ret, '\r\n|}') | |||
return table.concat(ret) | |||
end | |||
-- Returns a row containing a task given a title and a task table | |||
function p._GetTaskRow(title, task) | |||
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 | |||
local titlespan = hasDescription == true and 'rowspan="2"|' or '' | |||
-- Title | |||
table.insert(ret, '\r\n|-') | |||
table.insert(ret, '\r\n!'..titlespan..title) | |||
-- Description | |||
if hasDescription then | |||
table.insert(ret, '\r\n|colspan="2"|'..task.description) | |||
table.insert(ret, '\r\n|-') | |||
end | |||
-- Requirements | |||
table.insert(ret, '\r\n|') | |||
local requirements = {} | |||
for _, item in ipairs(task.goals.items) do | |||
local itemname = GameData.getEntityByID('items', item.id).name | |||
table.insert(requirements, Shared.formatnum(item.quantity)..' '..Icons.Icon({itemname, type='item'})) | |||
end | |||
for _, monster in ipairs(task.goals.monsters) do | |||
local monstername = GameData.getEntityByID('monsters', monster.id).name | |||
table.insert(requirements, Shared.formatnum(monster.quantity)..' '..Icons.Icon({monstername, type='monster'})) | |||
end | |||
for _, skill in ipairs(task.goals.skillXP) do | |||
local skillname = GameData.getSkillData(skill.id).name | |||
table.insert(requirements, Shared.formatnum(skill.quantity)..' '..Icons.Icon({skillname, type='skill'})..' XP') | |||
end | |||
for _, building in ipairs(task.goals.buildings) do | |||
local buildingname = p._GetBuildingByID(building.id).name | |||
table.insert(requirements, Shared.formatnum(building.quantity)..' '..Icons.Icon({buildingname, type='building'})) | |||
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, '\r\n|') | |||
local rewards = {} | |||
if task.rewards.gp ~= 0 then | |||
table.insert(rewards, Icons.GP(task.rewards.gp)) | |||
end | |||
if task.rewards.slayerCoins ~= 0 then | |||
table.insert(rewards, Icons.SC(task.rewards.slayerCoins)) | |||
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 | |||
local skillname = GameData.getSkillData(skill.id).name | |||
table.insert(rewards, Shared.formatnum(skill.quantity)..' '..Icons.Icon({skillname, type='skill'})..' XP') | |||
end | |||
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 | |||
table.insert(ret, table.concat(rewards, '<br>')) | |||
return table.concat(ret) | |||
end | |||
-- Returns all the tasks of a given category | |||
function p.GetTaskTable(frame) | |||
local category = frame.args ~= nil and frame.args[1] or frame | |||
local categoryname = GameData.getEntityByID(Township.taskCategories, category).name | |||
local taskcount = 0 | |||
local ret = {} | |||
table.insert(ret, '\r\n{| class="wikitable lighttable" style="text-align:left"') | |||
table.insert(ret, '\r\n!Task') | |||
table.insert(ret, '\r\n!Requirements') | |||
table.insert(ret, '\r\n!Rewards') | |||
for _, task in ipairs(Township.tasks) do | |||
-- Filter out other categories | |||
if task.category == category then | |||
taskcount = taskcount + 1 | |||
local title = categoryname..' '..taskcount | |||
table.insert(ret, p._GetTaskRow(title, task)) | |||
end | |||
end | |||
table.insert(ret, '\r\n|}') | |||
return table.concat(ret) | |||
end | |||
-- Returns a table containing all the tasks that reference an item or monster | |||
-- e.g. p.GetTaskReferenceTable({'Chicken Coop', 'dungeon'}) | |||
-- name = item or monster name | |||
-- type = 'item' or 'monster' or 'dungeon' | |||
function p.GetTaskReferenceTable(frame) | |||
-- Returns a set containing all the desired IDs | |||
local function GetReferenceIDs(referenceName, referenceType) | |||
local IDs = {} | |||
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 | |||
if referenceType == 'item' then | |||
IDs[GameData.getEntityByName('items', referenceName).id] = true | |||
end | |||
if referenceType == 'monster' then | |||
IDs[GameData.getEntityByName('monsters', referenceName).id] = true | |||
end | |||
return IDs | |||
end | |||
-- For a task, returns where to search for the desired IDs, given the type | |||
local function GetGetSearchTables(referenceType) | |||
local function searchItems(task) | |||
return {task.goals.items, task.rewards.items} | |||
end | |||
local function searchMonsters(task) | |||
return {task.goals.monsters} | |||
end | |||
-- item -> searchItems; monster or dungeon -> searchMonsters | |||
return referenceType == 'item' and searchItems or searchMonsters | |||
end | |||
local args = frame.args ~= nil and frame.args or frame | |||
local referenceName = Shared.fixPagename(args[1]) | |||
local referenceType = args[2] | |||
local referenceIDs = GetReferenceIDs(referenceName, referenceType) | |||
-- GetSearchTables = function searchItems/Monsters(task) | |||
local GetSearchTables = GetGetSearchTables(referenceType) | |||
local function checkTask(task) | |||
local function checkID(entry) | |||
return referenceIDs[entry.id] ~= nil | |||
end | |||
for _, searchTable in ipairs(GetSearchTables(task)) do | |||
-- Check to see if the table contains any of the IDs in referenceIDs | |||
if searchTable[1] ~= nil then -- Make sure table is not empty | |||
if #GameData.getEntities(searchTable, checkID) ~= 0 then -- Make sure we have at least 1 match | |||
return true | |||
end | |||
end | |||
end | |||
return false | |||
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, '\r\n{| class="wikitable" style="text-align:left"') | |||
table.insert(ret, '\r\n!Task') | |||
table.insert(ret, '\r\n!Requirements') | |||
table.insert(ret, '\r\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)) | |||
end | |||
table.insert(ret, '\r\n|}') | |||
return table.concat(ret) | |||
end | |||
function p.GetWorshipTable() | |||
local function GetCheckpointCell(checkpoint) | |||
return '\r\n|-\r\n!'..checkpoint..'%<br>'..(checkpoint*Township.maxWorship/100)..'/'..Township.maxWorship | |||
end | |||
local ret = {} | |||
table.insert(ret, '\r\n{| class="wikitable" style="text-align:left"') | |||
table.insert(ret, '\r\n!'..Icons.Icon({'Worship', type='township', nolink=true})) | |||
-- Names | |||
for _, worship in ipairs(Township.worships) do | |||
if worship.isHidden == false then | |||
table.insert(ret, '\r\n!'..Icons.Icon({worship.name, type='monster', size=50})..Icons.Icon({'Statue of '..worship.name, type='building', size=50, notext=true})) | |||
end | |||
end | |||
-- Requirements | |||
-- Hard-coded because there's only 1 requirement | |||
table.insert(ret, '\r\n|-\r\n!Requirements') | |||
local requirements = { | |||
['melvorF:Bane'] = 'Completion of<br>'..Icons.Icon({'Impending Darkness Event', type='dungeon'}) | |||
} | |||
for _, worship in ipairs(Township.worships) do | |||
if worship.isHidden == false then | |||
local requirement = requirements[worship.id] ~= nil and requirements[worship.id] or 'None' | |||
table.insert(ret, '\r\n|style="text-align:center|'..requirement) | |||
end | |||
end | |||
-- Base modifiers | |||
table.insert(ret, GetCheckpointCell(0)) | |||
for _, worship in ipairs(Township.worships) do | |||
mw.logObject(worship.isHidden == false) | |||
if worship.isHidden == false then | |||
table.insert(ret, '\r\n|'..Constants.getModifiersText(worship.modifiers)) | |||
end | |||
end | |||
-- Checkpoint modifiers | |||
for i, checkpoint in ipairs(Township.worshipCheckpoints) do | |||
table.insert(ret, GetCheckpointCell(checkpoint)) | |||
for _, worship in ipairs(Township.worships) do | |||
if worship.isHidden == false then | |||
table.insert(ret, '\r\n|'..Constants.getModifiersText(worship.checkpoints[i])) | |||
end | |||
end | |||
end | |||
-- Total sum | |||
table.insert(ret, '\r\n|-\r\n!Total') | |||
for _, worship in ipairs(Township.worships) do | |||
if worship.isHidden == false then | |||
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, '\r\n|'..Constants.getModifiersText(modifiers)) | |||
end | |||
end | |||
table.insert(ret, '\r\n|}') | table.insert(ret, '\r\n|}') | ||
return table.concat(ret) | return table.concat(ret) |
edits