Module:StormforgedController: Difference between revisions

From Against the Storm Official Wiki
(Created to serve a stormforged cornerstone template based on table templates)
 
m (Added exclusion)
Line 23: Line 23:
local ARG_NAME = "name"
local ARG_NAME = "name"
local ARG_SEARCH = "search"
local ARG_SEARCH = "search"
local ARGS_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_CAPTION = "caption"
local ARG_SKIP_PRICES = "skip_prices"
local ARG_SKIP_PRICES = "skip_prices"
Line 94: Line 95:
end
end


--- Takes a list of possible IDs and only loads the valid ones (Stormforged ones) to the member variable altardIDs.
--- 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 newIDs table a list of possible IDs to add to main list
local function loadStormforgedIDs(newIDs)
---@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
     for _, id in ipairs(newIDs) do
         if AltarEffectsData.isIDValid(id) then
         if AltarEffectsData.isIDValid(id) and not excludeList[id] then
             table.insert(altarIDs, id)
             table.insert(altarIDs, id)
         end
         end
Line 108: 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)
local function loadIDs(selectedID, searchName, searchTerm, excludeList)


     if not altarIDs or #altarIDs < 1 then
     if not altarIDs or #altarIDs < 1 then
Line 117: Line 119:
         -- Load the selected ID
         -- Load the selected ID
         if selectedID and selectedID ~= "" then
         if selectedID and selectedID ~= "" then
             loadStormforgedIDs( { selectedID } )
             loadStormforgedIDs( { selectedID }, excludeList )
             attemptedSearch = true
             attemptedSearch = true
         end
         end
Line 124: Line 126:
         if searchName and searchName ~= "" then
         if searchName and searchName ~= "" then
             local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
             local newIDs = PerksData.getAllPerkIDsWhereName(searchName)
             loadStormforgedIDs(newIDs)
             loadStormforgedIDs(newIDs, excludeList)
             attemptedSearch = true
             attemptedSearch = true
         end
         end
Line 131: Line 133:
         if searchTerm and searchTerm ~= "" then
         if searchTerm and searchTerm ~= "" then
             local newIDs = PerksData.getAllPerkIDsWhereDescription(searchTerm)
             local newIDs = PerksData.getAllPerkIDsWhereDescription(searchTerm)
             loadStormforgedIDs(newIDs)
             loadStormforgedIDs(newIDs, excludeList)
             attemptedSearch = true
             attemptedSearch = true
         end
         end
Line 150: Line 152:
     end
     end
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.
--- 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.
Line 257: 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, desiredCaption, skippingPrice)
local function renderTable(selectedID, searchName, searchTerm, excludeList, desiredCaption, skippingPrice)


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


     if #altarIDs < 1 then
     if #altarIDs < 1 then
Line 267: Line 284:
     desiredCaption = resolveCaption(selectedID, searchName, searchTerm, desiredCaption)
     desiredCaption = resolveCaption(selectedID, searchName, searchTerm, desiredCaption)


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


Line 285: Line 302:
     local searchName = frame.args[ARG_NAME]
     local searchName = frame.args[ARG_NAME]
     local searchTerm = frame.args[ARG_SEARCH]
     local searchTerm = frame.args[ARG_SEARCH]
    local exclusions = frame.args[ARGS_EXCLUDE_LIST]
     local desiredCaption = frame.args[ARG_CAPTION]
     local desiredCaption = frame.args[ARG_CAPTION]
     local skippingPrice = frame.args[ARG_SKIP_PRICES]
     local skippingPrice = frame.args[ARG_SKIP_PRICES]


     --TODO make displayOverride == DISPLAY_OPTION_LIST
     --TODO make displayOverride == DISPLAY_OPTION_LIST
    local excludeList = {}
    if exclusions and exclusions ~= "" then
        excludeList = expandExclusions(exclusions)
    end


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



Revision as of 15:04, 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_SEARCH = "search"
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, searchTerm, 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 searchTerm and searchTerm ~= "" then
            local newIDs = PerksData.getAllPerkIDsWhereDescription(searchTerm)
            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 searchTerm string the third search parameter, a term
---@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, searchTerm, 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 searchTerm and searchTerm ~= "" then
        return "Stormforged Cornerstones mentioning '" .. searchTerm .. "'"
    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, searchTerm, excludeList, desiredCaption, skippingPrice)

    loadIDs(selectedID, searchName, searchTerm, excludeList)

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

    desiredCaption = resolveCaption(selectedID, searchName, searchTerm, 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 searchTerm = frame.args[ARG_SEARCH]
    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, searchTerm, excludeList, desiredCaption, skippingPrice)
end

--endregion

return StormforgedController