Module:OrdersData: Difference between revisions

From Against the Storm Official Wiki
No edit summary
Merge from OrdersData2 to implement filtering by settlement difficulty
 
(2 intermediate revisions by the same user not shown)
Line 12: Line 12:
--region Private member variables
--region Private member variables


local ordersData
local ordersData2


--endregion
--endregion
Line 32: Line 32:
OrdersData.DIFFICULTY_MEDIUM_HARD = "medium, hard"
OrdersData.DIFFICULTY_MEDIUM_HARD = "medium, hard"
OrdersData.DIFFICULTY_EASY_MEDIUM_HARD = "easy, medium, hard"
OrdersData.DIFFICULTY_EASY_MEDIUM_HARD = "easy, medium, hard"
OrdersData.SETTLER = "settler"
OrdersData.PIONEER = "pioneer"
OrdersData.VETERAN = "veteran"
OrdersData.VICEROY = "viceroy"
OrdersData.PRESTIGE = "prestige"


--endregion
--endregion
Line 86: Line 92:
[OrdersData.DIFFICULTY_EASY_MEDIUM_HARD] = true,
[OrdersData.DIFFICULTY_EASY_MEDIUM_HARD] = true,
[OrdersData.NONE] = true,
[OrdersData.NONE] = true,
}
local SWITCH_SETTLEMENT_CHECK = {
[OrdersData.SETTLER] = true,
[OrdersData.PIONEER] = true,
[OrdersData.VETERAN] = true,
[OrdersData.VICEROY] = true,
[OrdersData.PRESTIGE] = true,
}
}


Line 97: Line 111:
["none"] = OrdersData.NONE,
["none"] = OrdersData.NONE,
}
}
--region Private constants


