Module:OrdersData
From Against the Storm Official Wiki
Documentation for this module may be created at Module:OrdersData/doc
---@class OrdersData
local OrdersData = {}
--region Dependencies
local JsonUtils = require("Module:JsonUtils")
--endregion
--region Private member variables
local ordersData
--endregion
--region Public enums
OrdersData.TIER_I = "i"
OrdersData.TIER_II = "ii"
OrdersData.TIER_III = "iii"
OrdersData.TIER_IV = "iv"
OrdersData.NONE = "none"
OrdersData.TRUE = "true"
OrdersData.FALSE = "false"
OrdersData.DIFFICULTY_EASY = "easy"
OrdersData.DIFFICULTY_MEDIUM = "medium"
OrdersData.DIFFICULTY_HARD = "hard"
--endregion
--region Private constants
local DATA_FILE = "Module:OrdersData/Orders.json"
local SWITCH_TIERS_CHECK = {
[OrdersData.TIER_I] = true,
[OrdersData.TIER_II] = true,
[OrdersData.TIER_III] = true,
[OrdersData.TIER_IV] = true,
}
local NORMALIZED_TEXT_TIERS = {
["i"] = OrdersData.TIER_I,
["ii"] = OrdersData.TIER_II,
["iii"] = OrdersData.TIER_III,
["iv"] = OrdersData.TIER_IV,
}
local SWITCH_TIMED_CHECK = {
[OrdersData.TRUE] = true,
[OrdersData.FALSE] = true,
}
local NORMALIZED_TEXT_TIMED = {
["true"] = OrdersData.TRUE,
["false"] = OrdersData.FALSE,
}
local TIERS_SORT_ORDER = {
[OrdersData.TIER_I] = 1,
[OrdersData.TIER_II] = 2,
[OrdersData.TIER_III] = 3,
[OrdersData.TIER_IV] = 4,
}
local SWITCH_DIFFICULTIES_CHECK = {
[OrdersData.DIFFICULTY_EASY] = true,
[OrdersData.DIFFICULTY_MEDIUM] = true,
[OrdersData.DIFFICULTY_HARD] = true,
}
local NORMALIZED_TEXT_DIFFICULTIES = {
["easy"] = OrdersData.DIFFICULTY_EASY,
["medium"] = OrdersData.DIFFICULTY_MEDIUM,
["hard"] = OrdersData.DIFFICULTY_HARD,
}
--region Private constants
local SCHEMA = {
ID = "id",
NAME = "displayName",
TIERS = "tiers",
TIMED_ORDER = "isTimed",
TIME_LIMIT = "timeToFail",
OBJECTIVES = "objectivesSets",
DIFFICULTY = "difficulty",
REQUIREMENTS = "logics",
REQUIREMENT_TYPE = "type",
REQUIREMENT_AMOUNT = "amount",
REQUIREMENT_ICON = "icon",
REQUIREMENT_NAME = "displayName",
REQUIREMENT_DESCRIPTION = "description",
REWARD_1 = "rewards1",
REWARD_2 = "rewards2",
REWARD_3 = "rewards3",
REWARD_ID = "id",
REWARD_NAME = "displayName",
REWARD_DESCRIPTION = "description",
IS_TIMED_TRUE = "true",
IS_TIMED_FALSE = "non-timed",
}
local ERROR_MESSAGE_INVALID_FUNCTION_CALL = "Invalid function call to OrdersData"
--endregion
--region Localization string constants
--endregion
--region Private methods
local function loadData()
if not ordersData then
local newData = JsonUtils.convertJSONToLuaTable(DATA_FILE)
--Promote the ID to be the key of the main data table.
ordersData = {}
for _, order in ipairs(newData) do
ordersData[order[SCHEMA.ID]] = order
end
end
end
local function getOrderFieldByID(orderID, fieldName)
loadData()
local order = ordersData[orderID]
if not order then
return nil
else
return order[fieldName]
end
end
local function getObjectivesArrayByID(orderID)
loadData()
local objectives = getOrderFieldByID(orderID, SCHEMA.OBJECTIVES)
if not objectives then
return nil
else
return objectives
end
end
local function getDifficultiesArrayByID(orderID)
loadData()
local objectives = getObjectivesArrayByID(orderID)
if not objectives then
return nil
else
local difficultyList = {}
for i, obj in pairs(objectives) do
for j, value in pairs(objectives[i]) do
if j == SCHEMA.DIFFICULTY then
table.insert(difficultyList,value)
end
end
end
return difficultyList
end
end
local function getRequirementsArrayByID(orderID)
loadData()
local objectives = getObjectivesArrayByID(orderID)
if not objectives then
return nil
else
local requirementsList = {}
for i, obj in pairs(objectives) do
for j, value in pairs(objectives[i]) do
if j == SCHEMA.REQUIREMENTS then
table.insert(requirementsList,value)
end
end
end
return requirementsList
end
end
local function getRequirementByIDAndRow(orderID, rowNum)
local requirements = getRequirementsArrayByID(orderID)
local requirement = requirements[rowNum]
if not requirement then
return nil
else
return requirement
end
end
local function getSpecificRequirementByIDAndRowAndSubrow(orderID, rowNum, subrow)
local specific = getRequirementByIDAndRow(orderID, rowNum)[subrow]
if not specific then
return nil
else
return specific
end
end
local function getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, field)
local specificField = getSpecificRequirementByIDAndRowAndSubrow(orderID, rowNum, subrow)[field]
if not specificField then
return nil
else
return specificField
end
end
local function makeFormattedRequirementByIDAndRowNumAndSubrow(orderID, rowNum, subrow)
local requirementType = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_TYPE)
if not requirementType then
return nil
end
---It's better if the following parts are reworked to not be hardcoded in the future
---For Good Delivery requirements, use the name instead of description
if requirementType == "Good" then
local requirementString = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_NAME)
requirementString = requirementString .. ". "
return requirementString
end
---For owning at least X buildings of type Y, use "Own at least [X] [Y](s)."
if requirementType == "BuildingOwning" then
local requirementString = "Own at least "
local requirementAmount = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_AMOUNT)
local requirementBuildingName = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_NAME)
if tonumber(requirementAmount) > 1 then
requirementString = requirementString .. requirementAmount .. " " .. requirementBuildingName .. "s."
else
requirementString = requirementString .. requirementAmount .. " " .. requirementBuildingName .. ". "
end
return requirementString
end
---For producing at least X goods, use "Produce [X] Goods in any capable building."
if requirementType == "GoodProduced" then
local requirementString = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_DESCRIPTION)
local requirementAmount = getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_AMOUNT)
requirementString = requirementString:sub(1, 7) .. " " .. requirementAmount .. requirementString:sub(8)
return requirementString
end
---In any other case, the description is already properly formatted
return getSpecificRequirementFieldByIDAndRowAndSubrowAndField(orderID, rowNum, subrow, SCHEMA.REQUIREMENT_DESCRIPTION)
end
local function getRewardsByID(orderID)
local reward1 = getOrderFieldByID(orderID, SCHEMA.REWARD_1)
local reward2 = getOrderFieldByID(orderID, SCHEMA.REWARD_2)
local reward3 = getOrderFieldByID(orderID, SCHEMA.REWARD_3)
return {reward1, reward2, reward3}
end
--endregion
--region Public methods
---@public
---Gets an array of all IDs of orders in the data.
---
---@return table an array of IDs
function OrdersData.getAllOrderIDs()
loadData()
local orderIDs = {}
for id, _ in pairs(ordersData) do
table.insert(orderIDs, id)
end
return orderIDs
end
---@public
-----Gets the name of the specified order.
-----
-----@param orderID string
-----@return string the display name
function OrdersData.getNameByID(orderID)
return getOrderFieldByID(orderID, SCHEMA.NAME)
end
---@public
-----Gets the tiers of the specified order.
-----
-----@param orderID string
-----@return array the list of valid tiers
function OrdersData.getTiers(orderID)
return getOrderFieldByID(orderID, SCHEMA.TIERS)
end
---@public
-----Gets the timed status of the specified order.
-----
-----@param orderID string
-----@return boolean the timed status
function OrdersData.getTimed(orderID)
return getOrderFieldByID(orderID, SCHEMA.TIMED_ORDER)
end
---@public
-----Gets the time limit of the specified order.
-----
-----@param orderID string
-----@return number the time limit
function OrdersData.getTimeLimit(orderID)
return getOrderFieldByID(orderID, SCHEMA.TIME_LIMIT)
end
---@public
-----Gets the specified difficulty of the specified order.
-----
-----@param orderID string
-----@param rowNum integer
-----@return difficultyList[rowNum] the difficulty listed in that row
function OrdersData.getDifficultyByIDAndRowNum(orderID, rowNum)
local difficultyList = getDifficultiesArrayByID(orderID)
return difficultyList[rowNum]
end
---@public
-----Gets the specified requirements of the specified order and returns it after formatting it for display.
-----
-----@param orderID string
-----@param rowNum integer
-----@return string the appropriately formatted requirement description
function OrdersData.getFormattedRequirementsByIDAndRowNum(orderID, rowNum)
requirements = getRequirementByIDAndRow(orderID, rowNum)
local requirementsAmount = #requirements
local formattedRequirements = ""
for i = 1, requirementsAmount do
if i > 1 then
formattedRequirements = formattedRequirements .. "<br />"
end
formattedRequirements = formattedRequirements .. makeFormattedRequirementByIDAndRowNumAndSubrow(orderID, rowNum, i)
end
return formattedRequirements
end
---@public
-----Gets the specified rewards of the specified order and returns it after formatting it for display.
-----
-----@param orderID string
-----@return string the appropriately formatted rewards description
function OrdersData.getRewardsFormattedByID(orderID)
local rewards = getRewardsByID(orderID)
local rewardsAmount = #rewards
local formattedRewards = ""
for i = 1, rewardsAmount do
if i > 1 then
formattedRewards = formattedRewards .. "\n"
end
local reward = rewards[i][1]
if reward then
formattedRewards = formattedRewards .. reward[SCHEMA.REWARD_DESCRIPTION]
end
end
return formattedRewards
end
---@public
-----Gets the number of unique difficulties for the order.
-----
-----@param orderID string
-----@return difficultyList[rowNum] the difficulty listed in that row
function OrdersData.getNumberOfDifficulties(orderID)
return #getDifficultiesArrayByID(orderID)
end
---@public
-----Gets the valid difficulties of the specified order.
-----
-----@param orderID string
-----@return array the list of valid difficulties
function OrdersData.getDifficulties(orderID)
return getDifficultiesArrayByID(orderID)
end
--- Standardizes the display of a string name of one of Orders' tier.
---
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param tierString string the descriptive name of the tier of an order
---@return string that same tier, normalized to title case
function OrdersData.normalizeTierText(tierString)
if not tierString or "" == tierString then
return nil
end
local tier = string.lower(tierString)
if not tier or not SWITCH_TIERS_CHECK[tier] then
return nil
end
return NORMALIZED_TEXT_TIERS[tier]
end
--- Standardizes the display of a string name of one of Orders' timed status.
---
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param timedString string the descriptive name of the timed status of an order
---@return string that same timed status, normalized to title case
function OrdersData.normalizeTimedText(timedString)
if not timedString or "" == timedString then
return nil
end
local timed = string.lower(timedString)
if not timed or not SWITCH_TIMED_CHECK[timed] then
return nil
end
return NORMALIZED_TEXT_TIMED[timed]
end
--- Standardizes the display of a string name of one of Orders' difficulty.
---
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param rarityString string the descriptive name of the difficulty of an order
---@return string that same tier, normalized to title case
function OrdersData.normalizeDifficultyText(difficultyString)
if not difficultyString or "" == difficultyString then
return nil
end
local difficulty = string.lower(difficultyString)
if not difficulty or not SWITCH_DIFFICULTIES_CHECK[difficulty] then
return nil
end
return NORMALIZED_TEXT_DIFFICULTIES[difficulty]
end
---isOrderIDValid
---@param orderID string the unique identifier of an Order
---@return boolean true if there is a record with the specified ID
function OrdersData.isOrderIDValid(orderID)
if not orderID or "" == orderID then
return false
else
local record = OrdersData.getNameByID(orderID)
return record ~= nil
end
end
--endregion
---isTierMatchByID
---@param orderID string the unique identifier of an Order
---@param tierToMatch string the tier to check against
---@return boolean true if the tier matches
function OrdersData.isTierMatchByID(orderID, tierToMatch)
local record = OrdersData.getTiers(orderID)
--need to retrieve first entry in record because of nested tables and convert to lowercase
if OrdersData.normalizeTierText(record[1]) == tierToMatch then
return true
else
return false
end
end
---isTimedMatchByID
---@param orderID string the unique identifier of an Order
---@param timedToMatch boolean the timed status to check against
---@return boolean true if the status matches
function OrdersData.isTimedMatchByID(orderID, timedToMatch)
local record = OrdersData.getTimed(orderID)
if record then
if timedToMatch == SCHEMA.IS_TIMED_TRUE then
return true
end
if timedToMatch == SCHEMA.IS_TIMED_FALSE then
return false
end
else
if timedToMatch == SCHEMA.IS_TIMED_TRUE then
return false
end
if timedToMatch == SCHEMA.IS_TIMED_FALSE then
return true
end
end
error(ERROR_MESSAGE_INVALID_FUNCTION_CALL .. ": isTimedMatchByID was called but could not return a boolean.")
end
---isDifficultyMatchByID
---@param orderID string the unique identifier of an Order
---@param difficultyToMatch string the order difficulty to check against
---@return boolean true if the difficulty matches a substring in the array
function OrdersData.isDifficultyMatchByID(orderID, difficultyToMatch)
local difficulties = OrdersData.getDifficulties(orderID)
for i, diff in ipairs(difficulties) do
mw.log(diff)
if string.find(diff, difficultyToMatch) then
return true
end
end
return false
end
---getAllOrdersIDsWhereName
--- Loop through all orders and return those whose names contain the
--- specified search term.
---
---@param searchTerm string terms to search for
---@return table list of orderIDs with a match in their name
function OrdersData.getAllOrdersIDsWhereName(searchTerm)
if not searchTerm then
return {}
end
if not ordersData then
loadData()
end
local list = {}
for _, order in pairs(ordersData) do
if string.lower(order[SCHEMA.NAME]):find(string.lower(searchTerm)) then
table.insert(list, order[SCHEMA.ID])
end
end
return list
end
--endregion
return OrdersData


