<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.hoodedhorse.com/Against_the_Storm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=76561198045847734</id>
	<title>Against the Storm Official Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.hoodedhorse.com/Against_the_Storm/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=76561198045847734"/>
	<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/Special:Contributions/76561198045847734"/>
	<updated>2026-10-06T03:01:58Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.5</generator>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Module:FarmsData&amp;diff=8574</id>
		<title>Module:FarmsData</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Module:FarmsData&amp;diff=8574"/>
		<updated>2024-10-09T09:31:27Z</updated>

		<summary type="html">&lt;p&gt;76561198045847734: Homestead has 4 recipes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;---&lt;br /&gt;
--- Module for compiling farming buildings information from wiki data sources.&lt;br /&gt;
--- Restructures the flat data tables that are produced from CsvUtils to make&lt;br /&gt;
--- them more conducive to Lua methods that need to display the information.&lt;br /&gt;
----&lt;br /&gt;
--- The standard way of using this module is a call like the following:&lt;br /&gt;
---&lt;br /&gt;
--- area = FarmsData.getFarmArea(farmName)&lt;br /&gt;
---&lt;br /&gt;
--- This will return the working radius of the farm. It is preferable to&lt;br /&gt;
--- call the getter methods with the name of the farm rather than&lt;br /&gt;
--- retrieving the entire data record for the farm. This way your code&lt;br /&gt;
--- stays protected from variations in this module. These getter methods are&lt;br /&gt;
--- called with the plain-language display name of the farm, as spelled&lt;br /&gt;
--- in the game, including punctuation.&lt;br /&gt;
---&lt;br /&gt;
--- * longDescription = FarmsData.getFarmDescription(farmName)&lt;br /&gt;
--- * constructionCategory = FarmsData.getFarmCategory(farmName)&lt;br /&gt;
--- * sizeX, sizeY = FarmsData.getFarmSize(farmName)&lt;br /&gt;
--- * requiredGoodID, stackSize = FarmsData.getFarmRequiredGood(farmName, requirementIndex)&lt;br /&gt;
--- * timeSeconds = FarmsData.getFarmConstructionTime(farmName)&lt;br /&gt;
--- * cityScore = FarmsData.getFarmCityScore(farmName)&lt;br /&gt;
--- * isMovable = FarmsData.isFarmMovable(farmName)&lt;br /&gt;
--- * isInitiallyEssential = FarmsData.isFarmInitiallyEssential(farmName)&lt;br /&gt;
--- * radius = FarmsData.getFarmArea(farmName)&lt;br /&gt;
--- * storageCapacity = FarmsData.getFarmStorage(farmName)&lt;br /&gt;
--- * workplaces = FarmsData.getFarmNumberOfWorkplaces(farmName)&lt;br /&gt;
--- * goodID, stackSize = FarmsData.getFarmRecipeProduct(farmName, recipeIndex)&lt;br /&gt;
--- * gradeStars, plantingSeconds, harvestSeconds = FarmsData.getFarmRecipeStats(farmName, recipeIndex)&lt;br /&gt;
--- * iconFilename = FarmsData.getFarmIcon(farmName)&lt;br /&gt;
---&lt;br /&gt;
--- And the following getter methods are all called with the farm&#039;s ID.&lt;br /&gt;
--- You will have an ID instead of a display name when dealing with other data&lt;br /&gt;
--- like recipes, species, etc.&lt;br /&gt;
---&lt;br /&gt;
--- * name = FarmsData.getFarmNameByID(farmID)&lt;br /&gt;
--- * iconFilename = FarmsData.getFarmIconByID(farmID)&lt;br /&gt;
---&lt;br /&gt;
--- You can retrieve all farms that produce a certain good (with any grade of&lt;br /&gt;
--- efficiency) with the following getter:&lt;br /&gt;
---&lt;br /&gt;
--- farmNamesTable = FarmsData.getFarmNamesWithRecipeProductID(productID)&lt;br /&gt;
---&lt;br /&gt;
--- There are methods to retrieve the lists of required goods, workplaces, and&lt;br /&gt;
--- recipes, but it is advised to instead use the getter methods&lt;br /&gt;
--- getFarmRequiredGood, getFarmNumberOfWorkplaces, and&lt;br /&gt;
--- getFarmRecipe with an index desired, which is good for loops. If&lt;br /&gt;
--- you must get the tables, there are three &amp;quot;getAll&amp;quot; methods:&lt;br /&gt;
---&lt;br /&gt;
--- * requiredGoodsTable = FarmsData.getAllFarmRequiredGoods(farmName)&lt;br /&gt;
--- * workplacesTable = FarmsData.getAllFarmWorkplaces(farmName)&lt;br /&gt;
--- * recipesTable = FarmsData.getAllFarmRecipes(farmName)&lt;br /&gt;
---&lt;br /&gt;
--- As a last resort, or if you need to transform the data structure, you can&lt;br /&gt;
--- call the method getAllDataForFarm(farmName) or&lt;br /&gt;
--- getAllDataForFarmByID(farmID). These return a whole record&lt;br /&gt;
--- from the data table corresponding to the required display name or ID.&lt;br /&gt;
---&lt;br /&gt;
--- The data table for farms has the following structure:&lt;br /&gt;
---&lt;br /&gt;
--- farmsTable = {&lt;br /&gt;
--- 	[&amp;quot;farm1_ID&amp;quot;] = {&lt;br /&gt;
--- 		[&amp;quot;id&amp;quot;] = &amp;quot;farm1_ID&amp;quot;,&lt;br /&gt;
--- 		[&amp;quot;displayName&amp;quot;] = &amp;quot;Plain Language Name&amp;quot;,&lt;br /&gt;
--- 		[&amp;quot;description&amp;quot;] = &amp;quot;A long string with some HTML entities too.&amp;quot;,&lt;br /&gt;
--- 		[&amp;quot;category&amp;quot;] = &amp;quot;Construction toolbar category&amp;quot;,&lt;br /&gt;
--- 		[&amp;quot;sizeX&amp;quot;] = 9, size in tiles&lt;br /&gt;
--- 		[&amp;quot;sizeY&amp;quot;] = 9, size in tiles&lt;br /&gt;
--- 		[&amp;quot;requiredGoods&amp;quot;] = {&lt;br /&gt;
--- 			[1] = { [&amp;quot;stackSize&amp;quot;] = 99, [&amp;quot;goodID&amp;quot;] = &amp;quot;good1_ID&amp;quot; },&lt;br /&gt;
--- 			[2] = { [&amp;quot;stackSize&amp;quot;] = 99, [&amp;quot;goodID&amp;quot;] = &amp;quot;good2_ID&amp;quot; },&lt;br /&gt;
--- 			[3] = { ... } or missing if fewer&lt;br /&gt;
--- 		},&lt;br /&gt;
--- 		[&amp;quot;constructionTime&amp;quot;] = 99, number of seconds&lt;br /&gt;
--- 		[&amp;quot;cityScore&amp;quot;] = 99, points?&lt;br /&gt;
--- 		[&amp;quot;movable&amp;quot;] = true or false, if it can be moved at any cost&lt;br /&gt;
--- 		[&amp;quot;initiallyEssential&amp;quot;] = true or false, if a new-game-starting blueprint&lt;br /&gt;
--- 		[&amp;quot;area&amp;quot;] = 99, tile radius&lt;br /&gt;
--- 		[&amp;quot;storage&amp;quot;] = 99, capacity&lt;br /&gt;
--- 		[&amp;quot;workplaces&amp;quot;] = {&lt;br /&gt;
--- 			[1] = &amp;quot;Any&amp;quot;,&lt;br /&gt;
--- 			[2] = &amp;quot;Any&amp;quot;,&lt;br /&gt;
--- 			[3] = &amp;quot;Any&amp;quot;,&lt;br /&gt;
--- 			[4] = ... representing the number of workplaces, or missing if fewer&lt;br /&gt;
--- 		},&lt;br /&gt;
--- 		[&amp;quot;recipes&amp;quot;] = {&lt;br /&gt;
--- 			[&amp;quot;good1_ID&amp;quot;] = {&lt;br /&gt;
--- 				&amp;quot;goodID&amp;quot; = &amp;quot;good1_ID&amp;quot;,&lt;br /&gt;
--- 				&amp;quot;stackSize&amp;quot; = 99,&lt;br /&gt;
--- 				&amp;quot;grade&amp;quot; = 9, number of stars,&lt;br /&gt;
--- 				&amp;quot;plantingTime&amp;quot; = 99, seconds,&lt;br /&gt;
--- 				&amp;quot;harvestTime&amp;quot; = 99, seconds&lt;br /&gt;
--- 			}&lt;br /&gt;
--- 			[&amp;quot;good2_ID&amp;quot;] = { ... }&lt;br /&gt;
--- 			[&amp;quot;good3_ID&amp;quot;] = { ... }&lt;br /&gt;
--- 			[&amp;quot;good4_ID&amp;quot;] = ... or missing if fewer&lt;br /&gt;
--- 		}&lt;br /&gt;
--- 	},&lt;br /&gt;
--- 	[&amp;quot;farm2_ID&amp;quot;] = {&lt;br /&gt;
--- 		...&lt;br /&gt;
--- 	},&lt;br /&gt;
--- 	[&amp;quot;farm3_ID&amp;quot;] = {&lt;br /&gt;
--- 		...&lt;br /&gt;
--- 	},&lt;br /&gt;
--- 	...&lt;br /&gt;
--- }&lt;br /&gt;
---&lt;br /&gt;
--- @module FarmsData&lt;br /&gt;
local FarmsData = {}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Dependencies&lt;br /&gt;
&lt;br /&gt;
local CsvUtils = require(&amp;quot;Module:CsvUtils&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- Some dependencies are loaded lazily&lt;br /&gt;
local GoodsData&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Private member variables&lt;br /&gt;
&lt;br /&gt;
--- Main data tables, like this: table[ID] = table containing data for that ID&lt;br /&gt;
local farmsTable&lt;br /&gt;
&lt;br /&gt;
--- Supporting table, list of names table[i] = name.&lt;br /&gt;
local farmsNames&lt;br /&gt;
&lt;br /&gt;
--- Lookup map. Built once and reused on subsequent calls within this session,&lt;br /&gt;
--- like this: table[display name] = farmID&lt;br /&gt;
local mapNamesToIDs&lt;br /&gt;
--- Lookup map. Built once and reused on subsequent calls within this session,&lt;br /&gt;
--- like this: table[goodID] = { farmName1, farmName2, ... }&lt;br /&gt;
local mapRecipeGoodIDsToFarmNames&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Private constants&lt;br /&gt;
&lt;br /&gt;
local DATA_TEMPLATE_NAME = &amp;quot;Template:Farms_csv&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local INDEX_ID = &amp;quot;id&amp;quot;&lt;br /&gt;
local INDEX_NAME = &amp;quot;displayName&amp;quot;&lt;br /&gt;
local INDEX_DESCRIPTION = &amp;quot;description&amp;quot;&lt;br /&gt;
local INDEX_CATEGORY = &amp;quot;category&amp;quot;&lt;br /&gt;
local INDEX_SIZE_X = &amp;quot;sizeX&amp;quot;&lt;br /&gt;
local INDEX_SIZE_Y = &amp;quot;sizeY&amp;quot;&lt;br /&gt;
local INDEX_CITY_SCORE = &amp;quot;cityScore&amp;quot;&lt;br /&gt;
local INDEX_MOVABLE = &amp;quot;movable&amp;quot;&lt;br /&gt;
local INDEX_INITIALLY_ESSENTIAL = &amp;quot;initiallyEssential&amp;quot;&lt;br /&gt;
local INDEX_AREA = &amp;quot;area&amp;quot;&lt;br /&gt;
local INDEX_STORAGE = &amp;quot;storage&amp;quot;&lt;br /&gt;
local INDEX_CONSTRUCTION_TIME = &amp;quot;constructionTime&amp;quot;&lt;br /&gt;
local INDEX_REQUIRED_GOODS = &amp;quot;requiredGoods&amp;quot; -- table&lt;br /&gt;
local INDEX_WORKPLACES = &amp;quot;workplaces&amp;quot; -- table&lt;br /&gt;
local INDEX_RECIPES = &amp;quot;recipes&amp;quot; -- table&lt;br /&gt;
&lt;br /&gt;
local INDEX_CONSTRUCTION_GOODS_STACK_SIZE = &amp;quot;stackSize&amp;quot;&lt;br /&gt;
local INDEX_CONSTRUCTION_GOODS_GOOD_ID = &amp;quot;goodID&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local INDEX_RECIPE_STACK_SIZE = &amp;quot;stackSize&amp;quot;&lt;br /&gt;
local INDEX_RECIPE_GOOD_ID = &amp;quot;goodID&amp;quot;&lt;br /&gt;
local INDEX_RECIPE_GRADE = &amp;quot;grade&amp;quot;&lt;br /&gt;
local INDEX_RECIPE_PLANTING_TIME = &amp;quot;plantingTime&amp;quot;&lt;br /&gt;
local INDEX_RECIPE_HARVESTING_TIME = &amp;quot;harvestingTime&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local PATTERN_SPLIT_STACK_AND_ID = &amp;quot;(%d+)%s([%[%]%s%a]+)&amp;quot;&lt;br /&gt;
local PATTERN_CAPTURE_END_NUMBER = &amp;quot;Grade(%d)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Private methods&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Creates a new subtable containing the construction goods required for the&lt;br /&gt;
--- specified farm.&lt;br /&gt;
---&lt;br /&gt;
--- @param originalFarm table farm data record from which to make subtable&lt;br /&gt;
--- @param farmsHeaderLookup table header lookup built from the CSV data&lt;br /&gt;
--- @return table subtable with the required construction goods&lt;br /&gt;
local function makeRequiredGoodsSubtable(originalFarm, farmsHeaderLookup)&lt;br /&gt;
&lt;br /&gt;
	-- A constant we&#039;ll need only within this function.&lt;br /&gt;
	local REQ_GOOD_HEADER_BASE_STRING = &amp;quot;requiredGood&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	-- Copy the originals directly into a subtable.&lt;br /&gt;
	local requiredIndex1 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. &amp;quot;1&amp;quot;]&lt;br /&gt;
	local requiredIndex2 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. &amp;quot;2&amp;quot;]&lt;br /&gt;
	local requiredIndex3 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. &amp;quot;3&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
	local number1, id1 = originalFarm[requiredIndex1]:match(PATTERN_SPLIT_STACK_AND_ID)&lt;br /&gt;
	local number2, id2 = originalFarm[requiredIndex2]:match(PATTERN_SPLIT_STACK_AND_ID)&lt;br /&gt;
	local number3, id3 = originalFarm[requiredIndex3]:match(PATTERN_SPLIT_STACK_AND_ID)&lt;br /&gt;
