Module:FarmsData

--- --- Module for compiling farming buildings information from wiki data sources. --- Restructures the flat data tables that are produced from CsvUtils to make --- them more conducive to Lua methods that need to display the information.

--- The standard way of using this module is a call like the following: --- --- area = FarmsData.getFarmArea(farmName) --- --- This will return the working radius of the farm. It is preferable to --- call the getter methods with the name of the farm rather than --- retrieving the entire data record for the farm. This way your code --- stays protected from variations in this module. These getter methods are --- called with the plain-language display name of the farm, as spelled --- in the game, including punctuation. --- --- * longDescription = FarmsData.getFarmDescription(farmName) --- * constructionCategory = FarmsData.getFarmCategory(farmName) --- * sizeX, sizeY = FarmsData.getFarmSize(farmName) --- * requiredGoodID, stackSize = FarmsData.getFarmRequiredGood(farmName, requirementIndex) --- * timeSeconds = FarmsData.getFarmConstructionTime(farmName) --- * cityScore = FarmsData.getFarmCityScore(farmName) --- * isMovable = FarmsData.isFarmMovable(farmName) --- * isInitiallyEssential = FarmsData.isFarmInitiallyEssential(farmName) --- * radius = FarmsData.getFarmArea(farmName) --- * storageCapacity = FarmsData.getFarmStorage(farmName) --- * workplaces = FarmsData.getFarmNumberOfWorkplaces(farmName) --- * goodID, stackSize = FarmsData.getFarmRecipeProduct(farmName, recipeIndex) --- * gradeStars, plantingSeconds, harvestSeconds = FarmsData.getFarmRecipeStats(farmName, recipeIndex) --- * iconFilename = FarmsData.getFarmIcon(farmName) --- --- And the following getter methods are all called with the farm's ID. --- You will have an ID instead of a display name when dealing with other data --- like recipes, species, etc. --- --- * name = FarmsData.getFarmNameByID(farmID) --- * iconFilename = FarmsData.getFarmIconByID(farmID) --- --- You can retrieve all farms that produce a certain good (with any grade of --- efficiency) with the following getter: --- --- farmNamesTable = FarmsData.getFarmNamesWithRecipeProductID(productID) --- --- There are methods to retrieve the lists of required goods, workplaces, and --- recipes, but it is advised to instead use the getter methods --- getFarmRequiredGood, getFarmNumberOfWorkplaces, and --- getFarmRecipe with an index desired, which is good for loops. If --- you must get the tables, there are three "getAll" methods: --- --- * requiredGoodsTable = FarmsData.getAllFarmRequiredGoods(farmName) --- * workplacesTable = FarmsData.getAllFarmWorkplaces(farmName) --- * recipesTable = FarmsData.getAllFarmRecipes(farmName) --- --- As a last resort, or if you need to transform the data structure, you can --- call the method getAllDataForFarm(farmName) or --- getAllDataForFarmByID(farmID). These return a whole record --- from the data table corresponding to the required display name or ID. --- --- The data table for farms has the following structure: --- --- farmsTable = { --- 	["farm1_ID"] = { --- 		["id"] = "farm1_ID", --- 		["displayName"] = "Plain Language Name", --- 		["description"] = "A long string with some HTML entities too.", --- 		["category"] = "Construction toolbar category", --- 		["sizeX"] = 9, size in tiles --- 		["sizeY"] = 9, size in tiles --- 		["requiredGoods"] = { --- 			[1] = { ["stackSize"] = 99, ["goodID"] = "good1_ID" }, --- 			[2] = { ["stackSize"] = 99, ["goodID"] = "good2_ID" }, --- 			[3] = { ... } or missing if fewer --- 		}, --- 		["constructionTime"] = 99, number of seconds --- 		["cityScore"] = 99, points? --- 		["movable"] = true or false, if it can be moved at any cost --- 		["initiallyEssential"] = true or false, if a new-game-starting blueprint --- 		["area"] = 99, tile radius --- 		["storage"] = 99, capacity --- 		["workplaces"] = { --- 			[1] = "Any", --- 			[2] = "Any", --- 			[3] = "Any", --- 			[4] = ... representing the number of workplaces, or missing if fewer --- 		}, --- 		["recipes"] = { --- 			["good1_ID"] = { --- 				"goodID" = "good1_ID", --- 				"stackSize" = 99, --- 				"grade" = 9, number of stars, --- 				"plantingTime" = 99, seconds, --- 				"harvestTime" = 99, seconds --- 			} --- 			["good2_ID"] = { ... } --- 			["good3_ID"] = { ... } --- 			["good4_ID"] = ... or missing if fewer --- 		} --- 	}, --- 	["farm2_ID"] = { --- 		... --- 	}, --- 	["farm3_ID"] = { --- 		... --- 	}, --- 	... --- } --- --- @module FarmsData local FarmsData = {}

