Module:Sandbox/GauTest/Township
Documentation for this module may be created at Module:Sandbox/GauTest/Township/doc
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local GameData = require('Module:GameData')
local Constants = require('Module:Constants')
local p = {}
local Township = GameData.getSkillData('melvorD:Township')
p.Township = Township
-- Returns the namespace name (eventually we should use an icon)
function p.PLACEHOLDER_NAMESPACE_ICON(namespace)
local namespaces = {
melvorD = 'Demo',
melvorF = 'Full',
melvorTotH = 'TotH'
}
return namespaces[namespace]
end
-- Returns the recipe for the item of a desired skill.
-- Unfortunately Module:Items/SourceTables.getItemSources does not provide parseable data so we instead use this quick function
function p._FindItemRecipes(itemid, skill)
-- No skill? No recipes
if skill == nil then
return {}
end
-- the key name for each skill in the json file
local skill_recipe_keys = {
['melvorD:Woodcutting'] = {recipes='trees', productID='productId'}, -- lowercase "d"
['melvorD:Fishing'] = {recipes='fish', productID='productId'}, -- lowercase "d"
['melvorD:Cooking'] = {recipes='recipes', productID='productID'},
['melvorD:Mining'] = {recipes='rockData', productID='productId'}, -- lowercase "d"
['melvorD:Smithing'] = {recipes='recipes', productID='productID'},
['melvorD:Farming'] = {recipes='recipes', productID='productId'}, -- lowercase "d"
['melvorD:Summoning'] = {recipes='recipes', productID='productID'},
['melvorD:Fletching'] = {recipes='recipes', productID='productID'},
['melvorD:Crafting'] = {recipes='recipes', productID='productID'},
['melvorD:Runecrafting'] = {recipes='recipes', productID='productID'},
['melvorD:Herblore'] = {recipes='recipes', productID='potionIDs'} -- Special case potions I-IV
--[[ Excluded skills:
Attack, Strength, Defence, Magic, Ranged, Prayer, Slayer
Thieving, Agility, Astrology, Firemaking, Township (not items)]]
}
local results = {}
local SkillData = GameData.getSkillData(skill)
local recipes = skill_recipe_keys[skill].recipes
local productID = skill_recipe_keys[skill].productID
if SkillData[recipes] ~= nil then
for _, recipe in ipairs(SkillData[recipes]) do
-- Special case for Herblore
if skill == 'melvorD:Herblore' then
-- Iterate over the 4 potion tiers
for _, potionid in ipairs(recipe[productID]) do
if itemid == potionid then
table.insert(results, Shared.clone(recipe))
end
end
-- Base case
else
if itemid == recipe[productID] then
table.insert(results, Shared.clone(recipe))
end
end
end
end
return results
end
-- Returns a list of all the Township resources
function p._ResourcesData()
-- Get a sorted list of all the resources
local resources = GameData.sortByOrderTable(Township.resources, Township.resourceDisplayOrder[1].ids)
resources = Shared.clone(resources)
return resources
end
-- Returns a list of all the Township resources along with the Trader's trade ratios
function p._TraderData()
-- Get the list of resources. We get a copy instead of directly using p.resources because we are going to modify the table
local resources = p._ResourcesData()
-- Get the list of tradeable items
-- See township.js -> TownshipResource.buildResourceItemConversions for the calculation of valid items
local function matchNone(item)
return false
end
local function matchFood(item)
return item.type == 'Food' and (not string.match(item.id, '_Perfect')) and item.category ~= 'Farming' and (not item.ignoreCompletion)
end
local function matchLogs(item)
return item.type == 'Logs'
end
local function matchOre(item)
return item.type == 'Ore' and item.id ~= 'melvorTotH:Meteorite_Ore'
end
local function matchCoal(item)
return item.id == 'melvorD:Coal_Ore'
end
local function matchBar(item)
return item.type == 'Bar' and item.id ~= 'melvorTotH:Meteorite_Bar'
end
local function matchHerb(item)
return item.type == 'Herb'
end
local function matchEssence(item)
return item.id == 'melvorD:Rune_Essence' or item.id == 'melvorTotH:Pure_Essence'
end
local function matchLeather(item)
return item.id == 'melvorD:Leather'
end
local function matchPotion(item)
return item.type == 'Potion' and string.match(item.id, '_IV')
end
local function matchClothing(item)
local valid_tiers = {'Leather', 'Hard Leather', 'Dragonhide', 'Elderwood', 'Revenant', 'Carrion'}
for _, tier in ipairs(valid_tiers) do
if item.tier == tier then
return true
end
end
return false
end
local traderMatchesList = {
['melvorF:GP'] = {traderMatches = matchNone},
['melvorF:Food'] = {traderMatches = matchFood},
['melvorF:Wood'] = {traderMatches = matchLogs},
['melvorF:Stone'] = {traderMatches = matchOre},
['melvorF:Ore'] = {traderMatches = matchOre},
['melvorF:Coal'] = {traderMatches = matchCoal},
['melvorF:Bar'] = {traderMatches = matchBar},
['melvorF:Herbs'] = {traderMatches = matchHerb},
['melvorF:Rune_Essence'] = {traderMatches = matchEssence},
['melvorF:Leather'] = {traderMatches = matchLeather},
['melvorF:Potions'] = {traderMatches = matchPotion},
['melvorF:Planks'] = {traderMatches = matchLogs},
['melvorF:Clothing'] = {traderMatches = matchClothing}
}
for _, resource in ipairs(resources) do
resource.itemConversions = Shared.clone(GameData.getEntities('items', traderMatchesList[resource.id].traderMatches))
end
-- Calculate the trader's conversion ratios
-- See township.js TownshipResource.getBaseConvertToTownshipRatio and TownshipResource.getBaseConvertFromTownshipRatio for the conversion prices
for _, resource in ipairs(resources) do
if resource.id == 'melvorF:Food' then
for _, item in ipairs(resource.itemConversions) do
item.toTownship = math.max(math.floor(1000/(item.healsFor*10)), 2)
item.fromTownship = item.healsFor*5*6
end
elseif resource.id == 'melvorF:Planks' then
for _, item in ipairs(resource.itemConversions) do
item.toTownship = math.max(math.floor(3000/math.max(item.sellsFor, 1)), 2)
item.fromTownship = math.max(math.ceil(item.sellsFor/2)*6, 1);
end
elseif resource.id == 'melvorF:Rune_Essence' then
for _, item in ipairs(resource.itemConversions) do
item.toTownship = 5
item.fromTownship = (item.sellsFor+1)*10*6
end
elseif resource.id == 'melvorF:Leather' then
for _, item in ipairs(resource.itemConversions) do
item.toTownship = 20
item.fromTownship = 20*6
end
else
for _, item in ipairs(resource.itemConversions) do
item.toTownship = math.max(math.floor(1000/math.max(item.sellsFor, 1)), 2)
item.fromTownship = math.max(item.sellsFor*6, 1)
end
end
end
return resources
end
p.resources = p._TraderData()
-- Builds the table of trader items
function p.GetTraderTable(frame)
-- Get the resources data with associated trader data
-- Build the text
local ret = {}
for _, resource in ipairs(p.resources) do
if #resource.itemConversions ~= 0 then -- Skips GP
local ret_resource = {}
-- Header
table.insert(ret_resource, '\r\n==='..resource.name..'===')
table.insert(ret_resource, '\r\n{| class="wikitable sortable stickyHeader"')
table.insert(ret_resource, '\r\n|- class="headerRow-0"')
table.insert(ret_resource, '\r\n!Item')
table.insert(ret_resource, '\r\n!Name')
table.insert(ret_resource, '\r\n!DLC')
table.insert(ret_resource, '\r\n!Level')
table.insert(ret_resource, '\r\n!Give To')
table.insert(ret_resource, '\r\n!Take From')
table.insert(ret_resource, '\r\n!Value')
table.insert(ret_resource, '\r\n!Value/Resource')
if resource.id =='melvorF:Food' then
table.insert(ret_resource, '\r\n!Heals')
table.insert(ret_resource, '\r\n!Heals/Resource')
end
-- Each item
for _, item in ipairs(resource.itemConversions) do
-- To indicate the skill level, we need to find the recipe of the item in the target skill
-- Unfortunately Module:Items/SourceTables.getItemSources does not provide parseable data
local required_level = nil
local recipes = nil
-- Get the skill based on the item.id or else use the resource's default skill
local skill_overrides = {
['melvorD:Raw_Magic_Fish'] = 'melvorD:Fishing',
['melvorF:Apple'] = 'melvorD:Farming',
}
local skill = skill_overrides[item.id] or p._GetResourceSkill(resource.id)
local skill_namespace, skill_localid = GameData.getLocalID(skill or '')
-- Check for upgraded Crafting items and downgrade them so we can display the crafting level for the base item
-- e.g. converts Black_Dhide_Body_U -> Black_Dhide_Body for the purposes of the lookup
local lookup_id = item.id
if string.match(item.id, '_U$') then
lookup_id = string.sub(item.id, 1, #item.id - 2)
end
-- Find the recipe's level
local recipes = p._FindItemRecipes(lookup_id, skill)
if #recipes == 1 then
required_level = recipes[1].level
end
-- Alright, now that we've found the required recipe and level, we can draw the item's row entry
table.insert(ret_resource, '\r\n|-')
-- Icon
table.insert(ret_resource, '\r\n|style="text-align:center"|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
-- Name
table.insert(ret_resource, '\r\n|style="text-align:left"|'..Icons.Icon({item.name, type='item', noicon=true}))
-- DLC
local item_namespace, item_localid = GameData.getLocalID(item.id)
table.insert(ret_resource, '\r\n|style="text-align:center"|'..p.PLACEHOLDER_NAMESPACE_ICON(item_namespace))
-- Level
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({skill_localid, 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.name, type='resource', notext=true})..' '..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
table.insert(ret_resource, '\r\n|}')
table.insert(ret, table.concat(ret_resource))
end
end
return table.concat(ret)
end
-- modifiers associated with specific biomes
local biome_data = {
['melvorF:Grasslands'] = {modifiers = {'increasedTownshipGrasslandsProduction', 'decreasedTownshipGrasslandsProduction'}},
['melvorF:Forest'] = {modifiers = {'increasedTownshipForestProduction', 'decreasedTownshipForestProduction'}},
['melvorF:Desert'] = {modifiers = {'increasedTownshipDesertProduction', 'decreasedTownshipDesertProduction'}},
['melvorF:Water'] = {modifiers = {'increasedTownshipWaterProduction', 'decreasedTownshipWaterProduction'}},
['melvorF:Swamp'] = {modifiers = {'increasedTownshipSwampProduction', 'decreasedTownshipSwampProduction'}},
['melvorF:Arid_Plains'] = {modifiers = {'increasedTownshipAridPlainsProduction', 'decreasedTownshipAridPlainsProduction'}},
['melvorF:Mountains'] = {modifiers = {'increasedTownshipMountainsProduction', 'decreasedTownshipMountainsProduction'}},
['melvorF:Valley'] = {modifiers = {'increasedTownshipValleyProduction', 'decreasedTownshipValleyProduction'}},
['melvorF:Jungle'] = {modifiers = {'increasedTownshipJungleProduction', 'decreasedTownshipJungleProduction'}},
['melvorF:Snowlands'] = {modifiers = {'increasedTownshipSnowlandsProduction', 'decreasedTownshipSnowlandsProduction', 'increasedTownshipCoalUsage', 'decreasedTownshipCoalUsage'}},
}
-- modifiers associated with specific buildings or resources
local unique_modifiers = {
['melvorF:Fishermans_Dock'] = {modifiers = {'increasedTownshipFishingDockProduction', 'decreasedTownshipFishingDockProduction'}},
['melvorF:Fishermans_Pier'] = {modifiers = {'increasedTownshipFishingDockProduction', 'decreasedTownshipFishingDockProduction'}},
['melvorF:Fishermans_Port'] = {modifiers = {'increasedTownshipFishingDockProduction', 'decreasedTownshipFishingDockProduction'}},
['melvorTotH:Fishermans_Estate'] = {modifiers = {'increasedTownshipFishingDockProduction', 'decreasedTownshipFishingDockProduction'}},
['melvorF:Magic_Emporium'] = {modifiers = {'increasedTownshipMagicEmporiumProduction', 'decreasedTownshipMagicEmporiumProduction'}},
['melvorF:Orchard'] = {modifiers = {'increasedTownshipOrchardProduction', 'decreasedTownshipOrchardProduction'}},
['melvorF:Farmland'] = {modifiers = {'increasedTownshipFarmProduction', 'decreasedTownshipFarmProduction'}},
['melvorF:Mill'] = {modifiers = {'increasedTownshipFarmProduction', 'decreasedTownshipFarmProduction'}},
['melvorF:Plantation'] = {modifiers = {'increasedTownshipFarmProduction', 'decreasedTownshipFarmProduction'}},
['melvorTotH:Farming_Estate'] = {modifiers = {'increasedTownshipFarmProduction', 'decreasedTownshipFarmProduction'}},
['melvorF:Woodcutters_Camp'] = {modifiers = {'increasedTownshipWoodcuttingProduction', 'decreasedTownshipWoodcuttingProduction'}},
['melvorF:Logging_Camp'] = {modifiers = {'increasedTownshipWoodcuttingProduction', 'decreasedTownshipWoodcuttingProduction'}},
['melvorF:Forestry_Camp'] = {modifiers = {'increasedTownshipWoodcuttingProduction', 'decreasedTownshipWoodcuttingProduction'}},
['melvorTotH:Forestry_Estate'] = {modifiers = {'increasedTownshipWoodcuttingProduction', 'decreasedTownshipWoodcuttingProduction'}},
['melvorF:Blacksmiths_Smithy'] = {modifiers = {'increasedTownshipBlacksmithProduction', 'decreasedTownshipBlacksmithProduction'}},
['melvorF:Blacksmiths_Forge'] = {modifiers = {'increasedTownshipBlacksmithProduction', 'decreasedTownshipBlacksmithProduction'}},
['melvorF:Blacksmiths_Workshop'] = {modifiers = {'increasedTownshipBlacksmithProduction', 'decreasedTownshipBlacksmithProduction'}},
['melvorTotH:Blacksmiths_Estate'] = {modifiers = {'increasedTownshipBlacksmithProduction', 'decreasedTownshipBlacksmithProduction'}},
['melvorF:Food'] = {modifiers = {'increasedTownshipFoodUsage', 'decreasedTownshipFoodUsage'}},
-- Absolute delta so doesn't apply to buildings
population = {modifiers = {'increasedTownshipPopulationCap', 'decreasedTownshipPopulationCap'}},
}
p.stats = {
happiness = {name='Happiness', modifiers = {'increasedTownshipHappiness', 'decreasedTownshipHappiness', 'increasedTownshipBuildingHappinessPenalties', 'decreasedTownshipBuildingHappinessPenalties'}},
education = {name='Education', modifiers = {'increasedTownshipEducation', 'decreasedTownshipEducation'}},
health = {name='Health', modifiers = {'increasedTownshipHealth', 'decreasedTownshipHealth'}},
storage = {name='Storage', modifiers = {'increasedTownshipMaxStorage', 'decreasedTownshipMaxStorage'}},
deadStorage = {name='Dead Storage', modifiers = {'increasedTownshipDeadStorage', 'decreasedTownshipDeadStorage'}},
population = {name='Population', modifiers = {}}, -- see unique_modifiers
}
-- Special modifiers
local special_modifiers = {
['all_buildings'] = {modifiers={'increasedTownshipBuildingCost', 'decreasedTownshipBuildingCost'}},
['all_production'] = {modifiers={'increasedTownshipResourceProduction', 'decreasedTownshipResourceProduction'}}, --Includes GP production as well
['unused'] = {modifiers = {'townshipDisableHunting'}},
}
-- skill -> The Skill used to produce the Trader's tradeable items
-- modifiers -> For buildings that produce this resource, the modifiers to search for.
local resource_info = {
['melvorF:GP'] = {skill = nil, modifiers={'increasedTownshipGPProduction', 'decreasedTownshipGPProduction', 'increasedTownshipTaxPerCitizen', 'decreasedTownshipTaxPerCitizen'}},
['melvorF:Food'] = {skill = 'melvorD:Cooking', modifiers={'increasedTownshipFoodProduction', 'decreasedTownshipFoodProduction'}},
['melvorF:Wood'] = {skill = 'melvorD:Woodcutting', modifiers={'increasedTownshipWoodProduction', 'decreasedTownshipWoodProduction'}},
['melvorF:Stone'] = {skill = 'melvorD:Mining', modifiers={'increasedTownshipStoneProduction', 'decreasedTownshipStoneProduction'}},
['melvorF:Ore'] = {skill = 'melvorD:Mining', modifiers={'increasedTownshipOreProduction', 'decreasedTownshipOreProduction'}},
['melvorF:Coal'] = {skill = 'melvorD:Mining', modifiers={'increasedTownshipCoalProduction', 'decreasedTownshipCoalProduction', 'TownshipCoalUsage'}},
['melvorF:Bar'] = {skill = 'melvorD:Smithing', modifiers={'increasedTownshipBarProduction', 'decreasedTownshipBarProduction'}},
['melvorF:Herbs'] = {skill = 'melvorD:Farming', modifiers={'increasedTownshipHerbProduction', 'decreasedTownshipHerbProduction'}},
['melvorF:Rune_Essence'] = {skill = 'melvorD:Mining', modifiers={'increasedTownshipRuneEssenceProduction', 'decreasedTownshipRuneEssenceProduction'}},
['melvorF:Leather'] = {skill = nil, modifiers={'increasedTownshipLeatherProduction', 'decreasedTownshipLeatherProduction'}},
['melvorF:Potions'] = {skill = 'melvorD:Herblore', modifiers={'increasedTownshipPotionProduction', 'decreasedTownshipPotionProduction'}},
['melvorF:Planks'] = {skill = 'melvorD:Woodcutting', modifiers={'increasedTownshipPlankProduction', 'decreasedTownshipPlankProduction'}},
['melvorF:Clothing'] = {skill = 'melvorD:Crafting', modifiers={'increasedTownshipClothingProduction', 'decreasedTownshipClothingProduction'}},
}
-- Gets a list of target modifiers based on the building name
function p._GetBuildingModifiers(building)
local modifiers = {}
local newModifiers = nil
local function addModifiers(_newModifiers)
if newModifiers ~= nil then
for _, newModifier in pairs(_newModifiers.modifiers) do
table.insert(modifiers, newModifier)
end
end
end
-- Unique ID modifiers
newModifiers = unique_modifiers[building.id]
addModifiers(newModifiers)
-- Building modifiers
newModifiers = special_modifiers.all_buildings
addModifiers(newModifiers)
-- Production modifiers
local anyProduction = false
for _, resource in ipairs(building.provides.resources) do
anyProduction = true
newModifiers = resource_info[resource.id]
addModifiers(newModifiers)
end
if anyProduction == true then
newModifiers = special_modifiers.all_production
addModifiers(newModifiers)
end
-- Benefit modifiers
for statid, stat in pairs(p.stats) do
if building.provides[statid] ~= nil and building.provides[statid] ~= 0 then
addModifiers(stat)
end
end
return modifiers
end
-- Gets a list of target modifiers based on a resource id
function p._GetResourceModifiers(resource_id)
local modifiers = {}
local newModifiers = nil
local function addModifiers(_newModifiers)
if newModifiers ~= nil then
for _, newModifier in pairs(_newModifiers.modifiers) do
table.insert(modifiers, newModifier)
end
end
end
-- Unique ID modifiers
newModifiers = unique_modifiers[resource_id]
addModifiers(newModifiers)
-- Production
newModifiers = special_modifiers.all_production
addModifiers(newModifiers)
-- Resource modifiers
newModifiers = resource_info[resource_id]
addModifiers(newModifiers)
return modifiers
end
-- Gets a list of target modifiers based on a stat id
function p._GetStatModifiers(stat_id)
local modifiers = {}
local newModifiers = nil
local function addModifiers(_newModifiers)
if newModifiers ~= nil then
for _, newModifier in pairs(_newModifiers.modifiers) do
table.insert(modifiers, newModifier)
end
end
end
-- Unique ID modifiers
newModifiers = unique_modifiers[stat_id]
addModifiers(newModifiers)
-- Production
newModifiers = p.stats[stat_id]
addModifiers(newModifiers)
return modifiers
end
-- Gets a list of target modifiers based on a biome id. Does not include buildings' biomeModifiers
function p._GetBiomeModifiers(biome_id)
local modifiers = {}
local newModifiers = nil
local function addModifiers(_newModifiers)
if newModifiers ~= nil then
for _, newModifier in pairs(_newModifiers.modifiers) do
table.insert(modifiers, newModifier)
end
end
end
-- Unique ID modifiers
newModifiers = biome_data[biome_id]
addModifiers(newModifiers)
return modifiers
end
-- Gets the modifiers for the specified entity
function p.GetModifiers(name, type)
if type == 'building' then
return p._GetBuildingModifiers(p._GetBuildingByName(name))
end
if type == 'resource' then
return p._GetResourceModifiers(name)
end
if type == 'stat' then
return p._GetStatModifiers(name)
end
if type == 'biome' then
return p._GetBiomeModifiers(name)
end
end
function p._GetResourceSkill(id)
return resource_info[id].skill
end
-- Gets a Township building by ID, e.g. melvorF:Hunters_Cabin
function p._GetBuildingByID(id)
return GameData.getEntityByID(Township.buildings, id)
end
-- Gets a Township building by name, e.g. Hunters Cabin
function p._GetBuildingByName(name)
return GameData.getEntityByName(Township.buildings, name)
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
-- Returns a string containing the Township level and population requirements for a tier
function p._GetTierText(tierlevel)
local tier = p._GetTierRequirements(tierlevel)
return Icons._SkillReq('Township', tier.level, false)..'<br>'..Icons.Icon({'Population', type='township', notext=true})..' '..tier.population
end
-- Gets a building and prepares all the relevant stats for the building
function p.GetBuildingTable(frame)
local name = frame.args ~= nil and frame.args[1] or frame
local building = Shared.clone(p._GetBuildingByName(name))
local ret = {}
-- Header
table.insert(ret, '\r\n{| class="wikitable infobox"')
-- Name
table.insert(ret, '\r\n|-\r\n!'..building.name)
-- Icon
table.insert(ret, '\r\n|-\r\n|style="text-align:center"|'..Icons.Icon({building.name, type='building', size='250', notext=true}))
-- ID
table.insert(ret, '\r\n|-\r\n| <b>Building ID:</b> '..building.id)
-- Type
table.insert(ret, '\r\n|-\r\n| <b>Type:</b> '..building.type)
-- Tier
local tier = p._GetTierText(building.tier)
table.insert(ret, '\r\n|-\r\n| <b>Requirements:</b><br>'..tier)
-- Upgrades From
table.insert(ret, '\r\n|-\r\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
local cost = p._GetBuildingBaseCost(building)
table.insert(ret, '<br>'..cost)
-- Upgrades To
local upgradesTo = p._GetBuildingIDUpgrade(building.id)
if upgradesTo ~= nil then
table.insert(ret, '\r\n|-\r\n| <b>Upgrades To:</b>')
table.insert(ret, '<br>'..Icons.Icon({upgradesTo.name, type='building'}))
local upgrade_cost = p._GetBuildingBaseCost(upgradesTo)
table.insert(ret, '<br>'..upgrade_cost)
end
-- Fixed benefits
local benefits = p._GetBuildingBenefits(building)
if benefits ~= nil then
table.insert(ret, '\r\n|-\r\n| <b>Provides:</b> '..benefits)
end
-- Production
local production = p._GetBuildingBaseProduction(building)
if production ~= nil then
table.insert(ret, '\r\n|-\r\n| <b>Base Production per '..Icons.Icon({'Workers', type='township', notext=true})..':</b><br>')
table.insert(ret, production)
end
-- Modifiers
if building.modifiers ~= nil and not Shared.tableIsEmpty(building.modifiers) then
table.insert(ret, '\r\n|-\r\n| <b>Modifiers:</b>\r\n'..Constants.getModifiersText(building.modifiers, true))
end
-- Biomes
table.insert(ret, '\r\n|-\r\n| <b>Biomes:</b><br><ul>')
for _, biomeid in ipairs(building.biomes) do
local biomename = GameData.getEntityByID(Township.biomes, biomeid).name
-- Optional hidden bonus/penalty for building
local modifier = nil
if #building.biomeModifiers > 0 then
modifier = GameData.getEntityByProperty(building.biomeModifiers, 'biomeID', biomeid)
end
if modifier ~= nil then
local color = modifier.value < 0 and 'red' or 'green'
local modifier_value = Shared.numStrWithSign(modifier.value)
table.insert(ret, '<li style="color:'..color..'"><b>'..biomename..' ('..modifier_value..'%)</b></li>')
else
table.insert(ret, '<li>'..biomename..'</li>')
end
end
-- End
table.insert(ret, '\r\n|}')
return table.concat(ret)
end
-- Given a resource id, return the job id
-- e.g. melvorF:Bar -> melvorF:Blacksmith
function p._GetJobFromResource(resource_id)
local job = GameData.getEntityByProperty(Township.jobs, 'produces', resource_id)
return job.id
end
-- Gets a string displaying the base production of a building, or nil if no production
function p._GetBuildingBaseProduction(building)
local production = Shared.clone(building.provides.resources)
if #production == 0 then
return nil
end
local retResources = {}
for _, resource in ipairs(production) do
local retProduction = {}
local job = p._GetJobFromResource(resource.id)
local workers = GameData.getEntityByID(building.provides.workers, job).quantity
-- Sourced from township.js -> Township.computeTownResourceGain()
local production = resource.quantity*100*(Township.tickLength/10)
local color = production < 0 and 'red' or 'green'
local resource_data = p._GetResourceByID(resource.id)
table.insert(retProduction, '<span style="color:'..color..'">'..Icons.Icon({resource_data.name, type='resource', notext=true})..' '..Shared.numStrWithSign(production)..'</span>')
if resource_data.requires ~= nil and #resource_data.requires > 0 then
for _, required_resource in ipairs(resource_data.requires) do
local demand = production*required_resource.quantity*100
local required_resource_data = p._GetResourceByID(required_resource.id)
table.insert(retProduction, '<span style="color:red">'..Icons.Icon({required_resource_data.name, type='resource', notext=true})..' -'..demand..'</span>')
end
end
retProduction = table.concat(retProduction, ', ')..'/t ('..Icons.Icon({'Workers', type='township', notext=true})..' '..workers..')'
table.insert(retResources, retProduction)
end
return table.concat(retResources, '<br>')
end
-- Gets a string displaying the building's benefits, or nil if no benefits
function p._GetBuildingBenefits(building)
local benefits = {}
for statid, stat in pairs(p.stats) do
if building.provides[statid] ~= nil and building.provides[statid] ~= 0 then
local quantity = building.provides[statid]
if quantity < 0 then
quantity = '<span style="color:red">'..quantity..'</span>'
else
quantity = Shared.numStrWithSign(quantity)
end
table.insert(benefits, Icons.Icon({stat.name, type='township', notext=true})..' '..quantity)
end
end
if #benefits > 0 then
return table.concat(benefits, ', ')
end
return nil
end
-- Given a building id, find the next building upgrade
function p._GetBuildingIDUpgrade(buildingid)
local function checkFunc(entity)
return entity.upgradesFrom ~= nil and entity.upgradesFrom == buildingid
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
-- Given a building, find the base resource cost
function p._GetBuildingBaseCost(building, _join)
local join = _join ~= nil and _join or ', '
local cost = {}
for _, resource in ipairs(building.cost) do
local resource_data = p._GetResourceByID(resource.id)
table.insert(cost, Icons.Icon({resource_data.name, type='resource', notext=true})..' '..resource.quantity)
end
return table.concat(cost, join)
end
-- Gets a resource from id
function p._GetResourceByID(id)
return GameData.getEntityByID(p.resources, id)
end
-- Gets a resource from name
function p._GetResourceByName(name)
return GameData.getEntityByName(p.resources, name)
end
-- Gets text for only the biomes that have a modifier for a building
function p._GetBuildingBiomeModifiers(biome)
local biomeRet = {}
for _, biome in ipairs(building.biomeModifiers) do
local biomename = GameData.getEntityByID(Township.biomes, biome.biomeID).name
local color = biome.value < 0 and 'red' or 'green'
local biome_value = Shared.numStrWithSign(biome.value)
table.insert(biomeRet, '<li style="color:'..color..'">'..biomename..' ('..biome_value..'%)</li>')
end
if #biomeRet == 0 then
return nil
end
return '<ul>'..table.concat(biomeRet)..'</ul>'
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)
-- 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
-- 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._GetBuildingIDUpgrade(_curBuilding.id)
if _curBuilding == nil then
break
end
end
if #buildingList == 1 then
return ''
end
local ret = {}
table.insert(ret, '\r\n== Upgrade Chart ==')
table.insert(ret, '\r\n{| class="wikitable"')
-- Name
table.insert(ret, '\r\n|- style="text-align:center" \r\n! Name')
for _, building in ipairs(buildingList) do
table.insert(ret, '\r\n!'..Icons.Icon({building.name, type='building'}))
end
-- Tier
table.insert(ret, '\r\n|-\r\n! Requirements')
for _, building in ipairs(buildingList) do
local tier = p._GetTierText(building.tier)
table.insert(ret, '\r\n|'..tier)
end
-- Cost
table.insert(ret, '\r\n|-\r\n! Cost')
for _, building in ipairs(buildingList) do
local cost = p._GetBuildingBaseCost(building)
table.insert(ret, '\r\n|'..cost)
end
-- Optional params
-- Generate a row
-- textFunc: returns nil if no data for a building, or else returns a string
local function BuildOptionalRow(header, textFunc)
local texts = {}
local hasTexts = false
for _, building in ipairs(buildingList) do
local text = textFunc(building)
hasTexts = hasTexts == true or text ~= nil
texts = texts ~= nil and texts or ''
table.insert(texts, text)
end
if hasTexts == true then
texts = table.concat(texts, '\r\n|')
table.insert(ret, header..texts)
end
end
BuildOptionalRow('\r\n|-\r\n! Benefits\r\n|', p._GetBuildingBenefits)
BuildOptionalRow('\r\n|-\r\n! Base Production per '..Icons.Icon({'Workers', type='township', notext=true})..'\r\n|', p._GetBuildingBaseProduction)
BuildOptionalRow('\r\n|-\r\n! Biome Production Modifiers\r\n|', p._GetBuildingBiomeModifiers)
-- End
table.insert(ret, '\r\n|}')
return table.concat(ret)
end
local FREE_LAND = Township.sectionSize
-- Gets the cost of the current price of land
-- Taken from township.js -> Township.getNextSectionCost
function p.GetLandCost(frame)
local nthland = tonumber(frame.args ~= nil and frame.args[1] or frame)
return p._GetLandCost(nthland)
end
function p._GetLandCost(nthland)
-- First FREE_LAND plots of land are free
if nthland <= FREE_LAND then
return 0
end
return math.floor(15^(0.0100661358978*(nthland/32) + (nthland/32)^0.42))
end
-- Gets the cost to buy land until you have X amount of available land
-- Currently the max is 2048 land
function p.GetCumulativeLandCost(frame)
local totalLand = tonumber(frame.args ~= nil and frame.args[1] or frame)
return p._GetCumulativeLandCost(totalLand)
end
function p._GetCumulativeLandCost(totalLand)
local cost = 0
while totalLand > FREE_LAND do
cost = cost + p._GetLandCost(totalLand)
totalLand = totalLand - 1
end
return cost
end
-- Returns a table showing the land cost of a town
function p.GetLandCostTable()
local ret = {}
table.insert(ret, '\r\n{| class="wikitable"')
table.insert(ret, '\r\n|- style="text-align:center" \r\n! Total Land \r\n! Single Land Cost \r\n! Total Cost')
for i=FREE_LAND,Township.maxTownSize,FREE_LAND do
table.insert(ret, '\r\n|-\r\n|'..i..'\r\n|'..Icons.GP(p._GetLandCost(i))..'\r\n|'..Icons.GP(p._GetCumulativeLandCost(i)))
end
table.insert(ret, '\r\n|}')
return table.concat(ret)
end
return p