&lt;br /&gt;
	-- don&#039;t add subtables that would just contain nils&lt;br /&gt;
	local requiredGoods = {&lt;br /&gt;
		number1 and id1 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number1), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id1 } or nil,&lt;br /&gt;
		number2 and id2 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number2), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id2 } or nil,&lt;br /&gt;
		number3 and id3 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number3), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id3 } or nil&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return requiredGoods&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Creates a new subtable containing the workplaces available in the specified&lt;br /&gt;
--- farm.&lt;br /&gt;
---&lt;br /&gt;
--- @param originalFarm table farm data record from which to make subtable&lt;br /&gt;
--- @param farmsHeaderLookup table header lookup built from the CSV data&lt;br /&gt;
--- @return table subtable with the workplaces&lt;br /&gt;
local function makeWorkplacesSubtable(originalFarm, farmsHeaderLookup)&lt;br /&gt;
&lt;br /&gt;
	-- A constant we&#039;ll need only within this function.&lt;br /&gt;
	local WORKPLACE_HEADER_BASE_STRING = &amp;quot;workplace&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	-- Copy the originals directly into a subtable.&lt;br /&gt;
	local workplaceIndex1 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. &amp;quot;1&amp;quot;]&lt;br /&gt;
	local workplaceIndex2 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. &amp;quot;2&amp;quot;]&lt;br /&gt;
	local workplaceIndex3 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. &amp;quot;3&amp;quot;]&lt;br /&gt;
	local workplaceIndex4 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. &amp;quot;4&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
	local workplace1 = originalFarm[workplaceIndex1]&lt;br /&gt;
	local workplace2 = originalFarm[workplaceIndex2]&lt;br /&gt;
	local workplace3 = originalFarm[workplaceIndex3]&lt;br /&gt;
	local workplace4 = originalFarm[workplaceIndex4]&lt;br /&gt;