local CsvUtils = require("Module:CsvUtils")

--region Private member variables

--- Main data tables, like this: table[ID] = table containing data for that ID local farmsTable

--- Lookup map. Built once and reused on subsequent calls within this session, --- like this: table[display name] = farmID local mapNamesToIDs --- Lookup map. Built once and reused on subsequent calls within this session, --- like this: table[goodID] = { farmName1, farmName2, ... } local mapRecipeGoodIDsToFarmNames --endregion

--region Private constants

local DATA_TEMPLATE_NAME = "Template:Farms_csv"

local INDEX_ID = "id" local INDEX_NAME = "displayName" local INDEX_DESCRIPTION = "description" local INDEX_CATEGORY = "category" local INDEX_SIZE_X = "sizeX" local INDEX_SIZE_Y = "sizeY" local INDEX_CITY_SCORE = "cityScore" local INDEX_MOVABLE = "movable" local INDEX_INITIALLY_ESSENTIAL = "initiallyEssential" local INDEX_AREA = "area" local INDEX_STORAGE = "storage" local INDEX_CONSTRUCTION_TIME = "constructionTime" local INDEX_REQUIRED_GOODS = "requiredGoods" -- table local INDEX_WORKPLACES = "workplaces" -- table local INDEX_RECIPES = "recipes" -- table

local INDEX_CONSTRUCTION_GOODS_STACK_SIZE = "stackSize" local INDEX_CONSTRUCTION_GOODS_GOOD_ID = "goodID"

local INDEX_RECIPE_STACK_SIZE = "stackSize" local INDEX_RECIPE_GOOD_ID = "goodID" local INDEX_RECIPE_GRADE = "grade" local INDEX_RECIPE_PLANTING_TIME = "plantingTime" local INDEX_RECIPE_HARVESTING_TIME = "harvestingTime"

local PATTERN_SPLIT_STACK_AND_ID = "(%d+)%s([%[%]%s%a]+)" local PATTERN_CAPTURE_END_NUMBER = "Grade(%d)"

--endregion

--region Private methods

--- --- Creates a new subtable containing the construction goods required for the --- specified farm. --- --- @param originalFarm table farm data record from which to make subtable --- @param farmsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the required construction goods local function makeRequiredGoodsSubtable(originalFarm, farmsHeaderLookup)

-- A constant we'll need only within this function. local REQ_GOOD_HEADER_BASE_STRING = "requiredGood"

-- Copy the originals directly into a subtable. local requiredIndex1 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "1"]	local requiredIndex2 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "2"]	local requiredIndex3 = farmsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "3"]

local number1, id1 = originalFarm[requiredIndex1]:match(PATTERN_SPLIT_STACK_AND_ID) local number2, id2 = originalFarm[requiredIndex2]:match(PATTERN_SPLIT_STACK_AND_ID) local number3, id3 = originalFarm[requiredIndex3]:match(PATTERN_SPLIT_STACK_AND_ID)

-- don't add subtables that would just contain nils local requiredGoods = { number1 and id1 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number1), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id1 } or nil, number2 and id2 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number2), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id2 } or nil, number3 and id3 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number3), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id3 } or nil }

return requiredGoods end

--- --- Creates a new subtable containing the workplaces available in the specified --- farm. --- --- @param originalFarm table farm data record from which to make subtable --- @param farmsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the workplaces local function makeWorkplacesSubtable(originalFarm, farmsHeaderLookup)

