Module:StormforgedController: Difference between revisions

From Against the Storm Official Wiki
m (Added exclusion)
m (renamed a term)
Line 22: Line 22:
local ARG_ID = "id"
local ARG_ID = "id"
local ARG_NAME = "name"
local ARG_NAME = "name"
local ARG_SEARCH = "search"
local ARG_DESCRIPTION = "description"
local ARGS_EXCLUDE_LIST = "exclude"
local ARGS_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_CAPTION = "caption"
Line 110: Line 110:
--- Requests the lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variables.
--- Requests the lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variables.
---
---
local function loadIDs(selectedID, searchName, searchTerm, excludeList)
local function loadIDs(selectedID, searchName, searchDesc, excludeList)


     if not altarIDs or #altarIDs < 1 then
     if not altarIDs or #altarIDs < 1 then
Line 131: Line 131:


         -- Add any description matches
         -- Add any description matches
         if searchTerm and searchTerm ~= "" then
         if searchDesc and searchDesc ~= "" then
             local newIDs = PerksData.getAllPerkIDsWhereDescription(searchTerm)
             local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDesc)
             loadStormforgedIDs(newIDs, excludeList)
             loadStormforgedIDs(newIDs, excludeList)
             attemptedSearch = true
             attemptedSearch = true
Line 174: Line 174:
---@param selectedID string the first search parameter, an id
---@param selectedID string the first search parameter, an id
---@param searchName string the second search parameter, a name
---@param searchName string the second search parameter, a name
---@param searchTerm string the third search parameter, a term
---@param searchDesc string the third search parameter, a term in a description
---@param desiredCaption string the caption requested, if any
---@param desiredCaption string the caption requested, if any
---@return string an appropriate caption, or blank if it should be default
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(selectedID, searchName, searchTerm, desiredCaption)
local function resolveCaption(selectedID, searchName, searchDesc, desiredCaption)


     if desiredCaption and desiredCaption ~= "" then
     if desiredCaption and desiredCaption ~= "" then
Line 189: Line 189:
         return "Stormforged Cornerstones named '" .. searchName .. "'"
         return "Stormforged Cornerstones named '" .. searchName .. "'"
     end
     end
     if searchTerm and searchTerm ~= "" then
     if searchDesc and searchDesc ~= "" then
         return "Stormforged Cornerstones mentioning '" .. searchTerm .. "'"
         return "Stormforged Cornerstones mentioning '" .. searchDesc .. "'"
     end
     end


Line 274: Line 274:
---@param skippingPrice table
---@param skippingPrice table
---@return string the wiki markup assembled by the view (other templates)
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(selectedID, searchName, searchTerm, excludeList, desiredCaption, skippingPrice)
local function renderTable(selectedID, searchName, searchDesc, excludeList, desiredCaption, skippingPrice)


     loadIDs(selectedID, searchName, searchTerm, excludeList)
     loadIDs(selectedID, searchName, searchDesc, excludeList)


     if #altarIDs < 1 then
     if #altarIDs < 1 then
Line 282: Line 282:
     end
     end


     desiredCaption = resolveCaption(selectedID, searchName, searchTerm, desiredCaption)
     desiredCaption = resolveCaption(selectedID, searchName, searchDesc, desiredCaption)


     return makeMarkupForTableStart(desiredCaption, skippingPrice) .. makeMarkupForTableRows(skippingPrice) .. "\n" .. makeMarkupForTableEnd()
     return makeMarkupForTableStart(desiredCaption, skippingPrice) .. makeMarkupForTableRows(skippingPrice) .. "\n" .. makeMarkupForTableEnd()
Line 301: Line 301:
     local selectedID = frame.args[ARG_ID]
     local selectedID = frame.args[ARG_ID]
     local searchName = frame.args[ARG_NAME]
     local searchName = frame.args[ARG_NAME]
     local searchTerm = frame.args[ARG_SEARCH]
     local searchDesc = frame.args[ARG_DESCRIPTION]
     local exclusions = frame.args[ARGS_EXCLUDE_LIST]
     local exclusions = frame.args[ARGS_EXCLUDE_LIST]
     local desiredCaption = frame.args[ARG_CAPTION]
     local desiredCaption = frame.args[ARG_CAPTION]
Line 314: Line 314:


     currentFrame = frame
     currentFrame = frame
     return renderTable(selectedID, searchName, searchTerm, excludeList, desiredCaption, skippingPrice)
     return renderTable(selectedID, searchName, searchDesc, excludeList, desiredCaption, skippingPrice)
end
end



Revision as of 15:12, 1 May 2024

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

---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by onken.
--- DateTime: 4/29/2024 9:27 PM
---
---@module StormforgedController
local StormforgedController = {}



--region Dependencies

local AltarEffectsData = require("Module:AltarEffectsData")
local PerksData = require("Module:PerksData")

--endregion



--region Private constants