&lt;br /&gt;
	-- if it&#039;s not an empty string, then save that to the table, otherwise nil&lt;br /&gt;
	local workplaces = {&lt;br /&gt;
		(workplace1 ~= &amp;quot;&amp;quot; and workplace1) or nil,&lt;br /&gt;
		(workplace2 ~= &amp;quot;&amp;quot; and workplace2) or nil,&lt;br /&gt;
		(workplace3 ~= &amp;quot;&amp;quot; and workplace3) or nil,&lt;br /&gt;
		(workplace4 ~= &amp;quot;&amp;quot; and workplace4) or nil&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return workplaces&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Creates a new subtable containing the recipes available in the specified&lt;br /&gt;
--- farm.&lt;br /&gt;
---&lt;br /&gt;
--- @param farmName string the display name of the farm&lt;br /&gt;
--- @param originalFarm table farm data record from which to make subtable&lt;br /&gt;
--- @param farmsHeaderLookup table header lookup built from the CSV data&lt;br /&gt;
--- @return table subtable with the recipe IDs&lt;br /&gt;
local function makeRecipesSubtable(farmName, originalFarm, farmsHeaderLookup)&lt;br /&gt;
&lt;br /&gt;
	-- A constant we&#039;ll need only within this function.&lt;br /&gt;
	local MAX_RECIPES = 4&lt;br /&gt;
	local LOOKUP_RECIPE_BASE_STRING = &amp;quot;recipe&amp;quot;&lt;br /&gt;
	local LOOKUP_GOOD_BASE_STRING = &amp;quot;Good&amp;quot;&lt;br /&gt;
	local LOOKUP_GRADE_BASE_STRING = &amp;quot;Grade&amp;quot;&lt;br /&gt;
	local LOOKUP_PLANTING_TIME_BASE_STRING = &amp;quot;PlantingTime&amp;quot;&lt;br /&gt;
	local LOOKUP_HARVESTING_TIME_BASE_STRING = &amp;quot;HarvestTime&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if not mapRecipeGoodIDsToFarmNames then&lt;br /&gt;
		mapRecipeGoodIDsToFarmNames = {}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Copy the originals directly into a subtable.&lt;br /&gt;
	local recipes = {}&lt;br /&gt;
	for i = 1, MAX_RECIPES do&lt;br /&gt;
&lt;br /&gt;
		-- A subtable for this recipe&lt;br /&gt;
		newRecipe = {}&lt;br /&gt;
		local originalGoodIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i&lt;br /&gt;
				.. LOOKUP_GOOD_BASE_STRING]&lt;br /&gt;
		local originalGradeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i&lt;br /&gt;
				.. LOOKUP_GRADE_BASE_STRING]&lt;br /&gt;
		local originalPlantingTimeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i&lt;br /&gt;
				.. LOOKUP_PLANTING_TIME_BASE_STRING]&lt;br /&gt;
		local originalHarvestingTimeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i&lt;br /&gt;
				.. LOOKUP_HARVESTING_TIME_BASE_STRING]&lt;br /&gt;
&lt;br /&gt;
		local originalStackSize, originalGoodID = originalFarm[originalGoodIndex]:match(PATTERN_SPLIT_STACK_AND_ID)&lt;br /&gt;