-- A constant we'll need only within this function. local WORKPLACE_HEADER_BASE_STRING = "workplace"

-- Copy the originals directly into a subtable. local workplaceIndex1 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "1"]	local workplaceIndex2 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "2"]	local workplaceIndex3 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "3"]	local workplaceIndex4 = farmsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "4"]

local workplace1 = originalFarm[workplaceIndex1] local workplace2 = originalFarm[workplaceIndex2] local workplace3 = originalFarm[workplaceIndex3] local workplace4 = originalFarm[workplaceIndex4]

-- if it's not an empty string, then save that to the table, otherwise nil local workplaces = { (workplace1 ~= "" and workplace1) or nil, (workplace2 ~= "" and workplace2) or nil, (workplace3 ~= "" and workplace3) or nil, (workplace4 ~= "" and workplace4) or nil }

return workplaces end

--- --- Creates a new subtable containing the recipes available in the specified --- farm. --- --- @param farmName string the display name of the farm --- @param originalFarm table farm data record from which to make subtable --- @param farmsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the recipe IDs local function makeRecipesSubtable(farmName, originalFarm, farmsHeaderLookup)

-- A constant we'll need only within this function. local MAX_RECIPES = 3 local LOOKUP_RECIPE_BASE_STRING = "recipe" local LOOKUP_GOOD_BASE_STRING = "Good" local LOOKUP_GRADE_BASE_STRING = "Grade" local LOOKUP_PLANTING_TIME_BASE_STRING = "PlantingTime" local LOOKUP_HARVESTING_TIME_BASE_STRING = "HarvestTime"

if not mapRecipeGoodIDsToFarmNames then mapRecipeGoodIDsToFarmNames = {} end

-- Copy the originals directly into a subtable. local recipes = {} for i = 1, MAX_RECIPES do

-- A subtable for this recipe newRecipe = {} local originalGoodIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i				.. LOOKUP_GOOD_BASE_STRING] local originalGradeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i				.. LOOKUP_GRADE_BASE_STRING] local originalPlantingTimeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i				.. LOOKUP_PLANTING_TIME_BASE_STRING] local originalHarvestingTimeIndex = farmsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i				.. LOOKUP_HARVESTING_TIME_BASE_STRING]

local originalStackSize, originalGoodID = originalFarm[originalGoodIndex]:match(PATTERN_SPLIT_STACK_AND_ID)

-- Skip blank recipes. if originalGoodID and originalGoodID ~= "" then newRecipe[INDEX_RECIPE_GOOD_ID] = originalGoodID newRecipe[INDEX_RECIPE_STACK_SIZE] = tonumber(originalStackSize) newRecipe[INDEX_RECIPE_GRADE] = tonumber(originalFarm[originalGradeIndex]:match(PATTERN_CAPTURE_END_NUMBER)) newRecipe[INDEX_RECIPE_PLANTING_TIME] = tonumber(originalFarm[originalPlantingTimeIndex]) newRecipe[INDEX_RECIPE_HARVESTING_TIME] = tonumber(originalFarm[originalHarvestingTimeIndex])

table.insert(recipes,newRecipe)

if not mapRecipeGoodIDsToFarmNames[originalGoodID] then mapRecipeGoodIDsToFarmNames[originalGoodID] = {} end table.insert(mapRecipeGoodIDsToFarmNames[originalGoodID], farmName) end end

return recipes end

--- --- Transforms the originalFarmsTable returned from CSV processing to be --- more conducive to member functions looking up data. Converts text strings --- into subtables. Converts header row into field keys on every record and --- stores records by id rather than arbitrary integer. --- --- @param originalFarmsTable table of CSV-based data, with header row, data rows --- @param farmsHeaderLookup table lookup table of headers to get indexes --- @return table better structured with IDs as keys local function restructureFarmsTable(originalFarmsTable, farmsHeaderLookup)