local ARG_ID = "id"
local ARG_NAME = "name"
local ARG_DESCRIPTION = "description"
local ARGS_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_SKIP_PRICES = "skip_prices"
local ARG_SKIP_PRICES_FLAG_VALUE = "skip"

--local DISPLAY_OPTION_TABLE = "table"

local TEMPLATE_PARAMETER_ID = "id"
local TEMPLATE_PARAMETER_DESC = "description"
local TEMPLATE_PARAMETER_PRICE_META = "price_in_meta_resources"
local TEMPLATE_PARAMETER_PRICE_VILL = "price_in_villagers"
local TEMPLATE_PARAMETER_UPGRA_META = "price_for_upgrade_in_meta_resources"
local TEMPLATE_PARAMETER_UPGRA_VILL = "price_for_upgrade_in_villagers"

local TEMPLATE_CONTAINER_START = "StormforgedCornerstonesTable"
local TEMPLATE_CONTAINER_SUFFIX_ROW = "/row"
local TEMPLATE_CONTAINER_SUFFIX_END = "/end"
local TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT = "/SkipPrices"

local ERROR_PREFIX_NAME_NOT_FOUND = "StormforgedController attempted to retrieve the display name of a Stormforged Cornerstones, but it was not found. Double-check that the data is up-to-date and loaded correctly:"

--endregion



--region Private member variables

local currentFrame = {}
local altarIDs = {}

--endregion



--region Private methods

--- Helper comparison function to give to table.sort to sort the perk names alphabetically that correspond to the identifiers of Stormforged Cornerstones.
---
---@param altarID1 string the identifier of a Stormforged Cornerstone
---@param altarID2 string the identifier of a Stormforged Cornerstone
---@return boolean to sort the corresponding display names alphabetically
local function compareNames(altarID1, altarID2)

    local name1 = PerksData.getNameByID(altarID1)
    local name2 = PerksData.getNameByID(altarID2)

    if not name1 then
        error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. altarID1 .. ".")
    end
    if not name2 then
        error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. altarID2 .. ".")

    end

    return name1 < name2
end

--- Runs through the list of perk IDs and checks that a name exists for each one. If not, it throws an error identifying the offending ID.
---
---@param list table the list of unique identifiers for which to look up names
local function validateNames(list)

    for _, id in ipairs(list) do

       --Validate the names. This should never happen, but this is here to save hours of troubleshooting.
        local name = PerksData.getNameByID(id)
        if not name then
            error(ERROR_PREFIX_NAME_NOT_FOUND .. "id=" .. id)
        end
    end
end

--- Takes a list of possible IDs and only loads the valid ones (Stormforged ones) to the member variable altardIDs. Skips all ids present in the exclude list.
---
---@param newIDs table a list of possible IDs to add to main list
---@param excludeList table where keys are ids to skip and the values are `true`
local function loadStormforgedIDs(newIDs, excludeList)

    for _, id in ipairs(newIDs) do
        if AltarEffectsData.isIDValid(id) and not excludeList[id] then
            table.insert(altarIDs, id)
        end
    end
end

--- Requests the lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variables.
---
local function loadIDs(selectedID, searchName, searchDesc, excludeList)

    if not altarIDs or #altarIDs < 1 then

        altarIDs = {}
        local attemptedSearch = false

        -- Load the selected ID
        if selectedID and selectedID ~= "" then
            loadStormforgedIDs( { selectedID }, excludeList )
            attemptedSearch = true
        end

        -- Add any name matches
        if searchName and searchName ~= "" then
            local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
            loadStormforgedIDs(newIDs, excludeList)
            attemptedSearch = true
        end

        -- Add any description matches
        if searchDesc and searchDesc ~= "" then
            local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDesc)
            loadStormforgedIDs(newIDs, excludeList)
            attemptedSearch = true
        end

        -- Or, if no searching was done, show them all
        if not attemptedSearch then
            altarIDs = AltarEffectsData.getAllAltarIDs()
        end
    end

    if #altarIDs > 0 then
        -- Remove all IDs that aren't for Stormforged Cornerstones--there were probably extra pulled from PerksData if there were search terms.
        validateNames(altarIDs)

        --Alphabetize by name
        table.sort(altarIDs, compareNames)

    end
end


---expandExclusions
---@param exclusions string the list of ids, separated by commas
---@return table the ids now as keys in the table and each value is `true`
local function expandExclusions(exclusions)

    local excludeList = {}
    for idToExclude in string.gmatch(exclusions, "[^,]+") do
        excludeList[idToExclude] = true
    end

    return excludeList
end