&lt;br /&gt;
		-- Skip blank recipes.&lt;br /&gt;
		if originalGoodID and originalGoodID ~= &amp;quot;&amp;quot; then&lt;br /&gt;
			newRecipe[INDEX_RECIPE_GOOD_ID] = originalGoodID&lt;br /&gt;
			newRecipe[INDEX_RECIPE_STACK_SIZE] = tonumber(originalStackSize)&lt;br /&gt;
			newRecipe[INDEX_RECIPE_GRADE] = tonumber(originalFarm[originalGradeIndex]:match(PATTERN_CAPTURE_END_NUMBER))&lt;br /&gt;
			newRecipe[INDEX_RECIPE_PLANTING_TIME] = tonumber(originalFarm[originalPlantingTimeIndex])&lt;br /&gt;
			newRecipe[INDEX_RECIPE_HARVESTING_TIME] = tonumber(originalFarm[originalHarvestingTimeIndex])&lt;br /&gt;
&lt;br /&gt;
			table.insert(recipes,newRecipe)&lt;br /&gt;
&lt;br /&gt;
			if not mapRecipeGoodIDsToFarmNames[originalGoodID] then&lt;br /&gt;
				mapRecipeGoodIDsToFarmNames[originalGoodID] = {}&lt;br /&gt;
			end&lt;br /&gt;
			table.insert(mapRecipeGoodIDsToFarmNames[originalGoodID], farmName)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return recipes&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Transforms the originalFarmsTable returned from CSV processing to be&lt;br /&gt;
--- more conducive to member functions looking up data. Converts text strings&lt;br /&gt;
--- into subtables. Converts header row into field keys on every record and&lt;br /&gt;
--- stores records by id rather than arbitrary integer.&lt;br /&gt;
---&lt;br /&gt;
--- @param originalFarmsTable table of CSV-based data, with header row, data rows&lt;br /&gt;
--- @param farmsHeaderLookup table lookup table of headers to get indexes&lt;br /&gt;
--- @return table better structured with IDs as keys&lt;br /&gt;
local function restructureFarmsTable(originalFarmsTable, farmsHeaderLookup)&lt;br /&gt;
&lt;br /&gt;
	-- A few constants we need only in this function.&lt;br /&gt;
	local DATA_ROWS = 2&lt;br /&gt;
	local INDEX_ORIGINAL_ID = 1&lt;br /&gt;
	local INDEX_ORIGINAL_NAME = 2&lt;br /&gt;
	local INDEX_ORIGINAL_DESCRIPTION = 3&lt;br /&gt;
	local INDEX_ORIGINAL_CATEGORY = 4&lt;br /&gt;
	local INDEX_ORIGINAL_SIZE_X = 5&lt;br /&gt;
	local INDEX_ORIGINAL_SIZE_Y = 6&lt;br /&gt;
	-- Required goods are indexes 7, 8, 9&lt;br /&gt;
	local INDEX_ORIGINAL_CONSTRUCTION_TIME = 10&lt;br /&gt;
	local INDEX_ORIGINAL_CITY_SCORE = 11&lt;br /&gt;
	local INDEX_ORIGINAL_MOVABLE = 12&lt;br /&gt;
	local INDEX_ORIGINAL_ESSENTIAL = 13&lt;br /&gt;
	local INDEX_ORIGINAL_AREA = 14&lt;br /&gt;
	local INDEX_ORIGINAL_STORAGE = 15&lt;br /&gt;
&lt;br /&gt;
	mapNamesToIDs = {}&lt;br /&gt;
	farmsNames = {}&lt;br /&gt;
&lt;br /&gt;
	local newFarmsTable = {}&lt;br /&gt;
	for _, originalFarm in ipairs(originalFarmsTable[DATA_ROWS]) do&lt;br /&gt;
&lt;br /&gt;
		-- Copy over the content, mapping unhelpful indexes into headers keys.&lt;br /&gt;
		local newFarm = {}&lt;br /&gt;
		newFarm[INDEX_ID] = originalFarm[INDEX_ORIGINAL_ID]&lt;br /&gt;
		newFarm[INDEX_NAME] = originalFarm[INDEX_ORIGINAL_NAME]&lt;br /&gt;
		newFarm[INDEX_DESCRIPTION] = originalFarm[INDEX_ORIGINAL_DESCRIPTION]&lt;br /&gt;
		newFarm[INDEX_CATEGORY] = originalFarm[INDEX_ORIGINAL_CATEGORY]&lt;br /&gt;
		newFarm[INDEX_SIZE_X] = tonumber(originalFarm[INDEX_ORIGINAL_SIZE_X])&lt;br /&gt;
		newFarm[INDEX_SIZE_Y] = tonumber(originalFarm[INDEX_ORIGINAL_SIZE_Y])&lt;br /&gt;
		newFarm[INDEX_CITY_SCORE] = tonumber(originalFarm[INDEX_ORIGINAL_CITY_SCORE])&lt;br /&gt;
		newFarm[INDEX_MOVABLE] = originalFarm[INDEX_ORIGINAL_MOVABLE] == &amp;quot;True&amp;quot;&lt;br /&gt;
		newFarm[INDEX_INITIALLY_ESSENTIAL] = originalFarm[INDEX_ORIGINAL_ESSENTIAL] == &amp;quot;True&amp;quot;&lt;br /&gt;
		newFarm[INDEX_AREA] = tonumber(originalFarm[INDEX_ORIGINAL_AREA])&lt;br /&gt;
		newFarm[INDEX_STORAGE] = tonumber(originalFarm[INDEX_ORIGINAL_STORAGE])&lt;br /&gt;
		newFarm[INDEX_CONSTRUCTION_TIME] = tonumber(originalFarm[INDEX_ORIGINAL_CONSTRUCTION_TIME])&lt;br /&gt;
&lt;br /&gt;
		newFarm[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalFarm, farmsHeaderLookup)&lt;br /&gt;
		newFarm[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalFarm, farmsHeaderLookup)&lt;br /&gt;
		newFarm[INDEX_RECIPES] = makeRecipesSubtable(newFarm[INDEX_NAME], originalFarm, farmsHeaderLookup)&lt;br /&gt;
&lt;br /&gt;
		newFarmsTable[ newFarm[INDEX_ID] ] = newFarm&lt;br /&gt;
&lt;br /&gt;
		table.insert(farmsNames, newFarm[INDEX_NAME])&lt;br /&gt;
&lt;br /&gt;
		-- Also populate the map for looking up IDs with display names&lt;br /&gt;
		mapNamesToIDs[ newFarm[INDEX_NAME] ] = newFarm[INDEX_ID]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return newFarmsTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Data loader function that uses the utility module and restructures the data&lt;br /&gt;
--- to be easier to access for invoking methods and later method calls. This&lt;br /&gt;
--- method is automatically called by all public member functions if the main&lt;br /&gt;
--- data table has not yet been populated in the current session.&lt;br /&gt;
local function loadData()&lt;br /&gt;
&lt;br /&gt;
	-- Utility module retrieves the data as basic, flat lua tables.&lt;br /&gt;
	local originalFarmsTable, farmsHeaderLookup = CsvUtils.extractTables(DATA_TEMPLATE_NAME)&lt;br /&gt;