-- A few constants we need only in this function. local DATA_ROWS = 2 local INDEX_ORIGINAL_ID = 1 local INDEX_ORIGINAL_NAME = 2 local INDEX_ORIGINAL_DESCRIPTION = 3 local INDEX_ORIGINAL_CATEGORY = 4 local INDEX_ORIGINAL_SIZE_X = 5 local INDEX_ORIGINAL_SIZE_Y = 6 -- Required goods are indexes 7, 8, 9 local INDEX_ORIGINAL_CONSTRUCTION_TIME = 10 local INDEX_ORIGINAL_CITY_SCORE = 11 local INDEX_ORIGINAL_MOVABLE = 12 local INDEX_ORIGINAL_ESSENTIAL = 13 local INDEX_ORIGINAL_AREA = 14 local INDEX_ORIGINAL_STORAGE = 15

mapNamesToIDs = {}

local newFarmsTable = {} for _, originalFarm in ipairs(originalFarmsTable[DATA_ROWS]) do

-- Copy over the content, mapping unhelpful indexes into headers keys. local newFarm = {} newFarm[INDEX_ID] = originalFarm[INDEX_ORIGINAL_ID] newFarm[INDEX_NAME] = originalFarm[INDEX_ORIGINAL_NAME] newFarm[INDEX_DESCRIPTION] = originalFarm[INDEX_ORIGINAL_DESCRIPTION] newFarm[INDEX_CATEGORY] = originalFarm[INDEX_ORIGINAL_CATEGORY] newFarm[INDEX_SIZE_X] = tonumber(originalFarm[INDEX_ORIGINAL_SIZE_X]) newFarm[INDEX_SIZE_Y] = tonumber(originalFarm[INDEX_ORIGINAL_SIZE_Y]) newFarm[INDEX_CITY_SCORE] = tonumber(originalFarm[INDEX_ORIGINAL_CITY_SCORE]) newFarm[INDEX_MOVABLE] = originalFarm[INDEX_ORIGINAL_MOVABLE] == "True" newFarm[INDEX_INITIALLY_ESSENTIAL] = originalFarm[INDEX_ORIGINAL_ESSENTIAL] == "True" newFarm[INDEX_AREA] = tonumber(originalFarm[INDEX_ORIGINAL_AREA]) newFarm[INDEX_STORAGE] = tonumber(originalFarm[INDEX_ORIGINAL_STORAGE]) newFarm[INDEX_CONSTRUCTION_TIME] = tonumber(originalFarm[INDEX_ORIGINAL_CONSTRUCTION_TIME])

newFarm[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalFarm, farmsHeaderLookup) newFarm[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalFarm, farmsHeaderLookup) newFarm[INDEX_RECIPES] = makeRecipesSubtable(newFarm[INDEX_NAME], originalFarm, farmsHeaderLookup)

newFarmsTable[ newFarm[INDEX_ID] ] = newFarm

-- Also populate the map for looking up IDs with display names mapNamesToIDs[ newFarm[INDEX_NAME] ] = newFarm[INDEX_ID] end

return newFarmsTable end

--- --- Data loader function that uses the utility module and restructures the data --- to be easier to access for invoking methods and later method calls. This --- method is automatically called by all public member functions if the main --- data table has not yet been populated in the current session. local function loadData

-- Utility module retrieves the data as basic, flat lua tables. local originalFarmsTable, farmsHeaderLookup = CsvUtils.extractTables(DATA_TEMPLATE_NAME)

-- Now restructure to be more conducive. farmsTable = restructureFarmsTable(originalFarmsTable, farmsHeaderLookup) end

--- --- Uses the display name, which people are more familiar with, to find the --- encoded ID of the farm. Useful for retrieving the data that is --- indexed by ID. --- --- Returns nil if the farm with the specified name is not found. --- --- @param displayName string plain-language name of the farm to find --- @return string ID of the farm found, or nil if not found local function findfarmIDByName(displayName)

-- At runtime, this should never be nil or empty, so throw an error. if not displayName or displayName == "" then error("Parameter is nil or empty for the farm's name: " .. displayName .. ".") end

if not farmsTable then loadData end

return mapNamesToIDs[displayName] end

--endregion

--region Public methods

