Module:WorkshopsData: Difference between revisions

From Against the Storm Official Wiki
m (adding deprecated flags next to public methods that shouldn't be used anymore)
(Updating to new data model!)
Tag: Replaced
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

Revision as of 01:31, 30 October 2024

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

---@module 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"


local workshopsDataModel = BaseDataModel.new(DATA_FILE)


-- Return the instance for workshops data, not this module itself!
return workshopsDataModel