Module:InstitutionsData: Difference between revisions

From Against the Storm Official Wiki
(added function to get all IDs; removed outdated comment at top)
m (Changed my mind; moved that public method into separate ServicesData module)
Tag: Manual revert
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
--- @module InstitutionsData
---@module InstitutionsData.lua
local InstitutionsData = {}
 
 
 
--region Dependencies
 
local DATA_TEMPLATE_NAME = "Template:Institutions_csv"
 
local CsvUtils = require("Module:CsvUtils")
 
-- Some dependencies are loaded lazily
local GoodsData
 
--endregion
 
 
 
--region Private member variables
 
--- Main data tables, like this: table[ID] = table containing data for that ID
local institutionsTable
 
--- Supporting table, list of names table[i] = name.
local institutionsNames
 
--- Lookup map. Built once and reused on subsequent calls within this session,
--- like this:  table[display name] = institutionID
local mapNamesToIDs
--- Lookup map. Built once and reused on all subsequent calls within this
--- session, like this: table[recipeID] = { institutionName1,
--- institutionName2, ... }
local mapRecipeIDsToInstitutionNames
 
--endregion
 
 
 
--region Private constants
 
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_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 PATTERN_SPLIT_STACK_AND_ID = "(%d+)%s([%[%]%s%a]+)"
 
--endregion
 
 
 
--region Private methods
 
---
--- Creates a new subtable containing the construction goods required for the
--- specified institution.
---
--- @param originalInstitution table institution data record from which to make subtable
--- @param institutionsHeaderLookup table header lookup built from the CSV data
--- @return table subtable with the required construction goods
local function makeRequiredGoodsSubtable(originalInstitution, institutionsHeaderLookup)
 
-- 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 = institutionsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "1"]
local requiredIndex2 = institutionsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "2"]
local requiredIndex3 = institutionsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "3"]
 
local number1, id1 = originalInstitution[requiredIndex1]:match(PATTERN_SPLIT_STACK_AND_ID)
local number2, id2 = originalInstitution[requiredIndex2]:match(PATTERN_SPLIT_STACK_AND_ID)
local number3, id3 = originalInstitution[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
---This module does not define an _actual_ derived class, but it creates an instance of one via prototyping patterns. Understand this file as a big procedure, top-to-bottom, rather than a class definition.
--- institution.
---
---
--- @param originalInstitution table institution data record from which to make subtable
---Initializes a BaseDataModel with the data file associated with this module. Makes necessary modifications to the basic data, including schema and method overrides, to permit the exceptions associated with data in this model.
--- @param institutionsHeaderLookup table header lookup built from the CSV data
--- @return table subtable with the workplaces
local function makeWorkplacesSubtable(originalInstitution, institutionsHeaderLookup)
 
-- A constant we'll need only within this function.
local WORKPLACE_HEADER_BASE_STRING = "workplace"
 
-- Copy the originals directly into a subtable.
local workplaceIndex1 = institutionsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "1"]
local workplaceIndex2 = institutionsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "2"]
local workplaceIndex3 = institutionsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "3"]
local workplaceIndex4 = institutionsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "4"]
 