&lt;br /&gt;
	-- Now restructure to be more conducive.&lt;br /&gt;
	farmsTable = restructureFarmsTable(originalFarmsTable, farmsHeaderLookup)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function load()&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Uses the display name, which people are more familiar with, to find the&lt;br /&gt;
--- encoded ID of the farm. Useful for retrieving the  data that is&lt;br /&gt;
--- indexed by ID.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm with the specified name is not found.&lt;br /&gt;
---&lt;br /&gt;
--- @param displayName string plain-language name of the farm to find&lt;br /&gt;
--- @return string ID of the farm found, or nil if not found&lt;br /&gt;
local function findFarmIDByName(displayName)&lt;br /&gt;
&lt;br /&gt;
	-- At runtime, this should never be nil or empty, so throw an error.&lt;br /&gt;
	if not displayName or displayName == &amp;quot;&amp;quot; then&lt;br /&gt;
		error(&amp;quot;Parameter is nil or empty for the farm&#039;s name: &amp;quot; .. displayName .. &amp;quot;.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return mapNamesToIDs[displayName]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Public methods&lt;br /&gt;
&lt;br /&gt;
--- Retrieve the whole table of data for the specified farm. Instead of&lt;br /&gt;
--- this, you should probably be calling the individual getter methods.&lt;br /&gt;
---&lt;br /&gt;
--- Throws an error if called with nil or empty string. Returns nil if the&lt;br /&gt;
--- specified farm cannot be found.&lt;br /&gt;
---&lt;br /&gt;
--- @param farmID string ID of the farm&lt;br /&gt;
--- @return table containing the data with key-value pairs, or nil if not found&lt;br /&gt;
function FarmsData.getAllDataForFarmByID(farmID)&lt;br /&gt;
&lt;br /&gt;
	-- At runtime, this should never be nil or empty.&lt;br /&gt;
	if not farmID or farmID == &amp;quot;&amp;quot; then&lt;br /&gt;
		error(&amp;quot;Parameter is nil or empty for the farm&#039;s ID.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farmsTable[farmID]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Retrieve the whole table of data for the specified farm. Instead of&lt;br /&gt;
--- this, you should probably be calling the individual getter methods.&lt;br /&gt;
---&lt;br /&gt;
--- Throws an error if called with nil or empty string. Returns nil if the&lt;br /&gt;
--- specified farm cannot be found.&lt;br /&gt;
---&lt;br /&gt;
--- @param displayName string plain language name of the farm&lt;br /&gt;
--- @return table containing the data with key-value pairs, or nil if not found&lt;br /&gt;
function FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	-- At runtime, this should never be nil or empty.&lt;br /&gt;
	if not displayName or displayName == &amp;quot;&amp;quot; then&lt;br /&gt;
		error(&amp;quot;Parameter is nil or empty for the farm&#039;s name.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local farmID = findFarmIDByName(displayName)&lt;br /&gt;
	if not farmID then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return farmsTable[farmID]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the ID for the farm specified by its plain language&lt;br /&gt;
--- display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return string the ID of the specified farm&lt;br /&gt;
function FarmsData.getFarmID(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_ID]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the description for the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return string the in-game description of the specified farm&lt;br /&gt;
function FarmsData.getFarmDescription(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_DESCRIPTION]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the construction toolbar category for the farm specified&lt;br /&gt;
--- by its plain language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return string the category of the specified farm&lt;br /&gt;
function FarmsData.getFarmCategory(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_CATEGORY]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the 2x2 size for the farm specified by its plain language&lt;br /&gt;
--- display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number the X-size of the farm&lt;br /&gt;
---@return number the Y-size of the farm&lt;br /&gt;
function FarmsData.getFarmSize(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_SIZE_X], farm[INDEX_SIZE_Y]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the goods required for construction for the farm specified&lt;br /&gt;
--- by its plain language display name, in a table that looks like this:&lt;br /&gt;
---&lt;br /&gt;
--- [&amp;quot;requiredGoods&amp;quot;] = {&lt;br /&gt;
---    [1] = { [&amp;quot;stackSize&amp;quot;] = 99, [&amp;quot;goodID&amp;quot;] = &amp;quot;good1_ID&amp;quot; },&lt;br /&gt;
---    [2] = { [&amp;quot;stackSize&amp;quot;] = 99, [&amp;quot;goodID&amp;quot;] = &amp;quot;good2_ID&amp;quot; },&lt;br /&gt;
---    [3] = { ... } or missing if fewer&lt;br /&gt;
--- }&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return table of required goods&lt;br /&gt;
function FarmsData.getAllFarmRequiredGoods(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_REQUIRED_GOODS]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the specified required construction good for the farm&lt;br /&gt;
--- specified by its plain language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@param requirementIndex number which construction good to retrieve&lt;br /&gt;
---@return string the ID of the good that is required, or nil if none&lt;br /&gt;
---@return number the stack size of that good, or nil if none&lt;br /&gt;
function FarmsData.getFarmRequiredGood(displayName, requirementIndex)&lt;br /&gt;
&lt;br /&gt;
	local requiredGoods = FarmsData.getAllFarmRequiredGoods(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not requiredGoods then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local requirement = requiredGoods[requirementIndex]&lt;br /&gt;
	if not requirement then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return requirement[INDEX_CONSTRUCTION_GOODS_GOOD_ID], requirement[INDEX_CONSTRUCTION_GOODS_STACK_SIZE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the construction time for the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of seconds it takes to construct the farm&lt;br /&gt;
function FarmsData.getFarmConstructionTime(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_CONSTRUCTION_TIME]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the city score awarded for the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of points for city score from having the farm&lt;br /&gt;
function FarmsData.getFarmCityScore(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_CITY_SCORE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves whether the farm specified by its plain language display&lt;br /&gt;
--- name can be moved, at any cost.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return boolean of whether the farm can be moved&lt;br /&gt;
function FarmsData.isFarmMovable(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_MOVABLE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves whether the farm specified by its plain language display&lt;br /&gt;
--- name has an essential starting blueprint for a new game profile.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return boolean of whether the farm&#039;s blueprint is essential&lt;br /&gt;
function FarmsData.isFarmInitiallyEssential(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_INITIALLY_ESSENTIAL]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the gathering area for the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of tiles of gathering radius&lt;br /&gt;
function FarmsData.getFarmArea(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_AREA]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the storage capacity of the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of storage capacity at the farm&lt;br /&gt;
function FarmsData.getFarmStorage(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_STORAGE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the table of workplaces for the farm specified by its&lt;br /&gt;
--- plain language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return table of workplaces&lt;br /&gt;
function FarmsData.getAllFarmWorkplaces(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_WORKPLACES]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the number of workplaces for the farm specified by its&lt;br /&gt;
--- plain language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of workplaces&lt;br /&gt;
function FarmsData.getFarmNumberOfWorkplaces(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return #farm[INDEX_WORKPLACES]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the table of recipes for the farm specified by its&lt;br /&gt;
--- plain language display name, in a table that looks like this:&lt;br /&gt;
---&lt;br /&gt;
--- [&amp;quot;recipes&amp;quot;] = {&lt;br /&gt;
--- 	[1] = &amp;quot;recipe1_ID&amp;quot;,&lt;br /&gt;
--- 	[2] = &amp;quot;recipe2_ID&amp;quot;,&lt;br /&gt;
--- 	[3] = &amp;quot;recipe3_ID&amp;quot;,&lt;br /&gt;
--- 	[4] = ... or missing if fewer&lt;br /&gt;
--- }&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return table of recipe IDs&lt;br /&gt;
function FarmsData.getAllFarmRecipes(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_RECIPES]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the number of recipes for the farm specified by its&lt;br /&gt;
--- plain language display name, in a table that looks like this:&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return number of recipe at the farm&lt;br /&gt;
function FarmsData.getFarmNumberOfRecipes(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return 0&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	recipes = farm[INDEX_RECIPES]&lt;br /&gt;
	if not recipes then&lt;br /&gt;
		return 0&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return #recipes&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the product&#039;s ID and the stack size produced by the specified&lt;br /&gt;
--- recipe at the farm specified by its plain language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@param recipeIndex number the index of the recipe ID&lt;br /&gt;
---@return string the ID of the good produced by the specified recipe&lt;br /&gt;
---@return number the stack size produced by the specified recipe&lt;br /&gt;
function FarmsData.getFarmRecipeProduct(displayName, recipeIndex)&lt;br /&gt;
&lt;br /&gt;
	local recipes = FarmsData.getAllFarmRecipes(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not recipes then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local recipe = recipes[recipeIndex]&lt;br /&gt;
	if not recipe then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return recipe[INDEX_RECIPE_GOOD_ID], recipe[INDEX_RECIPE_STACK_SIZE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the other stats for specified recipe at the farm specified by&lt;br /&gt;
--- its plain language display name: the efficiency grade and the planting and&lt;br /&gt;
--- harvesting times.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@param recipeIndex number the index of the recipe ID&lt;br /&gt;
---@return number of stars, the grade of the specified recipe&lt;br /&gt;
---@return number of seconds for planting&lt;br /&gt;
---@return number of seconds for harvesting&lt;br /&gt;
function FarmsData.getFarmRecipeStats(displayName, recipeIndex)&lt;br /&gt;
&lt;br /&gt;
	local recipes = FarmsData.getAllFarmRecipes(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not recipes then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local recipe = recipes[recipeIndex]&lt;br /&gt;
	if not recipe then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return recipe[INDEX_RECIPE_GRADE], recipe[INDEX_RECIPE_PLANTING_TIME], recipe[INDEX_RECIPE_HARVESTING_TIME]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the icon filename for the farm specified by its plain&lt;br /&gt;
--- language display name.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param displayName string the plain language name of the farm&lt;br /&gt;
---@return string the farm&#039;s icon filename, including the extension&lt;br /&gt;
function FarmsData.getFarmIcon(displayName)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarm(displayName)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- the base string of the icon is the ID. It has to be not nil to&lt;br /&gt;
	-- concatenate&lt;br /&gt;
	if farm[INDEX_ID] then&lt;br /&gt;
		return farm[INDEX_ID] .. &amp;quot;_icon.png&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the plain language display name for the farm specified by&lt;br /&gt;
--- its ID.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param farmID string the ID of the farm&lt;br /&gt;
---@return string the farm&#039;s name as seen in-game&lt;br /&gt;
function FarmsData.getFarmNameByID(farmID)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarmByID(farmID)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_NAME]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the icon filename for the the farm specified by its ID.&lt;br /&gt;
---&lt;br /&gt;
--- Returns nil if the farm named was not found.&lt;br /&gt;
---&lt;br /&gt;
---@param farmID string the ID of the farm&lt;br /&gt;
---@return string the farm&#039;s icon filename, including the extension&lt;br /&gt;
function FarmsData.getFarmIconByID(farmID)&lt;br /&gt;
&lt;br /&gt;
	local farm = FarmsData.getAllDataForFarmByID(farmID)&lt;br /&gt;
&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- the base string of the icon is the ID. It has to be not nil to&lt;br /&gt;
	-- concatenate&lt;br /&gt;
	if farm[INDEX_ID] then&lt;br /&gt;
		return farm[INDEX_ID] .. &amp;quot;_icon.png&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieve all display names of farms that have a recipe that produces the&lt;br /&gt;
--- specified product ID.&lt;br /&gt;
---&lt;br /&gt;
---@param productID string the ID of the good to produce&lt;br /&gt;
---@return table of farm names that produce the specified good&lt;br /&gt;
function FarmsData.getFarmNamesWithRecipeProductID(productID)&lt;br /&gt;
&lt;br /&gt;
	-- At runtime, this should never be nil or empty.&lt;br /&gt;
	if not productID or productID == &amp;quot;&amp;quot; then&lt;br /&gt;
		error(&amp;quot;Parameter is nil or empty for product ID.&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return mapRecipeGoodIDsToFarmNames[productID]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Retrieves the name of the farm at the specified index.&lt;br /&gt;
---&lt;br /&gt;
---@param index number index&lt;br /&gt;
---@return string name of the building at the specified index&lt;br /&gt;
function FarmsData.getFarmNameFromDatabase(index)&lt;br /&gt;
&lt;br /&gt;
	if not index then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farmsNames[index]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
--- Returns the number of buildings that can be retrieved from this&lt;br /&gt;
--- module&#039;s database.&lt;br /&gt;
---&lt;br /&gt;
---@return number of records&lt;br /&gt;
function FarmsData.getNumberOfFarmsInDatabase()&lt;br /&gt;
&lt;br /&gt;
	if not farmsTable then&lt;br /&gt;
		loadData()&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return #farmsNames&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--region Public Building interface&lt;br /&gt;
-- getCategory&lt;br /&gt;
-- getCityScore&lt;br /&gt;
-- getConstructionCosts (as [goodName] = stack size)&lt;br /&gt;
-- getConstructionTime&lt;br /&gt;
-- getDescription&lt;br /&gt;
-- getIcon&lt;br /&gt;
-- isMovable&lt;br /&gt;
-- getName&lt;br /&gt;
-- getNumberOfWorkplaces&lt;br /&gt;
-- getSize (as &amp;quot;X x Y&amp;quot;)&lt;br /&gt;
-- getStorage&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getCategory(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_CATEGORY]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getCityScore(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_CITY_SCORE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getConstructionCosts(id)&lt;br /&gt;
&lt;br /&gt;
	load()&lt;br /&gt;
	GoodsData = require(&amp;quot;Module:GoodsData&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	farm = farmsTable[id]&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local constructionCosts = {}&lt;br /&gt;
	for _, stacks in ipairs(farm[INDEX_REQUIRED_GOODS]) do&lt;br /&gt;
		local goodName = GoodsData.getGoodNameByID(stacks[INDEX_CONSTRUCTION_GOODS_GOOD_ID])&lt;br /&gt;
		constructionCosts[goodName] = stacks[INDEX_CONSTRUCTION_GOODS_STACK_SIZE]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return constructionCosts&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getConstructionTime(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_CONSTRUCTION_TIME]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getDescription(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_DESCRIPTION]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getIcon(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and id .. &amp;quot;_icon.png&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.isMovable(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_MOVABLE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getName(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_NAME]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getNumberOfWorkplaces(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and #farmsTable[id][INDEX_WORKPLACES]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getSize(id)&lt;br /&gt;
	load()&lt;br /&gt;
&lt;br /&gt;
	farm = farmsTable[id]&lt;br /&gt;
	if not farm then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return farm[INDEX_SIZE_X] .. &amp;quot; × &amp;quot; .. farm[INDEX_SIZE_Y]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FarmsData.getStorage(id)&lt;br /&gt;
	load()&lt;br /&gt;
	return farmsTable[id] ~= nil and farmsTable[id][INDEX_STORAGE]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return FarmsData&lt;/div&gt;</summary>
		<author><name>76561198045847734</name></author>
	</entry>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Template:Farms_csv&amp;diff=8573</id>
		<title>Template:Farms csv</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Template:Farms_csv&amp;diff=8573"/>
		<updated>2024-10-09T09:27:16Z</updated>

		<summary type="html">&lt;p&gt;76561198045847734: Add new columns for fourth recipe&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{Version|1.4.15}}&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
This template stores the CSV data for farming buildings as provided by the game developers.&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
&lt;br /&gt;
{{LuaCodeToAccessCSVDataTemplates|{{FULLPAGENAME}}}}&lt;br /&gt;
&lt;br /&gt;
==Data==&lt;br /&gt;
&lt;br /&gt;
What follows on the rest of this wiki page is the only content that gets transcluded, the raw data enclosed in a &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
id,displayName,description,category,sizeX,sizeY,requiredGood1,requiredGood2,requiredGood3,constructionTime,cityScore,movable,initiallyEssential,area,storage,workplace1,workplace2,workplace3,workplace4,recipe1Good,recipe1Grade,recipe1PlantingTime,recipe1HarvestTime,recipe2Good,recipe2Grade,recipe2PlantingTime,recipe2HarvestTime,recipe3Good,recipe3Grade,recipe3PlantingTime,recipe3HarvestTime,recipe4Good,recipe4Grade,recipe4PlantingTime,recipe4HarvestTime,&lt;br /&gt;
Grove,Forester&#039;s Hut,&amp;quot;Uses nearby farm fields to grow  Resin, Crystalized Dew.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,Any,Any,6 [Mat Raw] Resin,Grade2,30,30,3 [Metal] Crystalized Dew,Grade2,60,60,,,,,,,,,&lt;br /&gt;
Hallowed Herb Garden,Hallowed Herb Garden,&amp;quot;Uses nearby farm fields to grow  Roots, Herbs. Has improved efficiency and more worker slots.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,Any,,9 [Food Raw] Roots,Grade3,30,30,9 [Food Raw] Herbs,Grade3,30,30,,,,,,,,,&lt;br /&gt;
Hallowed SmallFarm,Hallowed Small Farm,&amp;quot;Uses nearby farm fields to grow  Vegetables, Grain. Has improved efficiency and more worker slots.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,3,True,False,3,20,Any,Any,Any,,9 [Food Raw] Vegetables,Grade3,30,30,9 [Food Raw] Grain,Grade3,30,30,,,,,,,,,&lt;br /&gt;
Herb Garden,Herb Garden,&amp;quot;Uses nearby farm fields to grow  Roots, Herbs.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,,,3 [Food Raw] Roots,Grade1,30,30,6 [Food Raw] Herbs,Grade2,30,30,,,,,,,,,&lt;br /&gt;
Homestead,Homestead,&amp;quot;Uses a large area of nearby farm fields to grow  Grain, Plant Fiber, Vegetables, Mushrooms.&amp;quot;,Food Production,3,3,5 [Mat Processed] Planks,2 [Mat Processed] Bricks,2 [Mat Processed] Fabric,50,10,True,False,10,40,Any,Any,Any,Any,9 [Food Raw] Grain,Grade3,30,30,9 [Mat Raw] Plant Fibre,Grade3,30,30,6 [Food Raw] Vegetables,Grade2,30,30,6 [Food Raw] Mushrooms,Grade2,30,30,&lt;br /&gt;
Plantation,Plantation,&amp;quot;Uses nearby farm fields to grow  Berries, Plant Fiber.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,,,6 [Food Raw] Berries,Grade2,30,30,6 [Mat Raw] Plant Fibre,Grade2,30,30,,,,,,,,,&lt;br /&gt;
SmallFarm,Small Farm,&amp;quot;Uses nearby farm fields to grow  Vegetables, Grain.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,3,True,False,3,20,Any,Any,,,3 [Food Raw] Vegetables,Grade1,30,30,6 [Food Raw] Grain,Grade2,30,30,,,,,,,,,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>76561198045847734</name></author>
	</entry>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Template:Farms_csv&amp;diff=8572</id>
		<title>Template:Farms csv</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Template:Farms_csv&amp;diff=8572"/>
		<updated>2024-10-09T09:12:08Z</updated>

		<summary type="html">&lt;p&gt;76561198045847734: Update recipe list for homestead to include mushrooms&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{Version|1.4.15}}&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
This template stores the CSV data for farming buildings as provided by the game developers.&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
&lt;br /&gt;
{{LuaCodeToAccessCSVDataTemplates|{{FULLPAGENAME}}}}&lt;br /&gt;
&lt;br /&gt;
==Data==&lt;br /&gt;
&lt;br /&gt;
What follows on the rest of this wiki page is the only content that gets transcluded, the raw data enclosed in a &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; block:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
id,displayName,description,category,sizeX,sizeY,requiredGood1,requiredGood2,requiredGood3,constructionTime,cityScore,movable,initiallyEssential,area,storage,workplace1,workplace2,workplace3,workplace4,recipe1Good,recipe1Grade,recipe1PlantingTime,recipe1HarvestTime,recipe2Good,recipe2Grade,recipe2PlantingTime,recipe2HarvestTime,recipe3Good,recipe3Grade,recipe3PlantingTime,recipe3HarvestTime,&lt;br /&gt;
Grove,Forester&#039;s Hut,&amp;quot;Uses nearby farm fields to grow  Resin, Crystalized Dew.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,Any,Any,6 [Mat Raw] Resin,Grade2,30,30,3 [Metal] Crystalized Dew,Grade2,60,60,,,,,&lt;br /&gt;
Hallowed Herb Garden,Hallowed Herb Garden,&amp;quot;Uses nearby farm fields to grow  Roots, Herbs. Has improved efficiency and more worker slots.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,Any,,9 [Food Raw] Roots,Grade3,30,30,9 [Food Raw] Herbs,Grade3,30,30,,,,,&lt;br /&gt;
Hallowed SmallFarm,Hallowed Small Farm,&amp;quot;Uses nearby farm fields to grow  Vegetables, Grain. Has improved efficiency and more worker slots.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,3,True,False,3,20,Any,Any,Any,,9 [Food Raw] Vegetables,Grade3,30,30,9 [Food Raw] Grain,Grade3,30,30,,,,,&lt;br /&gt;
Herb Garden,Herb Garden,&amp;quot;Uses nearby farm fields to grow  Roots, Herbs.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,,,3 [Food Raw] Roots,Grade1,30,30,6 [Food Raw] Herbs,Grade2,30,30,,,,,&lt;br /&gt;
Homestead,Homestead,&amp;quot;Uses a large area of nearby farm fields to grow  Grain, Plant Fiber, Vegetables, Mushrooms.&amp;quot;,Food Production,3,3,5 [Mat Processed] Planks,2 [Mat Processed] Bricks,2 [Mat Processed] Fabric,50,10,True,False,10,40,Any,Any,Any,Any,9 [Food Raw] Grain,Grade3,30,30,9 [Mat Raw] Plant Fibre,Grade3,30,30,6 [Food Raw] Vegetables,Grade2,30,30,6 [Food Raw] Mushrooms,Grade2,30,30,&lt;br /&gt;
Plantation,Plantation,&amp;quot;Uses nearby farm fields to grow  Berries, Plant Fiber.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,2,True,False,3,20,Any,Any,,,6 [Food Raw] Berries,Grade2,30,30,6 [Mat Raw] Plant Fibre,Grade2,30,30,,,,,&lt;br /&gt;
SmallFarm,Small Farm,&amp;quot;Uses nearby farm fields to grow  Vegetables, Grain.&amp;quot;,Food Production,2,2,2 [Mat Processed] Planks,,,20,3,True,False,3,20,Any,Any,,,3 [Food Raw] Vegetables,Grade1,30,30,6 [Food Raw] Grain,Grade2,30,30,,,,,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>76561198045847734</name></author>
	</entry>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Homestead&amp;diff=8571</id>
		<title>Homestead</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Homestead&amp;diff=8571"/>
		<updated>2024-10-09T09:02:37Z</updated>

		<summary type="html">&lt;p&gt;76561198045847734: Homestead also grows mushrooms according to in-game encyclopedia&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Version|1.4.15}}&lt;br /&gt;
{{Buildingbox|{{PAGENAME}}&lt;br /&gt;
 |specializations={{sl|Farming}}&lt;br /&gt;
 |purpose=Farming&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Homestead&#039;&#039;&#039; is a farm building that uses [[File:Farmfield_icon.png|32px]] [[Farm Fields]] to grow {{rl|Grain}} ({{3Star}}), {{rl|Plant Fiber}} ({{3Star}}), {{rl|Vegetables}} ({{2Star}}) and {{rl|Mushrooms}} ({{2Star}}). It can only be found in Forbidden [[Glades]] or as a blueprint reward from the Strider Port in [[Coastal Grove]]. It has 4 worker slots and provides {{sl|Farming}} proficiency to any {{Species link|Humans}} assigned to it, granting a +10% of double yields upon planting.&lt;br /&gt;
&lt;br /&gt;
== Seasonal Work ==&lt;br /&gt;
As a farm building, the &#039;&#039;&#039;Homestead&#039;&#039;&#039; requires different tasks to be done each [[Season]], these are:&lt;br /&gt;
* Drizzle: Planting takes place, any double yield bonus happens now.&lt;br /&gt;
* Clearance: Harvesting takes place.&lt;br /&gt;
* Storm: Plowing takes place, +10% Double Yield chances are added.&lt;br /&gt;
&lt;br /&gt;
== Production Chain ==&lt;br /&gt;
=== Recipes in the {{PAGENAME}} ===&lt;br /&gt;
{{Recipe|building={{PAGENAME}}}}&lt;br /&gt;
&lt;br /&gt;
== Perks and Cornerstones ==&lt;br /&gt;
&lt;br /&gt;
{{Perks|description=Homestead,fertile soil}}&lt;br /&gt;
&lt;br /&gt;
== Strategies ==&lt;/div&gt;</summary>
		<author><name>76561198045847734</name></author>
	</entry>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Homestead&amp;diff=8570</id>
		<title>Homestead</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Homestead&amp;diff=8570"/>
		<updated>2024-10-09T08:56:52Z</updated>

		<summary type="html">&lt;p&gt;76561198045847734: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Version|1.3.4}}&lt;br /&gt;
{{Buildingbox|{{PAGENAME}}&lt;br /&gt;
 |specializations={{sl|Farming}}&lt;br /&gt;
 |purpose=Farming&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Homestead&#039;&#039;&#039; is a farm building that uses [[File:Farmfield_icon.png|32px]] [[Farm Fields]] to grow {{rl|Grain}} ({{3Star}}), {{rl|Plant Fiber}} ({{3Star}}) and {{rl|Vegetables}} ({{2Star}}). It can only be found in Forbidden [[Glades]] or as a blueprint reward from the Strider Port in [[Coastal Grove]]. It has 4 worker slots and provides {{sl|Farming}} proficiency to any {{Species link|Humans}} assigned to it, granting a +10% of double yields upon planting.&lt;br /&gt;
&lt;br /&gt;
== Seasonal Work ==&lt;br /&gt;
As a farm building, the &#039;&#039;&#039;Homestead&#039;&#039;&#039; requires different tasks to be done each [[Season]], these are:&lt;br /&gt;
* Drizzle: Planting takes place, any double yield bonus happens now.&lt;br /&gt;
* Clearance: Harvesting takes place.&lt;br /&gt;
* Storm: Plowing takes place, +10% Double Yield chances are added.&lt;br /&gt;
&lt;br /&gt;
== Production Chain ==&lt;br /&gt;
=== Recipes in the {{PAGENAME}} ===&lt;br /&gt;
{{Recipe|building={{PAGENAME}}}}&lt;br /&gt;
&lt;br /&gt;
== Perks and Cornerstones ==&lt;br /&gt;
&lt;br /&gt;
{{Perks|description=Homestead,fertile soil}}&lt;br /&gt;
&lt;br /&gt;
== Strategies ==&lt;/div&gt;</summary>
		<author><name>76561198045847734</name></author>
	</entry>
</feed>