--- Retrieve the whole table of data for the specified farm. Instead of --- this, you should probably be calling the individual getter methods. --- --- Throws an error if called with nil or empty string. Returns nil if the --- specified farm cannot be found. --- --- @param farmID string ID of the farm --- @return table containing the data with key-value pairs, or nil if not found function FarmsData.getAllDataForFarmByID(farmID)

-- At runtime, this should never be nil or empty. if not farmID or farmID == "" then error("Parameter is nil or empty for the farm's ID.") end

if not farmsTable then loadData end

return farmsTable[farmID] end

--- Retrieve the whole table of data for the specified farm. Instead of --- this, you should probably be calling the individual getter methods. --- --- Throws an error if called with nil or empty string. Returns nil if the --- specified farm cannot be found. --- --- @param displayName string plain language name of the farm --- @return table containing the data with key-value pairs, or nil if not found function FarmsData.getAllDataForFarm(displayName)

-- At runtime, this should never be nil or empty. if not displayName or displayName == "" then error("Parameter is nil or empty for the farm's name.") end

if not farmsTable then loadData end local farmID = findfarmIDByName(displayName) if not farmID then return nil end return farmsTable[farmID] end

--- --- Retrieves the ID for the farm specified by its plain language --- display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return string the ID of the specified farm function FarmsData.getFarmID(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_ID] end

--- --- Retrieves the description for the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return string the in-game description of the specified farm function FarmsData.getFarmDescription(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_DESCRIPTION] end

--- --- Retrieves the construction toolbar category for the farm specified --- by its plain language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return string the category of the specified farm function FarmsData.getFarmCategory(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_CATEGORY] end

--- --- Retrieves the 2x2 size for the farm specified by its plain language --- display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number the X-size of the farm ---@return number the Y-size of the farm function FarmsData.getFarmSize(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_SIZE_X], farm[INDEX_SIZE_Y] end

--- --- Retrieves the goods required for construction for the farm specified --- by its plain language display name, in a table that looks like this: --- --- ["requiredGoods"] = { ---   [1] = { ["stackSize"] = 99, ["goodID"] = "good1_ID" }, ---   [2] = { ["stackSize"] = 99, ["goodID"] = "good2_ID" }, ---   [3] = { ... } or missing if fewer --- } --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return table of required goods function FarmsData.getAllFarmRequiredGoods(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_REQUIRED_GOODS] end

--- --- Retrieves the specified required construction good for the farm --- specified by its plain language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@param requirementIndex number which construction good to retrieve ---@return string the ID of the good that is required, or nil if none ---@return number the stack size of that good, or nil if none function FarmsData.getFarmRequiredGood(displayName, requirementIndex)

local requiredGoods = FarmsData.getAllFarmRequiredGoods(displayName)

if not requiredGoods then return nil end

local requirement = requiredGoods[requirementIndex] if not requirement then return nil end

return requirement[INDEX_CONSTRUCTION_GOODS_GOOD_ID], requirement[INDEX_CONSTRUCTION_GOODS_STACK_SIZE] end

--- --- Retrieves the construction time for the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of seconds it takes to construct the farm function FarmsData.getFarmConstructionTime(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_CONSTRUCTION_TIME] end

--- --- Retrieves the city score awarded for the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of points for city score from having the farm function FarmsData.getFarmCityScore(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_CITY_SCORE] end

--- --- Retrieves whether the farm specified by its plain language display --- name can be moved, at any cost. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return boolean of whether the farm can be moved function FarmsData.isFarmMovable(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_MOVABLE] end

--- --- Retrieves whether the farm specified by its plain language display --- name has an essential starting blueprint for a new game profile. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return boolean of whether the farm's blueprint is essential function FarmsData.isFarmInitiallyEssential(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_INITIALLY_ESSENTIAL] end

--- --- Retrieves the gathering area for the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of tiles of gathering radius function FarmsData.getFarmArea(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_AREA] end

--- --- Retrieves the storage capacity of the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of storage capacity at the farm function FarmsData.getFarmStorage(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_STORAGE] end

--- --- Retrieves the table of workplaces for the farm specified by its --- plain language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return table of workplaces function FarmsData.getAllFarmWorkplaces(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_WORKPLACES] end