local NORMALIZED_TEXT_SETTLEMENT = {
["settler"] = OrdersData.SETTLER,
["pioneer"] = OrdersData.PIONEER,
["veteran"] = OrdersData.VETERAN,
["viceroy"] = OrdersData.VICEROY,
["prestige"] = OrdersData.PRESTIGE,
["none"] = OrdersData.NONE,
}
local SETTLEMENT_CHECK_ARRAY = {
["settler"] = {
["easy"] = {"i", "ii", "iii", "iv"},
["medium"] = {},
["hard"] = {},
["easy, medium"] = {"i", "ii", "iii", "iv"},
["medium, hard"] = {},
["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
},
["pioneer"] = {
["easy"] = {"i", "ii", "iii", "iv"},
["medium"] = {"i", "ii", "iii", "iv"},
["hard"] = {},
["easy, medium"] = {"i", "ii", "iii", "iv"},
["medium, hard"] = {"i", "ii", "iii", "iv"},
["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
},
["veteran"] = {
["easy"] = {"i"},
["medium"] = {"i", "ii", "iii", "iv"},
["hard"] = {"iii", "iv"},
["easy, medium"] = {"i", "ii", "iii", "iv"},
["medium, hard"] = {"i", "ii", "iii", "iv"},
["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
},
["viceroy"] = {
["easy"] = {},
["medium"] = {"i", "ii", "iii", "iv"},
["hard"] = {"i", "ii", "iii"},
["easy, medium"] = {"i", "ii", "iii", "iv"},
["medium, hard"] = {"i", "ii", "iii", "iv"},
["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
},
["prestige"] = {
["easy"] = {},
["medium"] = {},
["hard"] = {"i", "ii", "iii", "iv"},
["easy, medium"] = {},
["medium, hard"] = {"i", "ii", "iii", "iv"},
["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
},
}
local SETTLEMENT_REVERSE_CHECK_ARRAY = {
["i"] = {
["easy"] = {"settler", "pioneer", "veteran"},
["medium"] = {"pioneer", "veteran", "viceroy"},
["hard"] = {"viceroy", "prestige"},
["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
},
["ii"] = {
["easy"] = {"settler", "pioneer"},
["medium"] = {"pioneer", "veteran", "viceroy"},
["hard"] = {"viceroy", "prestige"},
["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
},
["iii"] = {
["easy"] = {"settler", "pioneer"},
["medium"] = {"pioneer", "veteran", "viceroy"},
["hard"] = {"veteran", "viceroy", "prestige"},
["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
},
["iv"] = {
["easy"] = {"settler", "pioneer"},
["medium"] = {"pioneer", "veteran", "viceroy"},
["hard"] = {"veteran", "prestige"},
["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
},
}




Line 151: Line 250:
         ordersData = {}
         ordersData = {}
         --for _, order in ipairs(newData) do
         --for _, order in ipairs(newData) do
             --ordersData[order[SCHEMA.ID]] = order
             --OrdersData[order[SCHEMA.ID]] = order
         --end
         --end
         --ordersData = mw.loadJsonData("Module:OrdersData/Orders.json")
         --OrdersData = mw.loadJsonData("Module:OrdersData/Orders.json")
         local newData = mw.loadJsonData("Module:OrdersData/Orders.json")
         local newData = mw.loadJsonData("Module:OrdersData/Orders.json")
         for _, order in ipairs(newData) do
         for _, order in ipairs(newData) do
Line 201: Line 300:
         return difficultyList
         return difficultyList
     end
     end
end
local function getSettlementsArrayByID(orderID)
    local difficulties = OrdersData.getDifficulties(orderID)
    local tierRaw = OrdersData.getTiers(orderID)
    local tier = OrdersData.normalizeTierText(tierRaw[1])
    local settlementsArray = {}
    local isSettlementAlreadyAdded = {}
   
    for i, diff in ipairs(difficulties) do
    diff = OrdersData.normalizeDifficultyText(diff)
    for sett, diffToMatch in pairs(SETTLEMENT_CHECK_ARRAY) do
    local diffArray = diffToMatch[diff]
    if diffArray then
    for j, tierToMatch in ipairs(diffArray) do
    if tierToMatch == tier then
    if not isSettlementAlreadyAdded[sett] then
    isSettlementAlreadyAdded[sett] = true
    table.insert(settlementsArray, sett)
    end
    end
    end
    end
    end
    end
    return settlementsArray
end
end


Line 427: Line 553:
function OrdersData.getDifficulties(orderID)
function OrdersData.getDifficulties(orderID)
     return getDifficultiesArrayByID(orderID)
     return getDifficultiesArrayByID(orderID)
end
---@public
-----Gets the valid settlement difficulties of the specified order.
-----
-----@param orderID string
-----@return array the list of valid settlement difficulties
function OrdersData.getSettlements(orderID)
    return getSettlementsArrayByID(orderID)
end
end


Line 477: Line 612:
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
--- 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
---@param difficultyString string the descriptive name of the difficulty of an order
---@return string that same tier, normalized to title case
---@return string that same string, normalized to title case
function OrdersData.normalizeDifficultyText(difficultyString)
function OrdersData.normalizeDifficultyText(difficultyString)


Line 492: Line 627:


return NORMALIZED_TEXT_DIFFICULTIES[difficulty]
return NORMALIZED_TEXT_DIFFICULTIES[difficulty]
end
--- Standardizes the display of a string name of one of Orders' settlement difficulty.
---
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param settlementString string the descriptive name of the settlement difficulty of an order
---@return string that same string, normalized to title case
function OrdersData.normalizeSettlementDifficultyText(settlementString)
if not settlementString or "" == settlementString then
return nil
end
local settlement = string.lower(settlementString)
if not settlement or not SWITCH_SETTLEMENT_CHECK[settlement] then
return nil
end
return NORMALIZED_TEXT_SETTLEMENT[settlement]
end
end


Line 555: Line 711:
for i, diff in ipairs(difficulties) do
for i, diff in ipairs(difficulties) do
if string.find(OrdersData.normalizeDifficultyText(diff), difficultyToMatch) then
if string.find(OrdersData.normalizeDifficultyText(diff), difficultyToMatch) then
return true
end
end
return false
end
---isSettlementMatchByID
---@param orderID string the unique identifier of an Order
---@param settlementToMatch string the settlement difficulty to check against
---@return boolean true if the settlement difficulty is valid for the orderID checked
function OrdersData.isSettlementMatchByID(orderID, settlementToMatch)
local settlements = OrdersData.getSettlements(orderID)
for i, sett in ipairs(settlements) do
if OrdersData.normalizeSettlementDifficultyText(sett) == settlementToMatch then
return true
end
end
return false
end
---isSettlementMatchSpecificOrder
---@param orderDifficulty string the difficulty string of an order
---@param orderTier string the tier string of an order
---@param settlementToMatch string the settlement difficulty to check against
---@return boolean true if the settlement difficulty is valid for the difficulty and tier combo checked
function OrdersData.isSettlementMatchSpecificOrder(orderDifficulty, orderTier, settlementToMatch)
local tier = OrdersData.normalizeTierText(orderTier[1])
local difficulty = OrdersData.normalizeDifficultyText(orderDifficulty)
local settlementArray = SETTLEMENT_REVERSE_CHECK_ARRAY[tier][difficulty]
for i, sett in ipairs(settlementArray) do
if OrdersData.normalizeSettlementDifficultyText(sett) == settlementToMatch then
return true
return true
end
end
Line 607: Line 795:
for _, order in pairs(ordersData) do
for _, order in pairs(ordersData) do


local numRows = #OrdersData.getRequirementsArrayByID(order[SCHEMA.ID])
local numRows = #getRequirementsArrayByID(order[SCHEMA.ID])
--local numRows = #requirementsArray
local isMatch = false
local isMatch = false
for i = 1, numRows do
for i = 1, numRows do

Latest revision as of 01:47, 21 October 2025

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 ordersData2

--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"
OrdersData.DIFFICULTY_EASY_MEDIUM = "easy, medium"
OrdersData.DIFFICULTY_MEDIUM_HARD = "medium, hard"
OrdersData.DIFFICULTY_EASY_MEDIUM_HARD = "easy, medium, hard"

OrdersData.SETTLER = "settler"
OrdersData.PIONEER = "pioneer"
OrdersData.VETERAN = "veteran"
OrdersData.VICEROY = "viceroy"
OrdersData.PRESTIGE = "prestige"

--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,
	[OrdersData.NONE] = true,
}

local NORMALIZED_TEXT_TIERS = {
	["i"] = OrdersData.TIER_I,
	["ii"] = OrdersData.TIER_II,
	["iii"] = OrdersData.TIER_III,
	["iv"] = OrdersData.TIER_IV,
	["none"] = OrdersData.NONE,
}

local SWITCH_TIMED_CHECK = {
	[OrdersData.TRUE] = true,
	[OrdersData.FALSE] = true,
	[OrdersData.NONE] = true,
}

local NORMALIZED_TEXT_TIMED = {
	["true"] = OrdersData.TRUE,
	["false"] = OrdersData.FALSE,
	["none"] = OrdersData.NONE,
}

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,
	[OrdersData.DIFFICULTY_EASY_MEDIUM] = true,
	[OrdersData.DIFFICULTY_MEDIUM_HARD] = true,
	[OrdersData.DIFFICULTY_EASY_MEDIUM_HARD] = true,
	[OrdersData.NONE] = true,
}

local SWITCH_SETTLEMENT_CHECK = {
	[OrdersData.SETTLER] = true,
	[OrdersData.PIONEER] = true,
	[OrdersData.VETERAN] = true,
	[OrdersData.VICEROY] = true,
	[OrdersData.PRESTIGE] = true,
}

local NORMALIZED_TEXT_DIFFICULTIES = {
	["easy"] = OrdersData.DIFFICULTY_EASY,
	["medium"] = OrdersData.DIFFICULTY_MEDIUM,
	["hard"] = OrdersData.DIFFICULTY_HARD,
	["easy, medium"] = OrdersData.DIFFICULTY_EASY_MEDIUM,
	["medium, hard"] = OrdersData.DIFFICULTY_MEDIUM_HARD,
	["easy, medium, hard"] = OrdersData.DIFFICULTY_EASY_MEDIUM_HARD,
	["none"] = OrdersData.NONE,
}

local NORMALIZED_TEXT_SETTLEMENT = {
	["settler"] = OrdersData.SETTLER,
	["pioneer"] = OrdersData.PIONEER,
	["veteran"] = OrdersData.VETERAN,
	["viceroy"] = OrdersData.VICEROY,
	["prestige"] = OrdersData.PRESTIGE,
	["none"] = OrdersData.NONE,
}

local SETTLEMENT_CHECK_ARRAY = {
	["settler"] = {
		["easy"] = {"i", "ii", "iii", "iv"},
		["medium"] = {},
		["hard"] = {},
		["easy, medium"] = {"i", "ii", "iii", "iv"},
		["medium, hard"] = {},
		["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
	},
	["pioneer"] = {
		["easy"] = {"i", "ii", "iii", "iv"},
		["medium"] = {"i", "ii", "iii", "iv"},
		["hard"] = {},
		["easy, medium"] = {"i", "ii", "iii", "iv"},
		["medium, hard"] = {"i", "ii", "iii", "iv"},
		["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
	},
	["veteran"] = {
		["easy"] = {"i"},
		["medium"] = {"i", "ii", "iii", "iv"},
		["hard"] = {"iii", "iv"},
		["easy, medium"] = {"i", "ii", "iii", "iv"},
		["medium, hard"] = {"i", "ii", "iii", "iv"},
		["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
	},
		["viceroy"] = {
		["easy"] = {},
		["medium"] = {"i", "ii", "iii", "iv"},
		["hard"] = {"i", "ii", "iii"},
		["easy, medium"] = {"i", "ii", "iii", "iv"},
		["medium, hard"] = {"i", "ii", "iii", "iv"},
		["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
	},
		["prestige"] = {
		["easy"] = {},
		["medium"] = {},
		["hard"] = {"i", "ii", "iii", "iv"},
		["easy, medium"] = {},
		["medium, hard"] = {"i", "ii", "iii", "iv"},
		["easy, medium, hard"] = {"i", "ii", "iii", "iv"},
	},
}

local SETTLEMENT_REVERSE_CHECK_ARRAY = {
	["i"] = {
		["easy"] = {"settler", "pioneer", "veteran"},
		["medium"] = {"pioneer", "veteran", "viceroy"},
		["hard"] = {"viceroy", "prestige"},
		["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
		["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
		["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
	},
	["ii"] = {
		["easy"] = {"settler", "pioneer"},
		["medium"] = {"pioneer", "veteran", "viceroy"},
		["hard"] = {"viceroy", "prestige"},
		["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
		["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
		["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
	},
	["iii"] = {
		["easy"] = {"settler", "pioneer"},
		["medium"] = {"pioneer", "veteran", "viceroy"},
		["hard"] = {"veteran", "viceroy", "prestige"},
		["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
		["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
		["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
	},
	["iv"] = {
		["easy"] = {"settler", "pioneer"},
		["medium"] = {"pioneer", "veteran", "viceroy"},
		["hard"] = {"veteran", "prestige"},
		["easy, medium"] = {"settler", "pioneer", "veteran", "viceroy"},
		["medium, hard"] = {"pioneer", "veteran", "viceroy", "prestige"},
		["easy, medium, hard"] = {"settler", "pioneer", "veteran", "viceroy", "prestige"},
	},
}


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 = "false",

}

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
        --OrdersData = mw.loadJsonData("Module:OrdersData/Orders.json")
        local newData = mw.loadJsonData("Module:OrdersData/Orders.json")
        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 getSettlementsArrayByID(orderID)

    local difficulties = OrdersData.getDifficulties(orderID)
    local tierRaw = OrdersData.getTiers(orderID)
    local tier = OrdersData.normalizeTierText(tierRaw[1])
    local settlementsArray = {}
    local isSettlementAlreadyAdded = {}
    
    for i, diff in ipairs(difficulties) do
    	diff = OrdersData.normalizeDifficultyText(diff)
    	for sett, diffToMatch in pairs(SETTLEMENT_CHECK_ARRAY) do
    		local diffArray = diffToMatch[diff]
    		if diffArray then
    			for j, tierToMatch in ipairs(diffArray) do
    				if tierToMatch == tier then
    					if not isSettlementAlreadyAdded[sett] then
    						isSettlementAlreadyAdded[sett] = true
    						table.insert(settlementsArray, sett)
    					end
    				end
    			end
    		end
    	end
    end	
    return settlementsArray
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, _ in ipairs(requirements) 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 .. "<br /><br />"
    	end
    	for j,k in ipairs(rewards[i]) do
    		if j > 1 then
    			formattedRewards = formattedRewards .. " or <br />"
    		end
    		local reward = rewards[i][j]
    		if reward then
    			if string.find(reward[SCHEMA.REWARD_ID], "Blueprint") then
    				formattedRewards = formattedRewards  .. reward[SCHEMA.REWARD_NAME] .. " Blueprint"
    			else
    				formattedRewards = formattedRewards .. reward[SCHEMA.REWARD_DESCRIPTION]
    			end
    		end
    	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

---@public
-----Gets the valid settlement difficulties of the specified order.
-----
-----@param orderID string
-----@return array the list of valid settlement difficulties
function OrdersData.getSettlements(orderID)
    return getSettlementsArrayByID(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 difficultyString string the descriptive name of the difficulty of an order
---@return string that same string, 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

--- Standardizes the display of a string name of one of Orders' settlement difficulty.
---
--- If the provided tier is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param settlementString string the descriptive name of the settlement difficulty of an order
---@return string that same string, normalized to title case
function OrdersData.normalizeSettlementDifficultyText(settlementString)

	if not settlementString or "" == settlementString then

		return nil
	end

	local settlement = string.lower(settlementString)
	if not settlement or not SWITCH_SETTLEMENT_CHECK[settlement] then
		return nil
	end

	return NORMALIZED_TEXT_SETTLEMENT[settlement]
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
		if string.find(OrdersData.normalizeDifficultyText(diff), difficultyToMatch) then
			return true
		end
	end
	return false
end

---isSettlementMatchByID
---@param orderID string the unique identifier of an Order
---@param settlementToMatch string the settlement difficulty to check against
---@return boolean true if the settlement difficulty is valid for the orderID checked
function OrdersData.isSettlementMatchByID(orderID, settlementToMatch)
	local settlements = OrdersData.getSettlements(orderID)
	for i, sett in ipairs(settlements) do
		if OrdersData.normalizeSettlementDifficultyText(sett) == settlementToMatch then
			return true
		end
	end
	return false
end


---isSettlementMatchSpecificOrder
---@param orderDifficulty string the difficulty string of an order
---@param orderTier string the tier string of an order
---@param settlementToMatch string the settlement difficulty to check against
---@return boolean true if the settlement difficulty is valid for the difficulty and tier combo checked
function OrdersData.isSettlementMatchSpecificOrder(orderDifficulty, orderTier, settlementToMatch)
	local tier = OrdersData.normalizeTierText(orderTier[1])
	local difficulty = OrdersData.normalizeDifficultyText(orderDifficulty)
	local settlementArray = SETTLEMENT_REVERSE_CHECK_ARRAY[tier][difficulty]
	for i, sett in ipairs(settlementArray) do
		if OrdersData.normalizeSettlementDifficultyText(sett) == settlementToMatch 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

---getAllOrdersIDsWhereRequirements
--- Loop through all orders and return those whose requirements 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.getAllOrdersIDsWhereRequirements(searchTerm)

	if not searchTerm then
		return {}
	end

	if not ordersData then
		loadData()
	end

	local list = {}
	for _, order in pairs(ordersData) do

		local numRows = #getRequirementsArrayByID(order[SCHEMA.ID])
		--local numRows = #requirementsArray
		local isMatch = false
		for i = 1, numRows do
			if not isMatch then
				if string.lower(OrdersData.getFormattedRequirementsByIDAndRowNum(order[SCHEMA.ID], i)):find(string.lower(searchTerm)) then
					table.insert(list, order[SCHEMA.ID])
				end
			end
		end
	end

	return list
end

---getAllOrdersIDsWhereRequirements
--- Loop through all orders and return those whose requirements 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.getAllOrdersIDsWhereRewards(searchTerm)

	if not searchTerm then
		return {}
	end

	if not ordersData then
		loadData()
	end

	local list = {}
	for _, order in pairs(ordersData) do

		if string.lower(OrdersData.getRewardsFormattedByID(order[SCHEMA.ID])):find(string.lower(searchTerm)) then
			table.insert(list, order[SCHEMA.ID])
		end
	end

	return list
end
--endregion

return OrdersData