--- Ensures that if a caption is not defined, there is a suitable fallback in case the author has specified an id, name, or search term to use, so readers know what they're looking at.
---
--- If no caption was specified and also none of those filtering parameters, then leave it up to the template by returning a blank caption.
---
---@param selectedID string the first search parameter, an id
---@param searchName string the second search parameter, a name
---@param searchDesc string the third search parameter, a term in a description
---@param desiredCaption string the caption requested, if any
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(selectedID, searchName, searchDesc, desiredCaption)

    if desiredCaption and desiredCaption ~= "" then
        return desiredCaption
    end

    if selectedID and selectedID ~= "" then
        return "Stormforged Cornerstone with id '" .. selectedID .. "'"
    end
    if searchName and searchName ~= "" then
        return "Stormforged Cornerstones named '" .. searchName .. "'"
    end
    if searchDesc and searchDesc ~= "" then
        return "Stormforged Cornerstones mentioning '" .. searchDesc .. "'"
    end

    -- Delegate to the template for the default
    return ""
end

--- Calls the view (other templates) to render the beginning of the table.
---
---@param desiredCaption string the caption for the table, if any
---@param skippingPrice string the flag for skipping prices in the table, if any
---@return string the wiki markup to display, now longer
local function makeMarkupForTableStart(desiredCaption, skippingPrice)

    local suffixIfSkipping = (skippingPrice == ARG_SKIP_PRICES_FLAG_VALUE and TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT) or ""
    local startTemplate = TEMPLATE_CONTAINER_START .. suffixIfSkipping

    return currentFrame:expandTemplate{
        title = startTemplate,
        args = { caption = desiredCaption, skip_prices = skippingPrice } }
end

--- Calls the view (other templates) to render a single row of the table with data based on the provided identifier.
---
---@param skippingPrice string the flag for skipping prices in the table, if any
---@param id string the unique identifier of a Stormforged Cornerstone to use to add data to a new table row
---@return string the wiki markup to display, now longer
local function makeMarkupPerRow(skippingPrice, id)

    local rowTemplate = TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_ROW

    templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)
    }

    if skippingPrice ~= ARG_SKIP_PRICES_FLAG_VALUE then

        templateParameters[TEMPLATE_PARAMETER_PRICE_META] = AltarEffectsData.getPriceInMetaResources(id)
        templateParameters[TEMPLATE_PARAMETER_PRICE_VILL] = AltarEffectsData.getPriceInVillagers(id)
        templateParameters[TEMPLATE_PARAMETER_UPGRA_META] = AltarEffectsData.getUpgradePriceInMetaResources(id)
        templateParameters[TEMPLATE_PARAMETER_UPGRA_VILL] = AltarEffectsData.getUpgradePriceInVillagers(id)

    else
        rowTemplate = rowTemplate .. TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT
    end

    return currentFrame:expandTemplate{
        title = rowTemplate, args = templateParameters }
end

--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
---
---@param skippingPrice string the flag for skipping prices in the table, if any
---@return string the wiki markup to display, now longer
local function makeMarkupForTableRows(skippingPrice)

    local markup = ""
    for _, id in ipairs(altarIDs) do
        markup = markup .. makeMarkupPerRow(skippingPrice, id)
    end

    return markup
end

--- Calls the view (other templates) to render the end of the table.
---
---@return string the wiki markup to display, now longer
local function makeMarkupForTableEnd()

    local endTemplate = TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_END

    return currentFrame:expandTemplate{
        title = endTemplate,
        args = {} }
end

---Calls the view (other templates) to render the content and appends it to the wiki markup string that will replace this controller's template.
---
--- Calls several methods to build pieces of the table's markup based on the author's requests.
---
---@param desiredCaption table
---@param skippingPrice table
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(selectedID, searchName, searchDesc, excludeList, desiredCaption, skippingPrice)

    loadIDs(selectedID, searchName, searchDesc, excludeList)

    if #altarIDs < 1 then
        return "No Stormforged Cornerstones found."
    end

    desiredCaption = resolveCaption(selectedID, searchName, searchDesc, desiredCaption)

    return makeMarkupForTableStart(desiredCaption, skippingPrice) .. makeMarkupForTableRows(skippingPrice) .. "\n" .. makeMarkupForTableEnd()
end

--endregion



--region Public methods

---main
---@param frame table the MediaWiki frame provided by the template call
---@return string wiki markup to display
function StormforgedController.main(frame)

    -- Extract template parameters
    local selectedID = frame.args[ARG_ID]
    local searchName = frame.args[ARG_NAME]
    local searchDesc = frame.args[ARG_DESCRIPTION]
    local exclusions = frame.args[ARGS_EXCLUDE_LIST]
    local desiredCaption = frame.args[ARG_CAPTION]
    local skippingPrice = frame.args[ARG_SKIP_PRICES]

    --TODO make displayOverride == DISPLAY_OPTION_LIST

    local excludeList = {}
    if exclusions and exclusions ~= "" then
        excludeList = expandExclusions(exclusions)
    end

    currentFrame = frame
    return renderTable(selectedID, searchName, searchDesc, excludeList, desiredCaption, skippingPrice)
end

--endregion

return StormforgedController