--- --- Retrieves the number of workplaces for the farm specified by its --- plain language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of workplaces function FarmsData.getFarmNumberOfWorkplaces(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return #farm[INDEX_WORKPLACES] end

--- --- Retrieves the table of recipes for the farm specified by its --- plain language display name, in a table that looks like this: --- --- ["recipes"] = { --- 	[1] = "recipe1_ID", --- 	[2] = "recipe2_ID", --- 	[3] = "recipe3_ID", --- 	[4] = ... or missing if fewer --- } --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return table of recipe IDs function FarmsData.getAllFarmRecipes(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

return farm[INDEX_RECIPES] end

--- --- Retrieves the number of recipes for the farm specified by its --- plain language display name, in a table that looks like this: --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return number of recipe at the farm function FarmsData.getFarmNumberOfRecipes(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return 0 end

recipes = farm[INDEX_RECIPES] if not recipes then return 0 end

return #recipes end

--- --- Retrieves the product's ID and the stack size produced by the specified --- recipe at the farm specified by its plain language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@param recipeIndex number the index of the recipe ID ---@return string the ID of the good produced by the specified recipe ---@return number the stack size produced by the specified recipe function FarmsData.getFarmRecipeProduct(displayName, recipeIndex)

local recipes = FarmsData.getAllFarmRecipes(displayName)

if not recipes then return nil end

local recipe = recipes[recipeIndex] if not recipe then return nil end

return recipe[INDEX_RECIPE_GOOD_ID], recipe[INDEX_RECIPE_STACK_SIZE] end

--- --- Retrieves the other stats for specified recipe at the farm specified by --- its plain language display name: the efficiency grade and the planting and --- harvesting times. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@param recipeIndex number the index of the recipe ID ---@return number of stars, the grade of the specified recipe ---@return number of seconds for planting ---@return number of seconds for harvesting function FarmsData.getFarmRecipeStats(displayName, recipeIndex)

local recipes = FarmsData.getAllFarmRecipes(displayName)

if not recipes then return nil end

local recipe = recipes[recipeIndex] if not recipe then return nil end

return recipe[INDEX_RECIPE_GRADE], recipe[INDEX_RECIPE_PLANTING_TIME], recipe[INDEX_RECIPE_HARVESTING_TIME] end

--- --- Retrieves the icon filename for the farm specified by its plain --- language display name. --- --- Returns nil if the farm named was not found. --- ---@param displayName string the plain language name of the farm ---@return string the farm's icon filename, including the extension function FarmsData.getFarmIcon(displayName)

local farm = FarmsData.getAllDataForFarm(displayName)

if not farm then return nil end

-- the base string of the icon is the ID. It has to be not nil to	-- concatenate if farm[INDEX_ID] then return farm[INDEX_ID] .. "_icon.png" else return nil end end

--- --- Retrieves the plain language display name for the farm specified by --- its ID. --- --- Returns nil if the farm named was not found. --- ---@param farmID string the ID of the farm ---@return string the farm's name as seen in-game function FarmsData.getFarmNameByID(farmID)

local farm = FarmsData.getAllDataForFarmByID(farmID)

if not farm then return nil end

return farm[INDEX_NAME] end

--- --- Retrieves the icon filename for the the farm specified by its ID. --- --- Returns nil if the farm named was not found. --- ---@param farmID string the ID of the farm ---@return string the farm's icon filename, including the extension function FarmsData.getFarmIconByID(farmID)

local farm = FarmsData.getAllDataForFarmByID(farmID)

if not farm then return nil end

-- the base string of the icon is the ID. It has to be not nil to	-- concatenate if farm[INDEX_ID] then return farm[INDEX_ID] .. "_icon.png" else return nil end end

--- --- Retrieve all display names of farms that have a recipe that produces the --- specified product ID. --- ---@param productID string the ID of the good to produce ---@return table of farm names that produce the specified good function FarmsData.getFarmNamesWithRecipeProductID(productID)

-- At runtime, this should never be nil or empty. if not productID or productID == "" then error("Parameter is nil or empty for product ID.") end

if not farmsTable then loadData end

return mapRecipeGoodIDsToFarmNames[productID] end

--endregion

return FarmsData