Module:CampsData: Difference between revisions
From Against the Storm Official Wiki
(Created to manage data from gathering camps and woodcutters camp) |
m (Now stores the right fields as actual numbers instead of strings of numbers) |
||
Line 193: | Line 193: | ||
-- don't add subtables that would just contain nils | -- don't add subtables that would just contain nils | ||
local requiredGoods = { | local requiredGoods = { | ||
number1 and id1 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = number1, [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id1 } or nil, | 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] = number2, [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id2 } 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] = number3, [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id3 } or nil | number3 and id3 and { [INDEX_CONSTRUCTION_GOODS_STACK_SIZE] = tonumber(number3), [INDEX_CONSTRUCTION_GOODS_GOOD_ID] = id3 } or nil | ||
} | } | ||
Line 278: | Line 278: | ||
if originalGoodID and originalGoodID ~= "" then | if originalGoodID and originalGoodID ~= "" then | ||
newRecipe[INDEX_RECIPE_GOOD_ID] = originalGoodID | newRecipe[INDEX_RECIPE_GOOD_ID] = originalGoodID | ||
newRecipe[INDEX_RECIPE_GRADE] = originalCamp[originalGradeIndex]:match(PATTERN_CAPTURE_END_NUMBER) | newRecipe[INDEX_RECIPE_GRADE] = tonumber(originalCamp[originalGradeIndex]:match(PATTERN_CAPTURE_END_NUMBER)) | ||
newRecipe[INDEX_RECIPE_GATHERING_TIME] = originalCamp[originalGatheringTimeIndex] | newRecipe[INDEX_RECIPE_GATHERING_TIME] = tonumber(originalCamp[originalGatheringTimeIndex]) | ||
table.insert(recipes,newRecipe) | table.insert(recipes,newRecipe) | ||
Line 333: | Line 333: | ||
newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] | newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] | ||
newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] | newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] | ||
newCamp[INDEX_SIZE_X] = originalCamp[INDEX_ORIGINAL_SIZE_X] | newCamp[INDEX_SIZE_X] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_X]) | ||
newCamp[INDEX_SIZE_Y] = originalCamp[INDEX_ORIGINAL_SIZE_Y] | newCamp[INDEX_SIZE_Y] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_Y]) | ||
newCamp[INDEX_CITY_SCORE] = originalCamp[INDEX_ORIGINAL_CITY_SCORE] | newCamp[INDEX_CITY_SCORE] = tonumber(originalCamp[INDEX_ORIGINAL_CITY_SCORE]) | ||
newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] == "True" | newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] == "True" | ||
newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] == "True" | newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] == "True" | ||
newCamp[INDEX_AREA] = originalCamp[INDEX_ORIGINAL_AREA] | newCamp[INDEX_AREA] = tonumber(originalCamp[INDEX_ORIGINAL_AREA]) | ||
newCamp[INDEX_STORAGE] = originalCamp[INDEX_ORIGINAL_STORAGE] | newCamp[INDEX_STORAGE] = tonumber(originalCamp[INDEX_ORIGINAL_STORAGE]) | ||
newCamp[INDEX_CONSTRUCTION_TIME] = originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME] | newCamp[INDEX_CONSTRUCTION_TIME] = tonumber(originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME]) | ||
newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) | newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) | ||
Line 390: | Line 390: | ||
newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] | newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] | ||
newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] | newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] | ||
newCamp[INDEX_SIZE_X] = originalCamp[INDEX_ORIGINAL_SIZE_X] | newCamp[INDEX_SIZE_X] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_X]) | ||
newCamp[INDEX_SIZE_Y] = originalCamp[INDEX_ORIGINAL_SIZE_Y] | newCamp[INDEX_SIZE_Y] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_Y]) | ||
newCamp[INDEX_CONSTRUCTION_TIME] = originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME] | newCamp[INDEX_CONSTRUCTION_TIME] = tonumber(originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME]) | ||
newCamp[INDEX_CITY_SCORE] = originalCamp[INDEX_ORIGINAL_CITY_SCORE] | newCamp[INDEX_CITY_SCORE] = tonumber(originalCamp[INDEX_ORIGINAL_CITY_SCORE]) | ||
newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] | newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] | ||
newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] | newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] | ||
newCamp[INDEX_AREA] = originalCamp[INDEX_ORIGINAL_AREA] | newCamp[INDEX_AREA] = tonumber(originalCamp[INDEX_ORIGINAL_AREA]) | ||
newCamp[INDEX_STORAGE] = originalCamp[INDEX_ORIGINAL_STORAGE] | newCamp[INDEX_STORAGE] = tonumber(originalCamp[INDEX_ORIGINAL_STORAGE]) | ||
newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) | newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) |
Revision as of 04:14, 26 November 2023
Documentation for this module may be created at Module:CampsData/doc
--- --- Module for compiling camps 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 = CampsData.getCampArea(campName) --- --- This will return the working radius of the camp. It is preferable to --- call the getter methods with the name of the camp rather than --- retrieving the entire data record for the camp. This way your code --- stays protected from variations in this module. These getter methods are --- called with the plain-language display name of the camp, as spelled --- in the game, including punctuation. --- --- * longDescription = CampsData.getCampDescription(campName) --- * constructionCategory = CampsData.getCampCategory(campName) --- * sizeX, sizeY = CampsData.getCampSize(campName) --- * requiredGoodID, stackSize = CampsData.getCampRequiredGood(campName, requirementIndex) --- * timeSeconds = CampsData.getCampConstructionTime(campName) --- * cityScore = CampsData.getCampCityScore(campName) --- * isMovable = CampsData.isCampMovable(campName) --- * isInitiallyEssential = CampsData.isCampInitiallyEssential(campName) --- * radius = CampsData.getCampArea(campName) --- * storageCapacity = CampsData.getCampStorage(campName) --- * workplaces = CampsData.getCampNumberOfWorkplaces(campName) --- * goodID = CampsData.getCampRecipeProduct(campName, recipeIndex) --- * gradeStars, gatheringSeconds = CampsData.getCampRecipeStats(campName, recipeIndex) --- * iconFilename = CampsData.getCampIcon(campName) --- --- And the following getter methods are all called with the camp's ID. --- You will have an ID instead of a display name when dealing with other data --- like recipes, species, etc. --- --- * name = CampsData.getCampNameByID(campID) --- * iconFilename = CampsData.getCampIconByID(campID) --- --- You can retrieve all camps that produce a certain good (with any grade of --- efficiency) with the following getter: --- --- campNamesTable = CampsData.getCampNamesWithRecipeProductID(productID) --- --- There are methods to retrieve the lists of required goods, workplaces, and --- recipes, but it is advised to instead use the getter methods --- getCampRequiredGood, getCampNumberOfWorkplaces, and --- getCampRecipe with an index desired, which is good for loops. If --- you must get the tables, there are three "getAll" methods: --- --- * requiredGoodsTable = CampsData.getAllCampRequiredGoods(campName) --- * workplacesTable = CampsData.getAllCampWorkplaces(campName) --- * recipesTable = CampsData.getAllCampRecipes(campName) --- --- As a last resort, or if you need to transform the data structure, you can --- call the method getAllDataForCamp(campName) or --- getAllDataForCampByID(campID). These return a whole record --- from the data table corresponding to the required display name or ID. --- --- The data table for camps has the following structure: --- --- campsTable = { --- ["camp1_ID"] = { --- ["id"] = "camp1_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", --- "grade" = 9, number of stars, --- "gatheringTime" = 99, seconds, --- } --- ["good2_ID"] = { ... } --- ["good3_ID"] = { ... } --- ["good4_ID"] = ... or missing if fewer --- } --- }, --- ["camp2_ID"] = { --- ... --- }, --- ["camp3_ID"] = { --- ... --- }, --- ... --- } --- --- @module CampsData local CampsData = {} local CsvUtils = require("Module:CsvUtils") --region Private member variables --- Main data tables, like this: table[ID] = table containing data for that ID local campsTable --- Lookup map. Built once and reused on subsequent calls within this session, --- like this: table[display name] = campID local mapNamesToIDs --- Lookup map. Built once and reused on subsequent calls within this session, --- like this: table[goodID] = { campName1, campName2, ... } local mapRecipeGoodIDsToCampNames --endregion --region Private constants local GATHERER_HUTS_DATA_TEMPLATE_NAME = "Template:GathererHuts_csv" local CAMPS_DATA_TEMPLATE_NAME = "Template:Camps_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_CONSTRUCTION_TIME = "constructionTime" local INDEX_CITY_SCORE = "cityScore" local INDEX_MOVABLE = "movable" local INDEX_INITIALLY_ESSENTIAL = "initiallyEssential" local INDEX_AREA = "area" local INDEX_STORAGE = "storage" 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_GOOD_ID = "goodID" local INDEX_RECIPE_GRADE = "grade" local INDEX_RECIPE_GATHERING_TIME = "gatheringTime" local PATTERN_SPLIT_STACK_AND_ID = "(%d+)%s([%[%]%s%a]+)" local PATTERN_CAPTURE_END_NUMBER = "Grade(%d)" local WOODCUTTER_ID_WOOD = "[Mat Raw] Wood" local WOODCUTTER_GRADE = 3 local WOODCUTTER_GATHERING_TIME = 2 --endregion --region Private methods --- --- Creates a new subtable containing the construction goods required for the --- specified camp. --- --- @param originalCamp table camp data record from which to make subtable --- @param campsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the required construction goods local function makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) -- 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 = campsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "1"] local requiredIndex2 = campsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "2"] local requiredIndex3 = campsHeaderLookup[REQ_GOOD_HEADER_BASE_STRING .. "3"] local number1, id1 = originalCamp[requiredIndex1]:match(PATTERN_SPLIT_STACK_AND_ID) local number2, id2 = originalCamp[requiredIndex2]:match(PATTERN_SPLIT_STACK_AND_ID) local number3, id3 = originalCamp[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 --- camp. --- --- @param originalCamp table camp data record from which to make subtable --- @param campsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the workplaces local function makeWorkplacesSubtable(originalCamp, campsHeaderLookup) -- A constant we'll need only within this function. local WORKPLACE_HEADER_BASE_STRING = "workplace" -- Copy the originals directly into a subtable. local workplaceIndex1 = campsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "1"] local workplaceIndex2 = campsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "2"] local workplaceIndex3 = campsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "3"] local workplaceIndex4 = campsHeaderLookup[WORKPLACE_HEADER_BASE_STRING .. "4"] local workplace1 = originalCamp[workplaceIndex1] local workplace2 = originalCamp[workplaceIndex2] local workplace3 = originalCamp[workplaceIndex3] local workplace4 = originalCamp[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 --- camp. --- --- @param campName string the display name of the camp --- @param originalCamp table camp data record from which to make subtable --- @param campsHeaderLookup table header lookup built from the CSV data --- @return table subtable with the recipe IDs local function makeRecipesSubtable(campName, originalCamp, campsHeaderLookup) -- A constant we'll need only within this function. local MAX_RECIPES = 3 local LOOKUP_RECIPE_BASE_STRING = "recipe" local LOOKUP_GOOD_BASE_STRING = "SeekedDeposits" local LOOKUP_GRADE_BASE_STRING = "Grade" local LOOKUP_GATHERING_TIME_BASE_STRING = "GatheringTime" if not mapRecipeGoodIDsToCampNames then mapRecipeGoodIDsToCampNames = {} end -- Copy the originals directly into a subtable. local recipes = {} for i = 1, MAX_RECIPES do -- A subtable for this recipe newRecipe = {} local originalGoodIndex = campsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i .. LOOKUP_GOOD_BASE_STRING] local originalGradeIndex = campsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i .. LOOKUP_GRADE_BASE_STRING] local originalGatheringTimeIndex = campsHeaderLookup[LOOKUP_RECIPE_BASE_STRING .. i .. LOOKUP_GATHERING_TIME_BASE_STRING] local originalGoodID = originalCamp[originalGoodIndex] -- Skip blank recipes. if originalGoodID and originalGoodID ~= "" then newRecipe[INDEX_RECIPE_GOOD_ID] = originalGoodID newRecipe[INDEX_RECIPE_GRADE] = tonumber(originalCamp[originalGradeIndex]:match(PATTERN_CAPTURE_END_NUMBER)) newRecipe[INDEX_RECIPE_GATHERING_TIME] = tonumber(originalCamp[originalGatheringTimeIndex]) table.insert(recipes,newRecipe) if not mapRecipeGoodIDsToCampNames[originalGoodID] then mapRecipeGoodIDsToCampNames[originalGoodID] = {} end table.insert(mapRecipeGoodIDsToCampNames[originalGoodID], campName) end end return recipes end --- --- Transforms the originalCampsTable 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 originalCampsTable table of CSV-based data, with header row, data rows --- @param campsHeaderLookup table lookup table of headers to get indexes --- @return table better structured with IDs as keys local function restructureGathererHuts(originalCampsTable, campsHeaderLookup) -- 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 newCampsTable = {} for _, originalCamp in ipairs(originalCampsTable[DATA_ROWS]) do -- Copy over the content, mapping unhelpful indexes into headers keys. local newCamp = {} newCamp[INDEX_ID] = originalCamp[INDEX_ORIGINAL_ID] newCamp[INDEX_NAME] = originalCamp[INDEX_ORIGINAL_NAME] newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] newCamp[INDEX_SIZE_X] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_X]) newCamp[INDEX_SIZE_Y] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_Y]) newCamp[INDEX_CITY_SCORE] = tonumber(originalCamp[INDEX_ORIGINAL_CITY_SCORE]) newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] == "True" newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] == "True" newCamp[INDEX_AREA] = tonumber(originalCamp[INDEX_ORIGINAL_AREA]) newCamp[INDEX_STORAGE] = tonumber(originalCamp[INDEX_ORIGINAL_STORAGE]) newCamp[INDEX_CONSTRUCTION_TIME] = tonumber(originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME]) newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) newCamp[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalCamp, campsHeaderLookup) newCamp[INDEX_RECIPES] = makeRecipesSubtable(newCamp[INDEX_NAME], originalCamp, campsHeaderLookup) newCampsTable[ newCamp[INDEX_ID] ] = newCamp -- Also populate the map for looking up IDs with display names mapNamesToIDs[ newCamp[INDEX_NAME] ] = newCamp[INDEX_ID] end return newCampsTable end --- --- Creates a row for the only record in this table, the woodcutters' camp, in --- a way that matches the other gathering camps or huts. --- ---@param originalCampsTable table the one-record table ---@param campsHeaderLookup table lookup table of headers to get indexes ---@return table one record to add to campsTable local function addWoodcuttersCampRow(originalCampsTable, campsHeaderLookup) -- 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 -- The woodcutters' camp is at index 1 in the data rows. local originalCamp = originalCampsTable[DATA_ROWS][1] local newCamp = {} newCamp[INDEX_ID] = originalCamp[INDEX_ORIGINAL_ID] newCamp[INDEX_NAME] = originalCamp[INDEX_ORIGINAL_NAME] newCamp[INDEX_DESCRIPTION] = originalCamp[INDEX_ORIGINAL_DESCRIPTION] newCamp[INDEX_CATEGORY] = originalCamp[INDEX_ORIGINAL_CATEGORY] newCamp[INDEX_SIZE_X] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_X]) newCamp[INDEX_SIZE_Y] = tonumber(originalCamp[INDEX_ORIGINAL_SIZE_Y]) newCamp[INDEX_CONSTRUCTION_TIME] = tonumber(originalCamp[INDEX_ORIGINAL_CONSTRUCTION_TIME]) newCamp[INDEX_CITY_SCORE] = tonumber(originalCamp[INDEX_ORIGINAL_CITY_SCORE]) newCamp[INDEX_MOVABLE] = originalCamp[INDEX_ORIGINAL_MOVABLE] newCamp[INDEX_INITIALLY_ESSENTIAL] = originalCamp[INDEX_ORIGINAL_ESSENTIAL] newCamp[INDEX_AREA] = tonumber(originalCamp[INDEX_ORIGINAL_AREA]) newCamp[INDEX_STORAGE] = tonumber(originalCamp[INDEX_ORIGINAL_STORAGE]) newCamp[INDEX_REQUIRED_GOODS] = makeRequiredGoodsSubtable(originalCamp, campsHeaderLookup) newCamp[INDEX_WORKPLACES] = makeWorkplacesSubtable(originalCamp, campsHeaderLookup) -- Create a subtable of one recipe. This part of the record does not exist -- in the data and needs to be fabricated to match the other camps. local oneRecipe = {} local recipe = {} recipe[INDEX_RECIPE_GOOD_ID] = WOODCUTTER_ID_WOOD recipe[INDEX_RECIPE_GRADE] = WOODCUTTER_GRADE recipe[INDEX_RECIPE_GATHERING_TIME] = WOODCUTTER_GATHERING_TIME table.insert(oneRecipe, recipe) newCamp[INDEX_RECIPES] = oneRecipe campsTable[newCamp[INDEX_ID]] = newCamp -- This is the only camp that harvests wood. mapRecipeGoodIDsToCampNames[WOODCUTTER_ID_WOOD] = {} table.insert(mapRecipeGoodIDsToCampNames[WOODCUTTER_ID_WOOD], newCamp[INDEX_NAME]) 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 originalGathererHutsTable, gathererHutsHeaderLookup = CsvUtils.extractTables(GATHERER_HUTS_DATA_TEMPLATE_NAME) -- Now restructure to be more conducive. campsTable = restructureGathererHuts(originalGathererHutsTable, gathererHutsHeaderLookup) -- Now append the woodcutters' camp and fake recipe records for its local originalCampsTable, campsHeaderLookup = CsvUtils.extractTables(CAMPS_DATA_TEMPLATE_NAME) -- Now restructure and append a row for the camp, since there is only one record right now. addWoodcuttersCampRow(originalCampsTable, campsHeaderLookup) end --- --- Uses the display name, which people are more familiar with, to find the --- encoded ID of the camp. Useful for retrieving the data that is --- indexed by ID. --- --- Returns nil if the camp with the specified name is not found. --- --- @param displayName string plain-language name of the camp to find --- @return string ID of the camp found, or nil if not found local function findCampIDByName(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 camp's name: " .. displayName .. ".") end if not campsTable then loadData() end return mapNamesToIDs[displayName] end --endregion --region Public methods --- Retrieve the whole table of data for the specified camp. 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 camp cannot be found. --- --- @param campID string ID of the camp --- @return table containing the data with key-value pairs, or nil if not found function CampsData.getAllDataForCampByID(campID) -- At runtime, this should never be nil or empty. if not campID or campID == "" then error("Parameter is nil or empty for the camp's ID.") end if not campsTable then loadData() end return campsTable[campID] end --- Retrieve the whole table of data for the specified camp. 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 camp cannot be found. --- --- @param displayName string plain language name of the camp --- @return table containing the data with key-value pairs, or nil if not found function CampsData.getAllDataForCamp(displayName) -- At runtime, this should never be nil or empty. if not displayName or displayName == "" then error("Parameter is nil or empty for the camp's name.") end if not campsTable then loadData() end local campID = findCampIDByName(displayName) if not campID then return nil end return campsTable[campID] end --- --- Retrieves the ID for the camp specified by its plain language --- display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return string the ID of the specified camp function CampsData.getCampID(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_ID] end --- --- Retrieves the description for the camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return string the in-game description of the specified camp function CampsData.getCampDescription(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_DESCRIPTION] end --- --- Retrieves the construction toolbar category for the camp specified --- by its plain language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return string the category of the specified camp function CampsData.getCampCategory(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_CATEGORY] end --- --- Retrieves the 2x2 size for the camp specified by its plain language --- display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number the X-size of the camp ---@return number the Y-size of the camp function CampsData.getCampSize(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_SIZE_X], camp[INDEX_SIZE_Y] end --- --- Retrieves the goods required for construction for the camp 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 camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return table of required goods function CampsData.getAllCampRequiredGoods(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_REQUIRED_GOODS] end --- --- Retrieves the specified required construction good for the camp --- specified by its plain language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@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 CampsData.getCampRequiredGood(displayName, requirementIndex) local requiredGoods = CampsData.getAllCampRequiredGoods(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 camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number of seconds it takes to construct the camp function CampsData.getCampConstructionTime(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_CONSTRUCTION_TIME] end --- --- Retrieves the city score awarded for the camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number of points for city score from having the camp function CampsData.getCampCityScore(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_CITY_SCORE] end --- --- Retrieves whether the camp specified by its plain language display --- name can be moved, at any cost. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return boolean of whether the camp can be moved function CampsData.isCampMovable(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_MOVABLE] end --- --- Retrieves whether the camp specified by its plain language display --- name has an essential starting blueprint for a new game profile. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return boolean of whether the camp's blueprint is essential function CampsData.isCampInitiallyEssential(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_INITIALLY_ESSENTIAL] end --- --- Retrieves the gathering area for the camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number of tiles of gathering radius function CampsData.getCampArea(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_AREA] end --- --- Retrieves the storage capacity of the camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number of storage capacity at the camp function CampsData.getCampStorage(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_STORAGE] end --- --- Retrieves the table of workplaces for the camp specified by its --- plain language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return table of workplaces function CampsData.getAllCampWorkplaces(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_WORKPLACES] end --- --- Retrieves the number of workplaces for the camp specified by its --- plain language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return number of workplaces function CampsData.getCampNumberOfWorkplaces(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return #camp[INDEX_WORKPLACES] end --- --- Retrieves the table of recipes for the camp 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 camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return table of recipe IDs function CampsData.getAllCampRecipes(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end return camp[INDEX_RECIPES] end --- --- Retrieves the product's ID and the stack size produced by the specified --- recipe at the camp specified by its plain language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@param recipeIndex number the index of the recipe ID ---@return string the ID of the good produced by the specified recipe function CampsData.getCampRecipeProduct(displayName, recipeIndex) local recipes = CampsData.getAllCampRecipes(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] end --- --- Retrieves the other stats for specified recipe at the camp specified by --- its plain language display name: the efficiency grade and the gathering --- time. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@param recipeIndex number the index of the recipe ID ---@return number of stars, the grade of the specified recipe ---@return number of seconds for gathering function CampsData.getCampRecipeStats(displayName, recipeIndex) local recipes = CampsData.getAllCampRecipes(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_GATHERING_TIME] end --- --- Retrieves the icon filename for the camp specified by its plain --- language display name. --- --- Returns nil if the camp named was not found. --- ---@param displayName string the plain language name of the camp ---@return string the camp's icon filename, including the extension function CampsData.getCampIcon(displayName) local camp = CampsData.getAllDataForCamp(displayName) if not camp then return nil end -- the base string of the icon is the ID. It has to be not nil to -- concatenate if camp[INDEX_ID] then return camp[INDEX_ID] .. "_icon.png" else return nil end end --- --- Retrieves the plain language display name for the camp specified by --- its ID. --- --- Returns nil if the camp named was not found. --- ---@param campID string the ID of the camp ---@return string the camp's name as seen in-game function CampsData.getCampNameByID(campID) local camp = CampsData.getAllDataForCampByID(campID) if not camp then return nil end return camp[INDEX_NAME] end --- --- Retrieves the icon filename for the the camp specified by its ID. --- --- Returns nil if the camp named was not found. --- ---@param campID string the ID of the camp ---@return string the camp's icon filename, including the extension function CampsData.getCampIconByID(campID) local camp = CampsData.getAllDataForCampByID(campID) if not camp then return nil end -- the base string of the icon is the ID. It has to be not nil to -- concatenate if camp[INDEX_ID] then return camp[INDEX_ID] .. "_icon.png" else return nil end end --- --- Retrieve all display names of camps that have a recipe that produces the --- specified product ID. --- ---@param productID string the ID of the good to produce ---@return table of camp names that produce the specified good function CampsData.getCampNamesWithRecipeProductID(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 campsTable then loadData() end return mapRecipeGoodIDsToCampNames[productID] end --endregion return CampsData