Module:MysteriesData
From Against the Storm Official Wiki
Documentation for this module may be created at Module:MysteriesData/doc
---
---
---@module MysteriesData
local MysteriesData = {}
--region Dependencies
local JsonUtils = require("Module:JsonUtils")
--endregion
--region Private constants
local JSON_DATA_PAGE_1 = "Template:Simple_Seasonal_Effects_json"
local JSON_DATA_PAGE_2 = "Template:Conditional_Seasonal_Effects_json"
local ID_PREFIX_TO_SKIP = "_TO DELETE"
-- Keys in both
local KEY_ID = "id"
local KEY_DISPLAY_NAME = "displayName"
local KEY_DESCRIPTION = "description"
local KEY_ICON = "icon"
local KEY_DIFFICULTY_COST = "difficultyCost"
local KEY_COST_LABEL = "costLabel"
local KEY_HOSTILITY_LEVEL = "hostilityLevel"
local KEY_SEASON = "season"
-- Keys in Simple Seasonal Effects
local KEY_STACKS_WITH_HOSTILITY = "stacksWithHostility"
local KEY_STACKS_WITH_YEAR = "stacksWithYear"
local KEY_REMOVE_AFTER_YEAR = "removeAfterYear"
local KEY_EFFECT_ID = "effectId"
local KEY_IN_CUSTOM_MODE = "isInCustomMode"
-- Keys in Conditional Seasonal Effects
local KEY_VILLAGER_PERK_ID = "villagerPerkId"
local KEY_CONDITIONS = "conditions"
-- Keys in subtable
local KEY_CONDITIONS_CATEGORY_ID = "categoryId"
local KEY_CONDITIONS_CATEGORY_NAME = "categoryDisplayName"
local KEY_CONDITIONS_AMOUNT = "amount"
local IS_VALID_KEY = {
[KEY_ID] = true,
[KEY_DISPLAY_NAME] = true,
[KEY_DESCRIPTION] = true,
[KEY_ICON] = true,
[KEY_DIFFICULTY_COST] = true,
[KEY_COST_LABEL] = true,
[KEY_HOSTILITY_LEVEL] = true,
[KEY_SEASON] = true,
[KEY_STACKS_WITH_HOSTILITY] = true,
[KEY_STACKS_WITH_YEAR] = true,
[KEY_REMOVE_AFTER_YEAR] = true,
[KEY_EFFECT_ID] = true,
[KEY_IN_CUSTOM_MODE] = true,
[KEY_VILLAGER_PERK_ID] = true,
[KEY_CONDITIONS] = true,
}
local IS_VALID_CONDITIONS_KEY = {
[KEY_CONDITIONS_CATEGORY_ID] = true,
[KEY_CONDITIONS_CATEGORY_NAME] = true,
[KEY_CONDITIONS_AMOUNT] = true,
}
local ERROR_MESSAGE_ID_MISSING = "MysteriesData requires the unique identifier for a Forest Mystery to get its data. Double-check what was passed as a parameter, because it was either missing, nil, or blank."
local ERROR_MESSAGE_UNEXPECTED_DUPLICATE_ID_ON_MERGE = "MysteriesData attempted to merge the two JSON data tables, however it found two records with the same id."
local ERROR_MESSAGE_OBJECT_MISSING_KEY_ID = "MysteriesData discovered a record in the data that did not contain the required field named 'id' required to index the data. Double-check that the data has not been modified from how it was originally supplied by the developers."
--endregion
--region Private member variables
local mysteriesData
local listOfIDs
--endregion
--region Private methods
--- This method uses the member variables mysteriesData and listOfIDs
---
---@param dataTable table the data table to loop through
local function saveRecordsByKey(dataTable)
-- Store the mysteries by their ID, but also create a simple array of all IDs since we're already looping over them all.
local i = #listOfIDs + 1
for _, record in ipairs(dataTable) do
local mysteryID = record[KEY_ID]
-- Validate the id
if not mysteryID or mysteryID == "" then
error(ERROR_MESSAGE_OBJECT_MISSING_KEY_ID)
else
-- Skip the records that the devs have marked for deletion
if string.sub(mysteryID, 1, #ID_PREFIX_TO_SKIP) ~= ID_PREFIX_TO_SKIP then
-- Validate that there are no duplicate IDs in the final table.
if mysteriesData[mysteryID] ~= nil then
error(ERROR_MESSAGE_UNEXPECTED_DUPLICATE_ID_ON_MERGE .. "\n id=" .. mysteryID)
else
-- Promote the id field to be the key.
mysteriesData[mysteryID] = record
listOfIDs[i] = mysteryID
i = i + 1
end
end
end
end
end
--- Checks whether the Mysteries data has been loaded yet. If not, handles that and the post-processing necessary.
---
local function loadData()
-- This function gets called any time the data could need to be loaded. Just return as quickly as possible if it's already loaded.
if not mysteriesData then
mysteriesData = {}
listOfIDs = {}
saveRecordsByKey(JsonUtils.convertJSONToLuaTable(JSON_DATA_PAGE_1))
saveRecordsByKey(JsonUtils.convertJSONToLuaTable(JSON_DATA_PAGE_2))
end
end
--- Retrieves exactly one mystery table from the data.
--- Throws an error if the parameter is missing, nil, or empty.
--- Retrieving data from outside this class should be done via the public getter methods, not by getting the entire record, so this is local.
---
---@param mysteryID string the unique identifier of a Mystery
---@return table the whole table row for that Mystery
local function getRecordWhereID(mysteryID)
if not mysteryID or "" == mysteryID then
error(ERROR_MESSAGE_ID_MISSING)
else
loadData()
return mysteriesData[mysteryID]
end
end
--- Retrieves the specified field from the specified mystery.
--- Standardizes the calls from simple getters.
--- Retrieving data from outside this class should be done via the public getter methods, not by getting the entire record, so this is local.
---
---@param mysteryID string the unique identifier of a Mystery
---@param fieldName string the desired constant field name
---@return any the value of the field--type is not known by this method
local function getFieldWhereID(mysteryID, fieldName)
if not IS_VALID_KEY[fieldName] then
return nil
end
local record = getRecordWhereID(mysteryID)
if not record then
return nil
else
return record[fieldName] or nil
end
end
--- Retrieves the specified condition from the specified mystery.
--- Standardizes the calls from simple getters.
--- Retrieving data from outside this class should be done via the public getter methods, not by getting the entire record, so this is local.
---
---@param mysteryID string the unique identifier of a Mystery
---@param conditionIndex number the index of the desired condition
---@return table the desired condition
local function getConditionWhereID(mysteryID, conditionIndex)
local conditions = getFieldWhereID(mysteryID, KEY_CONDITIONS)
if not conditions then
return nil
else
return conditions[conditionIndex] or nil
end
end
--- Retrieves the specified condition from the specified mystery.
--- Standardizes the calls from simple getters.
--- Retrieving data from outside this class should be done via the public getter methods, not by getting the entire record, so this is local.
---
---@param mysteryID string the unique identifier of a Mystery
---@param conditionIndex number the index of the desired condition
---@param fieldName string the desired constant field name
---@return table the desired condition
local function getFieldFromConditionWhereID(mysteryID, conditionIndex, fieldName)
if not IS_VALID_CONDITIONS_KEY[fieldName] then
return nil
end
local condition = getConditionWhereID(mysteryID, conditionIndex)
if not condition then
return nil
else
return condition[fieldName] or nil
end
end
--endregion
--region Public methods
---isIDValid
---@param mysteryID string a unique identifier
---@return boolean true if there is a record with the specified identifier
function MysteriesData.isIDValid(mysteryID)
local record = getRecordWhereID(mysteryID)
if not record then
return false
else
return true
end
end
---getName
---@param mysteryID string the unique identifier of a mystery
---@return string the name of the mystery
function MysteriesData.getName(mysteryID)
return getFieldWhereID(mysteryID, KEY_DISPLAY_NAME)
end
---getDescription
---@param mysteryID string the unique identifier of a mystery
---@return string the description of the mystery
function MysteriesData.getDescription(mysteryID)
return getFieldWhereID(mysteryID, KEY_DESCRIPTION)
end
---getIcon
---@param mysteryID string the unique identifier of a mystery
---@return string the icon of the mystery
function MysteriesData.getIcon(mysteryID)
return getFieldWhereID(mysteryID, KEY_ICON)
end
---getCostDifficulty
---@param mysteryID string the unique identifier of a mystery
---@return number the difficulty cost of the mystery
function MysteriesData.getCostDifficulty(mysteryID)
return getFieldWhereID(mysteryID, KEY_DIFFICULTY_COST)
end
---getCostLabel
---@param mysteryID string the unique identifier of a mystery
---@return string the label of the cost of the mystery
function MysteriesData.getCostLabel(mysteryID)
return getFieldWhereID(mysteryID, KEY_COST_LABEL)
end
---getHostilityLevel
---@param mysteryID string the unique identifier of a mystery
---@return number the hostility level when the mystery kicks in
function MysteriesData.getHostilityLevel(mysteryID)
return getFieldWhereID(mysteryID, KEY_HOSTILITY_LEVEL)
end
---getSeason
---@param mysteryID string the unique identifier of a mystery
---@return number the season when the mystery is in effect
function MysteriesData.getSeason(mysteryID)
return getFieldWhereID(mysteryID, KEY_SEASON)
end
---isStacksWithHostility
---@param mysteryID string the unique identifier of a mystery
---@return boolean true if the mystery gets worse with higher hostility
function MysteriesData.isStacksWithHostility(mysteryID)
return getFieldWhereID(mysteryID, KEY_STACKS_WITH_HOSTILITY) or false
end
---isStacksWithYear
---@param mysteryID string the unique identifier of a mystery
---@return boolean true if the mystery gets worse with each year
function MysteriesData.isStacksWithYear(mysteryID)
return getFieldWhereID(mysteryID, KEY_STACKS_WITH_YEAR) or false
end
---isRemovedAfterYear
---@param mysteryID string the unique identifier of a mystery
---@return boolean true if the mystery is removed after one year
function MysteriesData.isRemovedAfterYear(mysteryID)
return getFieldWhereID(mysteryID, KEY_REMOVE_AFTER_YEAR) or false
end
---getEffectID
---@param mysteryID string the unique identifier of a mystery
---@return string the identifier of the effect applied to the settlement
function MysteriesData.getEffectID(mysteryID)
return getFieldWhereID(mysteryID, KEY_EFFECT_ID)
end
---isInCustomMode
---@param mysteryID string the unique identifier of a mystery
---@return boolean true if the mystery is available in custom mode
function MysteriesData.isInCustomMode(mysteryID)
return getFieldWhereID(mysteryID, KEY_IN_CUSTOM_MODE) or false
end
---getVillagerPerkID
---@param mysteryID string the unique identifier of a mystery
---@return string the identifier of the effect applied to each villager
function MysteriesData.getVillagerPerkID(mysteryID)
return getFieldWhereID(mysteryID, KEY_VILLAGER_PERK_ID)
end
---getNumberOfConditions
---@param mysteryID string the unique identifier of a mystery
---@return number how many conditions for the specified mystery
function MysteriesData.getNumberOfConditions(mysteryID)
local conditions = getFieldWhereID(mysteryID, KEY_CONDITIONS)
if not conditions then
return 0
else
return #conditions
end
end
---getCategoryIDForCondition
---@param mysteryID string the unique identifier of a mystery
---@param conditionIndex number the index of the desired condition
---@return string the identifier of the category of the condition
function MysteriesData.getCategoryIDForCondition(mysteryID, conditionIndex)
return getFieldFromConditionWhereID(mysteryID, conditionIndex, KEY_CONDITIONS_CATEGORY_ID)
end
---getCategoryNameForCondition
---@param mysteryID string the unique identifier of a mystery
---@param conditionIndex number the index of the desired condition
---@return string the name of the category of the condition
function MysteriesData.getCategoryNameForCondition(mysteryID, conditionIndex)
return getFieldFromConditionWhereID(mysteryID, conditionIndex, KEY_CONDITIONS_CATEGORY_ID)
end
---getAmountForCondition
---@param mysteryID string the unique identifier of a mystery
---@param conditionIndex number the index of the desired condition
---@return number the number required to meet the condition
function MysteriesData.getAmountForCondition(mysteryID, conditionIndex)
return getFieldFromConditionWhereID(mysteryID, conditionIndex, KEY_CONDITIONS_AMOUNT)
end
function MysteriesData.getAllMysteryIDs()
loadData()
return listOfIDs
end
--endregion
return MysteriesData