local workplace1 = originalInstitution[workplaceIndex1]
local workplace2 = originalInstitution[workplaceIndex2]
local workplace3 = originalInstitution[workplaceIndex3]
local workplace4 = originalInstitution[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


--Create instance
local BaseDataModel = require("Module:BaseDataModel")
local DATA_FILE = "Module:InstitutionsData/Institutions.json"


---@type BaseDataModel
local institutionsDataModel = BaseDataModel.new(DATA_FILE)


---
--- Creates a new subtable containing the recipes available in the specified
--- institution.
---
--- @param institutionName string the name of the institution
--- @param originalInstitution table institution data record from which to make subtable
--- @param institutionsHeaderLookup table header lookup built from the CSV data
--- @return table subtable with the recipe IDs
local function makeRecipesSubtable(institutionName, originalInstitution, institutionsHeaderLookup)
-- A constant we'll need only within this function.
local LOOKUP_RECIPE_BASE_STRING = "recipe"
-- Copy the originals directly into a subtable.
local recipeIndex1 = institutionsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "1"]
local recipeIndex2 = institutionsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "2"]
local recipeIndex3 = institutionsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "3"]
local recipeIndex4 = institutionsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "4"]
local recipe1 = originalInstitution[recipeIndex1]
local recipe2 = originalInstitution[recipeIndex2]
local recipe3 = originalInstitution[recipeIndex3]
local recipe4 = originalInstitution[recipeIndex4]
local recipes = {
(recipe1 ~= "" and recipe1) or nil,
(recipe2 ~= "" and recipe2) or nil,
(recipe3 ~= "" and recipe3) or nil,
(recipe4 ~= "" and recipe4) or nil
}
-- Make reverse-lookup map.
if recipe1 and recipe1 ~= "" then
if not mapRecipeIDsToInstitutionNames[recipe1] then
mapRecipeIDsToInstitutionNames[recipe1] = {}
end
table.insert( mapRecipeIDsToInstitutionNames[recipe1], institutionName)
end
if recipe2 and recipe2 ~= "" then
if not mapRecipeIDsToInstitutionNames[recipe2] then
mapRecipeIDsToInstitutionNames[recipe2] = {}
end
table.insert( mapRecipeIDsToInstitutionNames[recipe2], institutionName)
end
if recipe3 and recipe3 ~= "" then
if not mapRecipeIDsToInstitutionNames[recipe3] then
mapRecipeIDsToInstitutionNames[recipe3] = {}
end
table.insert( mapRecipeIDsToInstitutionNames[recipe3], institutionName)
end
if recipe4 and recipe4 ~= "" then
if not mapRecipeIDsToInstitutionNames[recipe4] then
mapRecipeIDsToInstitutionNames[recipe4] = {}
end
table.insert( mapRecipeIDsToInstitutionNames[recipe4], institutionName)
end
return recipes
end
---
--- Transforms the originalInstitutionsTable 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 originalInstitutionsTable table of CSV-based data, with header row, data rows
--- @param institutionsHeaderLookup table lookup table of headers to get indexes
--- @return table better structured with IDs as keys
local function restructureInstitutionsTable(originalInstitutionsTable, institutionsHeaderLookup)
-- 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
mapNamesToIDs = {}
institutionsNames = {}
mapRecipeIDsToInstitutionNames = {}
local newInstitutionsTable = {}
for _, originalInstitution in ipairs(originalInstitutionsTable[DATA_ROWS]) do
-- Copy over the content, mapping unhelpful indexes into headers keys.
local newInstitution = {}
newInstitution[INDEX_ID] = originalInstitution[INDEX_ORIGINAL_ID]
newInstitution[INDEX_NAME] = originalInstitution[INDEX_ORIGINAL_NAME]
newInstitution[INDEX_DESCRIPTION] = originalInstitution[INDEX_ORIGINAL_DESCRIPTION]
newInstitution[INDEX_CATEGORY] = originalInstitution[INDEX_ORIGINAL_CATEGORY]
newInstitution[INDEX_SIZE_X] = tonumber(originalInstitution[INDEX_ORIGINAL_SIZE_X])
newInstitution[INDEX_SIZE_Y] = tonumber(originalInstitution[INDEX_ORIGINAL_SIZE_Y])
newInstitution[INDEX_CITY_SCORE] = tonumber(originalInstitution[INDEX_ORIGINAL_CITY_SCORE])
newInstitution[INDEX_MOVABLE] = "TRUE" == originalInstitution[INDEX_ORIGINAL_MOVABLE]
newInstitution[INDEX_INITIALLY_ESSENTIAL] = "TRUE" == originalInstitution[INDEX_ORIGINAL_ESSENTIAL]
newInstitution[INDEX_CONSTRUCTION_TIME] = tonumber(originalInstitution[INDEX_ORIGINAL_CONSTRUCTION_TIME])
newInstitution[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalInstitution, institutionsHeaderLookup)
newInstitution[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalInstitution, institutionsHeaderLookup)
newInstitution[INDEX_RECIPES] = makeRecipesSubtable(newInstitution[INDEX_NAME], originalInstitution, institutionsHeaderLookup)
newInstitutionsTable[ newInstitution[INDEX_ID] ] = newInstitution
table.insert(institutionsNames, newInstitution[INDEX_NAME])
-- Also populate the map for looking up IDs with display names
mapNamesToIDs[ newInstitution[INDEX_NAME] ] = newInstitution[INDEX_ID]
end
return newInstitutionsTable
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 originalInstitutionsTable, institutionsHeaderLookup = CsvUtils.extractTables(DATA_TEMPLATE_NAME)
-- Now restructure to be more conducive.
institutionsTable = restructureInstitutionsTable(originalInstitutionsTable, institutionsHeaderLookup)
end


