|
|
Line 1: |
Line 1: |
| --- @module WorkshopsData | | ---@module WorkshopsData |
| local WorkshopsData = {} | | local WorkshopsData = {} |
|
| |
|
|
| |
|
| | -- Initialize a new instance of the BaseDataModel, configured to load the workshops data |
| | local BaseDataModel = require("Module:BaseDataModel") |
| | local DATA_FILE = "Module:WorkshopsData/Workshops.json" |
|
| |
|
| --region Dependencies
| |
|
| |
|
| local DATA_TEMPLATE_NAME = "Template:Workshops_csv" | | local workshopsDataModel = BaseDataModel.new(DATA_FILE) |
|
| |
|
| local CsvUtils = require("Module:CsvUtils")
| |
|
| |
|
| -- Some dependencies are loaded lazily | | -- Return the instance for workshops data, not this module itself! |
| local GoodsData
| | return workshopsDataModel |
| | |
| --endregion
| |
| | |
| | |
| | |
| --region Private member variables
| |
| | |
| --- Main data table, like this: table[ID] = table containing data for that ID
| |
| local workshopsTable
| |
| | |
| --- Supporting table, list of names table[i] = name.
| |
| local workshopsNames
| |
| | |
| --- Lookup map. Built once and reused on all subsequent calls within this
| |
| --- session, like this: table[displayName] = workshopID
| |
| local mapNamesToIDs
| |
| | |
| --- Lookup map. Built once and reused on all subsequent calls within this
| |
| --- session, like this: table[recipeID] = { workshopName1, workshopName2, ... }
| |
| local mapRecipeIDsToWorkshopNames
| |
| | |
| --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_ESSENTIAL = "initiallyEssential"
| |
| local INDEX_STORAGE_CAP = "storage"
| |
| local INDEX_CONSTRUCTION_TIME = "constructionTime"
| |
| local INDEX_REQUIRED_GOODS = "requiredGoods"
| |
| local INDEX_WORKPLACES = "workplaces"
| |
| local INDEX_RECIPES = "recipes"
| |
| | |
| 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 workshop.
| |
| ---
| |
| --- @param originalWorkshop table workshop data record from which to make subtable
| |
| --- @param workshopsHeaderLookup table header lookup built from the CSV data
| |
| --- @return table subtable with the required construction goods
| |
| local function makeRequiredGoodsSubtable(originalWorkshop, workshopsHeaderLookup)
| |
| | |
| -- 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 = workshopsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "1"]
| |
| local requiredIndex2 = workshopsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "2"]
| |
| local requiredIndex3 = workshopsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "3"]
| |
| | |
| local number1, id1 = originalWorkshop[requiredIndex1]:match(PATTERN_SPLIT_STACK_AND_ID)
| |
| local number2, id2 = originalWorkshop[requiredIndex2]:match(PATTERN_SPLIT_STACK_AND_ID)
| |
| local number3, id3 = originalWorkshop[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
| |
| --- workshop.
| |
| ---
| |
| --- @param originalWorkshop table workshop data record from which to make subtable
| |
| --- @param workshopsHeaderLookup table header lookup built from the CSV data
| |
| --- @return table subtable with the workplaces
| |
| local function makeWorkplacesSubtable(originalWorkshop, workshopsHeaderLookup)
| |
| | |
| -- A constant we'll need only within this function.
| |
| local WORKPLACE_HEADER_BASE_STRING = "workplace"
| |
| | |
| -- Copy the originals directly into a subtable.
| |
| local workplaceIndex1 = workshopsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "1"]
| |
| local workplaceIndex2 = workshopsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "2"]
| |
| local workplaceIndex3 = workshopsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "3"]
| |
| local workplaceIndex4 = workshopsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "4"]
| |
| | |
| local workplace1 = originalWorkshop[workplaceIndex1]
| |
| local workplace2 = originalWorkshop[workplaceIndex2]
| |
| local workplace3 = originalWorkshop[workplaceIndex3]
| |
| local workplace4 = originalWorkshop[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
| |
| --- workshop.
| |
| ---
| |
| --- @param workshopName string the display name of the workshop
| |
| --- @param originalWorkshop table workshop data record from which to make subtable
| |
| --- @param workshopsHeaderLookup table header lookup built from the CSV data
| |
| --- @return table subtable with the recipe IDs
| |
| local function makeRecipesSubtable(workshopName, originalWorkshop, workshopsHeaderLookup)
| |
| | |
| -- A constant we'll need only within this function.
| |
| local LOOKUP_RECIPE_BASE_STRING = "recipe"
| |
| | |
| -- Copy the originals directly into a subtable.
| |
| local recipeIndex1 = workshopsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "1"]
| |
| local recipeIndex2 = workshopsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "2"]
| |
| local recipeIndex3 = workshopsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "3"]
| |
| local recipeIndex4 = workshopsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. "4"]
| |
| | |
| local recipe1 = originalWorkshop[recipeIndex1]
| |
| local recipe2 = originalWorkshop[recipeIndex2]
| |
| local recipe3 = originalWorkshop[recipeIndex3]
| |
| local recipe4 = originalWorkshop[recipeIndex4]
| |
| | |
| local recipes = {
| |
| (recipe1 ~= "" and recipe1) or nil,
| |
| (recipe2 ~= "" and recipe2) or nil,
| |
| (recipe3 ~= "" and recipe3) or nil,
| |
| (recipe4 ~= "" and recipe4) or nil
| |
| }
| |
| | |
| if recipe1 and recipe1 ~= "" then
| |
| if not mapRecipeIDsToWorkshopNames[recipe1] then
| |
| mapRecipeIDsToWorkshopNames[recipe1] = {}
| |
| end
| |
| table.insert(mapRecipeIDsToWorkshopNames[recipe1], workshopName)
| |
| end
| |
| if recipe2 and recipe2 ~= "" then
| |
| if not mapRecipeIDsToWorkshopNames[recipe2] then
| |
| mapRecipeIDsToWorkshopNames[recipe2] = {}
| |
| end
| |
| table.insert(mapRecipeIDsToWorkshopNames[recipe2], workshopName)
| |
| end
| |
| if recipe3 and recipe3 ~= "" then
| |
| if not mapRecipeIDsToWorkshopNames[recipe3] then
| |
| mapRecipeIDsToWorkshopNames[recipe3] = {}
| |
| end
| |
| table.insert(mapRecipeIDsToWorkshopNames[recipe3], workshopName)
| |
| end
| |
| if recipe4 and recipe4 ~= "" then
| |
| if not mapRecipeIDsToWorkshopNames[recipe4] then
| |
| mapRecipeIDsToWorkshopNames[recipe4] = {}
| |
| end
| |
| table.insert(mapRecipeIDsToWorkshopNames[recipe4], workshopName)
| |
| end
| |
| | |
| return recipes
| |
| end
| |
| | |
| | |
| | |
| ---
| |
| --- Transforms the originalWorkshopTable 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 originalWorkshopsTable table of CSV-based data, with header row, data rows
| |
| --- @param workshopsHeaderLookup table lookup table of headers to get indexes
| |
| --- @return table better structured with IDs as keys
| |
| local function restructureWorkshopsTable(originalWorkshopsTable, workshopsHeaderLookup)
| |
| | |
| -- A few constants we'll need only within 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_STORAGE_CAP = 14
| |
| | |
| mapNamesToIDs = {}
| |
| workshopsNames = {}
| |
| mapRecipeIDsToWorkshopNames = {}
| |
| | |
| local newWorkshopTable = {}
| |
| for _, originalWorkshop in ipairs (originalWorkshopsTable[DATA_ROWS]) do
| |
| | |
| -- Copy over the content, mapping unhelpful indexes into headers keys.
| |
| local newWorkshop = {}
| |
| newWorkshop[INDEX_ID] = originalWorkshop[INDEX_ORIGINAL_ID]
| |
| newWorkshop[INDEX_NAME] = originalWorkshop[INDEX_ORIGINAL_NAME]
| |
| newWorkshop[INDEX_DESCRIPTION] = originalWorkshop[INDEX_ORIGINAL_DESCRIPTION]
| |
| newWorkshop[INDEX_CATEGORY] = originalWorkshop[INDEX_ORIGINAL_CATEGORY]
| |
| newWorkshop[INDEX_SIZE_X] = tonumber(originalWorkshop[INDEX_ORIGINAL_SIZE_X])
| |
| newWorkshop[INDEX_SIZE_Y] = tonumber(originalWorkshop[INDEX_ORIGINAL_SIZE_Y])
| |
| newWorkshop[INDEX_CITY_SCORE] = tonumber(originalWorkshop[INDEX_ORIGINAL_CITY_SCORE])
| |
| newWorkshop[INDEX_MOVABLE] = originalWorkshop[INDEX_ORIGINAL_MOVABLE] == "TRUE"
| |
| newWorkshop[INDEX_ESSENTIAL] = originalWorkshop[INDEX_ORIGINAL_ESSENTIAL] == "TRUE"
| |
| newWorkshop[INDEX_STORAGE_CAP] = tonumber(originalWorkshop[INDEX_ORIGINAL_STORAGE_CAP])
| |
| newWorkshop[INDEX_CONSTRUCTION_TIME] = tonumber(originalWorkshop[INDEX_ORIGINAL_CONSTRUCTION_TIME])
| |
| | |
| newWorkshop[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalWorkshop, workshopsHeaderLookup)
| |
| newWorkshop[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalWorkshop, workshopsHeaderLookup)
| |
| newWorkshop[INDEX_RECIPES] = makeRecipesSubtable(newWorkshop[INDEX_NAME], originalWorkshop, workshopsHeaderLookup)
| |
| | |
| newWorkshopTable[ newWorkshop[INDEX_ID] ] = newWorkshop
| |
| | |
| table.insert(workshopsNames, newWorkshop[INDEX_NAME])
| |
| | |
| -- Also populate the map for looking up IDs with display names
| |
| mapNamesToIDs[newWorkshop[INDEX_NAME]] = newWorkshop[INDEX_ID]
| |
| end
| |
| | |
| return newWorkshopTable
| |
| end
| |
| | |
| | |
| | |
| local function load()
| |
| | |
| if not workshopsTable then
| |
| | |
| -- Utility module retrieves the data as basic, flat lua tables.
| |
| local originalWorkshopsTable, workshopsHeaderLookup = CsvUtils.extractTables(DATA_TEMPLATE_NAME)
| |
| | |
| -- Now restructure to be more conducive.
| |
| workshopsTable = restructureWorkshopsTable(originalWorkshopsTable, workshopsHeaderLookup)
| |
| end
| |
| end
| |
| | |
| --endregion
| |
| | |
| | |
| | |
| --region Public methods
| |
| | |
| --- DEPRECATED --- DO NOT USE ---
| |
| ---@deprecated
| |
| function WorkshopsData.getAllWorkshopRecipes(displayName)
| |
| | |
| if not displayName or displayName == "" then
| |
| error("Parameter is nil or empty for display name.")
| |
| end
| |
| | |
| load()
| |
| | |
| local id = mapNamesToIDs[displayName]
| |
| if not id then
| |
| return nil
| |
| end
| |
| | |
| local workshop = workshopsTable[id]
| |
| if not workshop then
| |
| return nil
| |
| end
| |
| | |
| return workshop[INDEX_RECIPES]
| |
| end
| |
| | |
| | |
| | |
| --- DEPRECATED --- DO NOT USE ---
| |
| ---@deprecated
| |
| function WorkshopsData.getWorkshopIcon(displayName)
| |
| | |
| if not displayName or displayName == "" then
| |
| error("Parameter is nil or empty for display name.")
| |
| end
| |
| | |
| load()
| |
| | |
| local id = mapNamesToIDs[displayName]
| |
| if not id then
| |
| return nil
| |
| end
| |
| | |
| -- the base string of the icon is the ID. It has to be not nil to
| |
| -- concatenate
| |
| return id .. "_icon.png"
| |
| end
| |
| | |
| | |
| | |
| --- DEPRECATED --- DO NOT USE ---
| |
| ---@deprecated
| |
| function WorkshopsData.getWorkshopNamesWithRecipeID(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
| |
| | |
| load()
| |
| | |
| return mapRecipeIDsToWorkshopNames[recipeID]
| |
| end
| |
| | |
| --endregion
| |
| | |
| | |
| | |
| --region Public Building interface
| |
| | |
| -- getID(displayName)
| |
| -- getCategory(id)
| |
| -- getCityScore(id)
| |
| -- getConstructionCosts(id) -- returns { [goodName] = stack size }
| |
| -- getConstructionTime(id)
| |
| -- getDescription(id)
| |
| -- getIcon(id)
| |
| -- isMovable(id)
| |
| -- getName(id)
| |
| -- getNumberOfWorkplaces(id)
| |
| -- getSize(id) -- returns string "X x Y"
| |
| -- getStorage(id)
| |
| | |
| function WorkshopsData.getID(displayName)
| |
| load()
| |
| return mapNamesToIDs[displayName]
| |
| end
| |
| | |
| function WorkshopsData.getCategory(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_CATEGORY]
| |
| end
| |
| | |
| function WorkshopsData.getCityScore(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_CITY_SCORE]
| |
| end
| |
| | |
| function WorkshopsData.getConstructionCosts(id)
| |
| | |
| load()
| |
| GoodsData = require("Module:GoodsData")
| |
| | |
| workshop = workshopsTable[id]
| |
| if not workshop then
| |
| return nil
| |
| end
| |
| | |
| local constructionCosts = {}
| |
| for _, stacks in ipairs(workshop[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 WorkshopsData.getConstructionTime(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_CONSTRUCTION_TIME]
| |
| end
| |
| | |
| function WorkshopsData.getDescription(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_DESCRIPTION]
| |
| end
| |
| | |
| function WorkshopsData.getIcon(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and id .. "_icon.png"
| |
| end
| |
| | |
| function WorkshopsData.isMovable(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_MOVABLE]
| |
| end
| |
| | |
| function WorkshopsData.getName(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_NAME]
| |
| end
| |
| | |
| function WorkshopsData.getNumberOfWorkplaces(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and #workshopsTable[id][INDEX_WORKPLACES]
| |
| end
| |
| | |
| function WorkshopsData.getSize(id)
| |
| load()
| |
| | |
| workshop = workshopsTable[id]
| |
| if not workshop then
| |
| return nil
| |
| end
| |
| | |
| return workshop[INDEX_SIZE_X] .. " × " .. workshop[INDEX_SIZE_Y]
| |
| end
| |
| | |
| function WorkshopsData.getStorage(id)
| |
| load()
| |
| return workshopsTable[id] ~= nil and workshopsTable[id][INDEX_STORAGE_CAP]
| |
| end
| |
| | |
| --endregion
| |
| | |
| return WorkshopsData | |