local function load()


if not workshopsTable then
--Begin instance overrides
loadData()
for _, building in pairs(institutionsDataModel.dataTable) do
end
    building[institutionsDataModel.schema.CATEGORY2] = "Service Building"
    building[institutionsDataModel.schema.IS_PROVIDING_SERVICES] = true
end
end


institutionsDataModel.schema.RECIPE.GRADE = "gradeId"


--IMPORTANT: The data structure has one fewer levels, like RECIPE > PRODUCT_ID
institutionsDataModel.schema.RECIPE.PRODUCT.ID = "servedNeed"


---
--- Uses the display name, which people are more familiar with, to find the
--- encoded ID of the institution. Useful for retrieving the  data that is
--- indexed by ID.
---
--- Returns nil if the institution with the specified name is not found.
---
--- @param displayName string plain-language name of the institution to find
--- @return string ID of the institution found, or nil if not found
local function findInstitutionIDByName(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 institution's name: " .. displayName .. ".")
end
if not institutionsTable then
loadData()
end


return mapNamesToIDs[displayName]
end


--/** Function definitions follow. **/
--Collapse the regions in the IDE to see the procedure pick up with the assignments of these functions to override the associated member methods of BaseDataModel.
--region Public building interface
--no overrides!
--endregion
--endregion


--region Public building recipe query interface


local INDEX_PAIR_BUILDING_ID = "buildingID"
local INDEX_PAIR_RECIPE = "recipe"


--region Public methods
---@public
 
---`Overrides BaseDataModel`
--- Retrieve the whole table of data for the specified institution. 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
---Gets all this instance's buildings' recipes that have the specified product. Returns an array of pairs of building IDs and recipe data.
--- specified institution cannot be found.
---
---
--- @param institutionID string ID of the institution
---@param self BaseDataModel this instance, with specific data
--- @return table containing the data with key-value pairs, or nil if not found
---@param productID string the product
function InstitutionsData.getAllDataForInstitutionByID(institutionID)
---@return table array of pairs of building IDs and recipe data, or {} if none found
function getIDsAndRecipesWhereProductID(self, productID)


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


if not institutionsTable then
    local ret = {}
loadData()
    for id, building in pairs(self.dataTable) do
end
        for _, recipe in ipairs(building[self.schema.RECIPES]) do


return institutionsTable[institutionID]
            --If there's a matching served need
            if recipe[self.schema.RECIPE.PRODUCT.ID] == productID then
                -- Add as an buildingID-recipe pair
                table.insert(ret, {
                    [INDEX_PAIR_BUILDING_ID] = id,
                    [INDEX_PAIR_RECIPE] = recipe,
                })
            end
        end
    end
    return ret
end
end


 
---@public
 
---`Overrides BaseDataModel`
--- Retrieve the whole table of data for the specified institution. 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
---Gets all this instance's specified building's recipes that produce the specified product. Returns an array of pairs of building IDs and recipe data.
--- specified institution cannot be found.
---
---
--- @param displayName string plain language name of the institution
---@param self BaseDataModel this instance, with specific data
--- @return table containing the data with key-value pairs, or nil if not found
---@param productID string the product
function InstitutionsData.getAllDataForInstitution(displayName)
---@param buildingID string the building
---@return table array of pairs of building IDs and recipe data, or {} if none found
function getIDsAndRecipesWhereProductIDAndBuildingID(self, productID, buildingID)


-- At runtime, this should never be nil or empty.
    --Can't findID from here, but that's okay, this is a small data file: just loop.
if not displayName or displayName == "" then
    local ret = {}
error("Parameter is nil or empty for the institution's name.")
    for id, building in pairs(self.dataTable) do
end


if not institutionsTable then
        if building[self.schema.ID] == buildingID then
loadData()
end
local institutionID = findInstitutionIDByName(displayName)
if not institutionID then
return nil
end
return institutionsTable[institutionID]
end


            for _, recipe in ipairs(building[self.schema.RECIPES]) do


                --If there's a matching seeked deposit
                if recipe[self.schema.RECIPE.PRODUCT.ID] == productID then
                    -- Add as an buildingID-recipe pair
                    table.insert(ret, {
                        [INDEX_PAIR_BUILDING_ID] = id,
                        [INDEX_PAIR_RECIPE] = recipe,
                    })
                end
            end


---
        end
--- Retrieves the ID for the institution specified by its plain language
    end
--- display name.
    return ret
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return string the ID of the specified institution
function InstitutionsData.getInstitutionID(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_ID]
end
end


--endregion


--region Public recipe data retrieval interface


---@public
---`Overrides BaseDataModel`
---
---
--- Retrieves the description for the institution specified by its plain
---Gets the product ID from the provided recipe.
--- language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return string the in-game description of the specified institution
function InstitutionsData.getInstitutionDescription(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_DESCRIPTION]
end
 
 
 
---
--- Retrieves the construction toolbar category for the institution specified
--- by its plain language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return string the category of the specified institution
function InstitutionsData.getInstitutionCategory(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_CATEGORY]
end
 
 
 
---
--- Retrieves the 2x2 size for the institution specified by its plain language
--- display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return number the X-size of the institution
---@return number the Y-size of the institution
function InstitutionsData.getInstitutionSize(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_SIZE_X], institution[INDEX_SIZE_Y]
end
 
 
 
---
--- Retrieves the goods required for construction for the institution 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 institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return table of required goods
function InstitutionsData.getAllInstitutionRequiredGoods(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_REQUIRED_GOODS]
end
 
 
 
---
--- Retrieves the specified required construction good for the institution
--- specified by its plain language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@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 InstitutionsData.getInstitutionRequiredGood(displayName, requirementIndex)
 
local requiredGoods = InstitutionsData.getAllInstitutionRequiredGoods(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 institution specified by its plain
--- language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return number of seconds it takes to construct the institution
function InstitutionsData.getInstitutionConstructionTime(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_CONSTRUCTION_TIME]
end
 
 
 
---
--- Retrieves the city score awarded for the institution specified by its plain
--- language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return number of points for city score from having the institution
function InstitutionsData.getInstitutionCityScore(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_CITY_SCORE]
end
 
 
 
---
--- Retrieves whether the institution specified by its plain language display
--- name can be moved, at any cost.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return boolean of whether the institution can be moved
function InstitutionsData.isInstitutionMovable(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_MOVABLE]
end
 
 
 
---
--- Retrieves whether the institution specified by its plain language display
--- name has an essential starting blueprint for a new game profile.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return boolean of whether the institution's blueprint is essential
function InstitutionsData.isInstitutionInitiallyEssential(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_INITIALLY_ESSENTIAL]
end
 
 
 
---
--- Retrieves the table of workplaces for the institution specified by its
--- plain language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return table of workplaces
function InstitutionsData.getAllInstitutionWorkplaces(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_WORKPLACES]
end
 
 
 
---
--- Retrieves the number of workplaces for the institution specified by its
--- plain language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return number of workplaces
function InstitutionsData.getInstitutionNumberOfWorkplaces(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return #institution[INDEX_WORKPLACES]
end
 
 
 
---
--- Retrieves the table of recipes for the institution 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 institution named was not found.
---
---@param displayName string the plain language name of the institution
---@return table of recipe IDs
function InstitutionsData.getAllInstitutionRecipes(displayName)
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
return institution[INDEX_RECIPES]
end
 
 
 
---
--- Retrieves the specified recipe at the institution specified by its plain
--- language display name.
---
--- Returns nil if the institution named was not found.
---
---@param displayName string the plain language name of the institution
---@param recipeIndex number the index of the recipe ID
---@return string the ID of the specified recipe
function InstitutionsData.getInstitutionRecipe(displayName, recipeIndex)
 
local recipes = InstitutionsData.getAllInstitutionRecipes(displayName)
 
if not recipes then
return nil
end
 
return recipes[recipeIndex]
end
 
 
 
---
--- Retrieves the icon filename for the institution specified by its plain
--- language display name.
---
---
--- Returns nil if the institution named was not found.
---@param self BaseDataModel this instance, with specific data
---
---@param recipeData recipePairTable
---@param displayName string the plain language name of the institution
---@return string product ID
---@return string the institution's icon filename, including the extension
function getRecipeProductID(self, recipeData)
function InstitutionsData.getInstitutionIcon(displayName)
    return recipeData.recipe[self.schema.RECIPE.PRODUCT.ID]
 
local institution = InstitutionsData.getAllDataForInstitution(displayName)
 
if not institution then
return nil
end
 
-- the base string of the icon is the ID. It has to be not nil to
-- concatenate
if institution[INDEX_ID] then
return institution[INDEX_ID] .. "_icon.png"
else
return nil
end
end
end


 
---@public
 
---`Overrides BaseDataModel`
---
---
--- Retrieves the plain language display name for the institution specified by
---Gets the amount of product produced by the provided recipe.
--- its ID.
---
---
--- Returns nil if the institution named was not found.
---Parameters
---
---
---@param institutionID string the ID of the institution
---`self:BaseDataModel` - ignored
---@return string the institution's name as seen in-game
function InstitutionsData.getInstitutionNameByID(institutionID)
 
local institution = InstitutionsData.getAllDataForInstitutionByID(institutionID)
 
if not institution then
return nil
end
 
return #institution[INDEX_NAME]
end
 
 
 
---
--- Retrieves the icon filename for the the institution specified by its ID.
---
--- Returns nil if the institution named was not found.
---
---@param institutionID string the ID of the institution
---@return string the institution's icon filename, including the extension
function InstitutionsData.getInstitutionIconByID(institutionID)
 
local institution = InstitutionsData.getAllDataForInstitutionByID(institutionID)
 
if not institution then
return nil
end
 
-- the base string of the icon is the ID. It has to be not nil to
-- concatenate
if institution[INDEX_ID] then
return institution[INDEX_ID] .. "_icon.png"
else
return nil
end
end
 
 
 
---
--- Retrieve all display names of institutions that have the specified recipe
--- ID.
---
---@param recipeID string the ID of the recipe
---@return table of institution names that produce the specified recipe
function InstitutionsData.getInstitutionNamesWithRecipeID(recipeID)
 
-- At runtime, this should never be nil or empty.
if not recipeID or recipeID == "" then
error("Parameter is nil or empty for product ID.")
end
 
if not institutionsTable then
loadData()
end
 
return mapRecipeIDsToInstitutionNames[recipeID]
end
 
 
 
---
--- Retrieves the name of the institution at the specified index.
---
---@param index number index
---@return string name of the building at the specified index
function InstitutionsData.getInstitutionNameFromDatabase(index)
 
if not index then
return nil
end
 
if not institutionsTable then
loadData()
end
 
return institutionsNames[index]
end
 
 
 
---
---
--- Returns the number of buildings that can be retrieved from this
---`recipeData:recipePairTable` - ignored
--- module's database.
---
---
---@return number of records
---@return number stack size
function InstitutionsData.getNumberOfInstitutionsInDatabase()
function getRecipeProductAmount()
 
    return 1
if not institutionsTable then
loadData()
end
 
return #institutionsNames
end
end


--endregion
--endregion
--/** End Function definitions. Procedure resumes. **/






--region Public Building interface
--Override building recipe query interface
-- getID(displayName)
institutionsDataModel.getIDsAndRecipesWhereProductID = getIDsAndRecipesWhereProductID
-- getCategory
institutionsDataModel.getIDsAndRecipesWhereProductIDAndBuildingID = getIDsAndRecipesWhereProductIDAndBuildingID
-- getCityScore
-- getConstructionCosts (as [goodName] = stack size)
-- getConstructionTime
-- getDescription
-- getIcon
-- isMovable
-- getName
-- getNumberOfWorkplaces
-- getSize (as "X x Y")
-- getStorage


function InstitutionsData.getID(displayName)
--Override recipe data retrieval interface
load()
institutionsDataModel.getRecipeProductID = getRecipeProductID
return mapNamesToIDs[displayName]
institutionsDataModel.getRecipeProductAmount = getRecipeProductAmount
end


function InstitutionsData.getCategory(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_CATEGORY]
end


function InstitutionsData.getCityScore(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_CITY_SCORE]
end
function InstitutionsData.getConstructionCosts(id)
load()
GoodsData = require("Module:GoodsData")
institution = institutionsTable[id]
if not institution then
return nil
end
local constructionCosts = {}
for _, stacks in ipairs(institution[INDEX_REQUIRED_GOODS]) do
local goodName = GoodsData.getGoodNameByID(stacks[INDEX_CONSTRUCTION_GOODS_GOOD_ID])
constructionCosts[goodName] = stacks[INDEX_CONSTRUCTION_GOODS_STACK_SIZE]
end
return constructionCosts
end
function InstitutionsData.getConstructionTime(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_CONSTRUCTION_TIME]
end
function InstitutionsData.getDescription(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_DESCRIPTION]
end
function InstitutionsData.getIcon(id)
load()
return institutionsTable[id] ~= nil and id .. "_icon.png"
end
function InstitutionsData.isMovable(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_MOVABLE]
end
function InstitutionsData.getName(id)
load()
return institutionsTable[id] ~= nil and institutionsTable[id][INDEX_NAME]
end
function InstitutionsData.getNumberOfWorkplaces(id)
load()
return institutionsTable[id] ~= nil and #institutionsTable[id][INDEX_WORKPLACES]
end
function InstitutionsData.getSize(id)
load()
institution = institutionsTable[id]
if not institution then
return nil
end
return institution[INDEX_SIZE_X] .. " × " .. institution[INDEX_SIZE_Y]
end
function InstitutionsData.getStorage()
return nil
end
--endregion


return InstitutionsData
return institutionsDataModel

Latest revision as of 23:01, 10 November 2024

Documentation for this module may be created at Module:InstitutionsData/doc

---@module InstitutionsData.lua
---
---This module does not define an _actual_ derived class, but it creates an instance of one via prototyping patterns. Understand this file as a big procedure, top-to-bottom, rather than a class definition.
---
---Initializes a BaseDataModel with the data file associated with this module. Makes necessary modifications to the basic data, including schema and method overrides, to permit the exceptions associated with data in this model.



--Create instance
local BaseDataModel = require("Module:BaseDataModel")
local DATA_FILE = "Module:InstitutionsData/Institutions.json"

---@type BaseDataModel
local institutionsDataModel = BaseDataModel.new(DATA_FILE)



--Begin instance overrides
for _, building in pairs(institutionsDataModel.dataTable) do
    building[institutionsDataModel.schema.CATEGORY2] = "Service Building"
    building[institutionsDataModel.schema.IS_PROVIDING_SERVICES] = true
end

institutionsDataModel.schema.RECIPE.GRADE = "gradeId"

--IMPORTANT: The data structure has one fewer levels, like RECIPE > PRODUCT_ID
institutionsDataModel.schema.RECIPE.PRODUCT.ID = "servedNeed"



--/** Function definitions follow. **/
--Collapse the regions in the IDE to see the procedure pick up with the assignments of these functions to override the associated member methods of BaseDataModel.
--region Public building interface
--no overrides!
--endregion

--region Public building recipe query interface

local INDEX_PAIR_BUILDING_ID = "buildingID"
local INDEX_PAIR_RECIPE = "recipe"

---@public
---`Overrides BaseDataModel`
---
---Gets all this instance's buildings' recipes that have the specified product. Returns an array of pairs of building IDs and recipe data.
---
---@param self BaseDataModel this instance, with specific data
---@param productID string the product
---@return table array of pairs of building IDs and recipe data, or {} if none found
function getIDsAndRecipesWhereProductID(self, productID)

    if not self.dataTable then
        error(ERROR_MESSAGE_INSTANCE_NOT_INITIALIZED)
    end

    local ret = {}
    for id, building in pairs(self.dataTable) do
        for _, recipe in ipairs(building[self.schema.RECIPES]) do

            --If there's a matching served need
            if recipe[self.schema.RECIPE.PRODUCT.ID] == productID then
                -- Add as an buildingID-recipe pair
                table.insert(ret, {
                    [INDEX_PAIR_BUILDING_ID] = id,
                    [INDEX_PAIR_RECIPE] = recipe,
                })
            end
        end
    end
    return ret
end

---@public
---`Overrides BaseDataModel`
---
---Gets all this instance's specified building's recipes that produce the specified product. Returns an array of pairs of building IDs and recipe data.
---
---@param self BaseDataModel this instance, with specific data
---@param productID string the product
---@param buildingID string the building
---@return table array of pairs of building IDs and recipe data, or {} if none found
function getIDsAndRecipesWhereProductIDAndBuildingID(self, productID, buildingID)

    --Can't findID from here, but that's okay, this is a small data file: just loop.
    local ret = {}
    for id, building in pairs(self.dataTable) do

        if building[self.schema.ID] == buildingID then

            for _, recipe in ipairs(building[self.schema.RECIPES]) do

                --If there's a matching seeked deposit
                if recipe[self.schema.RECIPE.PRODUCT.ID] == productID then
                    -- Add as an buildingID-recipe pair
                    table.insert(ret, {
                        [INDEX_PAIR_BUILDING_ID] = id,
                        [INDEX_PAIR_RECIPE] = recipe,
                    })
                end
            end

        end
    end
    return ret
end

--endregion

--region Public recipe data retrieval interface

---@public
---`Overrides BaseDataModel`
---
---Gets the product ID from the provided recipe.
---
---@param self BaseDataModel this instance, with specific data
---@param recipeData recipePairTable
---@return string product ID
function getRecipeProductID(self, recipeData)
    return recipeData.recipe[self.schema.RECIPE.PRODUCT.ID]
end

---@public
---`Overrides BaseDataModel`
---
---Gets the amount of product produced by the provided recipe.
---
---Parameters
---
---`self:BaseDataModel` - ignored
---
---`recipeData:recipePairTable` - ignored
---
---@return number stack size
function getRecipeProductAmount()
    return 1
end

--endregion
--/** End Function definitions. Procedure resumes. **/



--Override building recipe query interface
institutionsDataModel.getIDsAndRecipesWhereProductID = getIDsAndRecipesWhereProductID
institutionsDataModel.getIDsAndRecipesWhereProductIDAndBuildingID = getIDsAndRecipesWhereProductIDAndBuildingID

--Override recipe data retrieval interface
institutionsDataModel.getRecipeProductID = getRecipeProductID
institutionsDataModel.getRecipeProductAmount = getRecipeProductAmount



return institutionsDataModel