Module:StormforgedController: Difference between revisions

From Against the Storm Official Wiki
m (Added exclusion)
(finished refactoring for list and inline display versions)
 
(14 intermediate revisions by the same user not shown)
Line 1: Line 1:
---
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Serves the Stormforged Cornerstones template by capturing input and using it to control the display of data.
--- Created by onken.
--- DateTime: 4/29/2024 9:27 PM
---
---
---@module StormforgedController
---@module StormforgedController
Line 13: Line 11:
local AltarEffectsData = require("Module:AltarEffectsData")
local AltarEffectsData = require("Module:AltarEffectsData")
local PerksData = require("Module:PerksData")
local PerksData = require("Module:PerksData")
local ControllerUtilities = require("Module:ControllerUtilities")


--endregion
--endregion
Line 20: Line 20:
--region Private constants
--region Private constants


local ARG_ID = "id"
local ARG_ID_LIST = "id"
local ARG_NAME = "name"
local ARG_NAME = "name"
local ARG_SEARCH = "search"
local ARG_DESCRIPTION = "description"
local ARG_UPGRADABLE_ID = "upgrades_from_id"
local ARGS_EXCLUDE_LIST = "exclude"
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"
local ARG_SKIP_PRICES_FLAG_VALUE = "skip"
local ARG_SKIP_FLAG_VALUE = "skip"
 
local ARG_SHOW_ID = "show_id"
--local DISPLAY_OPTION_TABLE = "table"
local ARG_SHOW_DESCRIPTION = "show_description"
local ARG_SHOW_REGULAR = "show_regular"
local ARG_SHOW_FLAG_VALUE = "show"
local ARG_DISPLAY_OVERRIDE = "display"
local ARG_DISPLAY_OVERRIDE_OPTION_LIST = "list"
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_LIST_TYPE = "list_type"


local TEMPLATE_PARAMETER_ID = "id"
local TEMPLATE_PARAMETER_CAPTION = ARG_CAPTION
local TEMPLATE_PARAMETER_DESC = "description"
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_DESC = ARG_DESCRIPTION
local TEMPLATE_PARAMETER_PRICE_META = "price_in_meta_resources"
local TEMPLATE_PARAMETER_PRICE_META = "price_in_meta_resources"
local TEMPLATE_PARAMETER_PRICE_VILL = "price_in_villagers"
local TEMPLATE_PARAMETER_PRICE_VILLAGER = "price_in_villagers"
local TEMPLATE_PARAMETER_UPGRA_META = "price_for_upgrade_in_meta_resources"
local TEMPLATE_PARAMETER_UPGRADE_META = "price_for_upgrade_in_meta_resources"
local TEMPLATE_PARAMETER_UPGRA_VILL = "price_for_upgrade_in_villagers"
local TEMPLATE_PARAMETER_UPGRADE_VILLAGER = "price_for_upgrade_in_villagers"
 
local TEMPLATE_PARAMETER_SHOW_ID = ARG_SHOW_ID
local TEMPLATE_CONTAINER_START = "StormforgedCornerstonesTable"
local TEMPLATE_PARAMETER_UPGRADE_FROM = "upgrades_from"
local TEMPLATE_CONTAINER_SUFFIX_ROW = "/row"
local TEMPLATE_PARAMETER_LIST_TYPE = ARG_LIST_TYPE
local TEMPLATE_CONTAINER_SUFFIX_END = "/end"
local TEMPLATE_PARAMETER_ICON_SIZE = "icon_size"
local TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT = "/SkipPrices"
local TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT = "none"


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:"
local TEMPLATE_TABLE_BASE = "StormforgedCornerstonesTable"
local TEMPLATE_TABLE_SUFFIX_ROW = "/row"
local TEMPLATE_TABLE_SUFFIX_END = "/end"
local TEMPLATE_TABLE_SKIP_PRICES_VARIANT = "/SkipPrices"
local TEMPLATE_LIST_ITEM = "StormforgedCornerstonesList/item"
local TEMPLATE_INLINE_LINK = "pl"


--endregion
--endregion
Line 51: Line 64:


local currentFrame = {}
local currentFrame = {}
local altarIDs = {}
local altarIDs = {}
local isIDAlreadyAdded = {}


--endregion
--endregion
Line 59: Line 74:
--region Private methods
--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.
--- 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 altarID1 string the identifier of a Stormforged Cornerstone
--- Works from the member variable altarIDs
---@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 validateNamesFromIDs()
local function validateNames(list)


     for _, id in ipairs(list) do
     for _, id in ipairs(altarIDs) do


       --Validate the names. This should never happen, but this is here to save hours of troubleshooting.
       --Validate the names. This should never happen, but this is here to save hours of troubleshooting.
Line 95: Line 90:
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.
--- Takes a list of possible IDs and only loads the valid ones (Stormforged ones) to the member variable altarIDs. 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
Line 102: Line 97:


     for _, id in ipairs(newIDs) do
     for _, id in ipairs(newIDs) do
         if AltarEffectsData.isIDValid(id) and not excludeList[id] then
         if AltarEffectsData.isIDValid(id) and not excludeList[id] and not isIDAlreadyAdded[id] then
 
             table.insert(altarIDs, id)
             table.insert(altarIDs, id)
            isIDAlreadyAdded[id] = true
         end
         end
     end
     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.
--- Requests the desired lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variable altarIDs.
---
---
local function loadIDs(selectedID, searchName, searchTerm, excludeList)
---@param selectList table list of ids of the cornerstone to include
---@param searchName string search term to look in cornerstone names
---@param searchDesc string search term to look in cornerstone descriptions
---@param regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@param excludeList table list of ids
local function loadIDs(selectList, searchName, searchDesc, regularID, excludeList)


     if not altarIDs or #altarIDs < 1 then
     altarIDs = {}
    isIDAlreadyAdded = {}
    local attemptedSearch = false


        altarIDs = {}
    -- Load the selected IDs
        local attemptedSearch = false
    if selectList then
 
        loadStormforgedIDs(selectList, excludeList)
        -- Load the selected ID
        -- It should always be true, but it can be blank.
        if selectedID and selectedID ~= "" then
        if #selectList > 0 then
            loadStormforgedIDs( { selectedID }, excludeList )
             attemptedSearch = true
             attemptedSearch = true
         end
         end
    end


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


        -- 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
        end
    end


        -- Or, if no searching was done, show them all
    -- Add any upgrade matches
        if not attemptedSearch then
    if regularID and regularID ~= "" then
            altarIDs = AltarEffectsData.getAllAltarIDs()
        loadStormforgedIDs({ AltarEffectsData.getUpgradeWhereRegularID(regularID) }, excludeList)
         end
         attemptedSearch = true
     end
     end


     if #altarIDs > 0 then
     -- Or, if no searching was even attempted (regardless of result), show them all.
        -- Remove all IDs that aren't for Stormforged Cornerstones--there were probably extra pulled from PerksData if there were search terms.
    if not attemptedSearch then
         validateNames(altarIDs)
         local newIDs = AltarEffectsData.getAllAltarIDs()
 
         loadStormforgedIDs(newIDs, excludeList)
         --Alphabetize by name
        table.sort(altarIDs, compareNames)
 
     end
     end
end


 
    if #altarIDs > 0 then
---expandExclusions
        -- Remove all IDs that aren't for Stormforged Cornerstones--there will be extra regular Perks pulled from PerksData whenever there are search terms.
---@param exclusions string the list of ids, separated by commas
        validateNamesFromIDs()
---@return table the ids now as keys in the table and each value is `true`
        -- Alphabetize by name.
local function expandExclusions(exclusions)
        table.sort(altarIDs, PerksData.compareNames)
 
    local excludeList = {}
    for idToExclude in string.gmatch(exclusions, "[^,]+") do
        excludeList[idToExclude] = true
     end
     end
    return excludeList
end
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 172: Line 165:
--- If no caption was specified and also none of those filtering parameters, then leave it up to the template by returning a blank caption.
--- 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 desiredCaption string the caption requested, if any
---@param selectList table list of ids of the cornerstone to include
---@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 regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@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(desiredCaption, selectList, searchName, searchDesc, regularID)


    -- No matter what, if the author provided a caption, use that.
     if desiredCaption and desiredCaption ~= "" then
     if desiredCaption and desiredCaption ~= "" then
         return desiredCaption
         return desiredCaption
     end
     end


     if selectedID and selectedID ~= "" then
     -- If no selection, delegate to template.
         return "Stormforged Cornerstone with id '" .. selectedID .. "'"
    local addedToText = false
 
    desiredCaption = "" .. #altarIDs .. " Stormforged Cornerstones"
 
    if selectList and #selectList > 0 then
         if #selectList == 1 then
            desiredCaption = desiredCaption .. " with ID '" .. selectList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " with selected IDs"
        end
        addedToText = true
     end
     end
     if searchName and searchName ~= "" then
     if searchName and searchName ~= "" then
         return "Stormforged Cornerstones named '" .. searchName .. "'"
         if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " named '" .. searchName .. "'"
        addedToText = true
    end
 
    if searchDesc and searchDesc ~= "" then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " mentioning '" .. searchDesc .. "'"
        addedToText = true
     end
     end
     if searchTerm and searchTerm ~= "" then
 
         return "Stormforged Cornerstones mentioning '" .. searchTerm .. "'"
     if regularID and regularID ~= "" then
         if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " upgraded from '" .. regularID .. "'"
        addedToText = true
     end
     end


     -- Delegate to the template for the default
     if excludeList and #excludeList > 0 then
     return ""
        desiredCaption = desiredCaption .. " (with exclusions)"
        addedToText = true
    end
 
    if addedToText then
        return desiredCaption
    else
        -- Delegate to the template for the default
        return ""
    end
end
 
---resolveStartTemplate
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
local function resolveTableStartTemplate(isSkippingPrices)
 
    local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""
 
     return TEMPLATE_TABLE_BASE .. suffixIfSkipping
end
 
---resolveRowTemplate
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
local function resolveTableRowTemplate(isSkippingPrices)
 
    local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""
 
    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_ROW .. suffixIfSkipping
end
 
---resolveEndTemplate
---@return string the template name to use
local function resolveTableEndTemplate()
 
    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_END
end
end


Line 200: Line 259:
---
---
---@param desiredCaption string the caption for the table, if any
---@param desiredCaption string the caption for the table, if any
---@param skippingPrice string the flag for skipping prices in the table, if any
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the wiki markup to display, now longer
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
local function makeMarkupForTableStart(desiredCaption, skippingPrice)
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableStart(desiredCaption, isSkippingPrices, argsForTableTemplate)


     local suffixIfSkipping = (skippingPrice == ARG_SKIP_PRICES_FLAG_VALUE and TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT) or ""
     argsForTableTemplate[TEMPLATE_PARAMETER_CAPTION] = desiredCaption
    local startTemplate = TEMPLATE_CONTAINER_START .. suffixIfSkipping


     return currentFrame:expandTemplate{
     return currentFrame:expandTemplate{ title = resolveTableStartTemplate(isSkippingPrices), args = argsForTableTemplate }
        title = startTemplate,
        args = { caption = desiredCaption, skip_prices = skippingPrice } }
end
end


--- Calls the view (other templates) to render a single row of the table with data based on the provided identifier.
--- 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
---@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
---@param isSkippingPrices boolean true if skipping variant should be used
local function makeMarkupPerRow(skippingPrice, id)
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
 
---@return string the wiki markup assembled by the view (other templates)
    local rowTemplate = TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_ROW
local function makeMarkupPerRow(id, isSkippingPrices, argsForTableTemplate)
 
    templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)
    }


     if skippingPrice ~= ARG_SKIP_PRICES_FLAG_VALUE then
     argsForTableTemplate[TEMPLATE_PARAMETER_ID] = id
    argsForTableTemplate[TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)


        templateParameters[TEMPLATE_PARAMETER_PRICE_META] = AltarEffectsData.getPriceInMetaResources(id)
    argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_FROM] = AltarEffectsData.getRegularVersionEffectID(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
     -- This is default, but adds a lot of extra template overhead if it's not needed, so only add it if necessary.
         rowTemplate = rowTemplate .. TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT
    if not isSkippingPrices then
         argsForTableTemplate[TEMPLATE_PARAMETER_PRICE_META] = AltarEffectsData.getPriceInMetaResources(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_PRICE_VILLAGER] = AltarEffectsData.getPriceInVillagers(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_META] = AltarEffectsData.getUpgradePriceInMetaResources(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_VILLAGER] = AltarEffectsData.getUpgradePriceInVillagers(id)
     end
     end


     return currentFrame:expandTemplate{
     return currentFrame:expandTemplate{ title = resolveTableRowTemplate(isSkippingPrices), args = argsForTableTemplate }
        title = rowTemplate, args = templateParameters }
end
end


--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
--- 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
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the wiki markup to display, now longer
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
local function makeMarkupForTableRows(skippingPrice)
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableRows(isSkippingPrices, argsForTableTemplate)


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


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


    local endTemplate = TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_END
     return currentFrame:expandTemplate{ title = resolveTableEndTemplate(), args = {} }
 
     return currentFrame:expandTemplate{
        title = endTemplate,
        args = {} }
end
end


Line 271: Line 320:
--- Calls several methods to build pieces of the table's markup based on the author's requests.
--- Calls several methods to build pieces of the table's markup based on the author's requests.
---
---
---@param desiredCaption table
---@param desiredCaption string override the table caption
---@param skippingPrice table
---@param isSkippingPrices boolean true if skipping variant should be used
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(desiredCaption, isSkippingPrices, argsForTableTemplate)
 
    local startMarkup = makeMarkupForTableStart(desiredCaption, isSkippingPrices, argsForTableTemplate)
    local rowsMarkup =  makeMarkupForTableRows(isSkippingPrices, argsForTableTemplate)
 
    return startMarkup .. rowsMarkup .. makeMarkupForTableEnd()
end
 
---makeMarkupPerListITem
---@param id string the id of the cornerstone to display
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerListItem(id, listType, isShowingID)
 
    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_LIST_TYPE] = listType,
        [TEMPLATE_PARAMETER_SHOW_ID] = isShowingID and ARG_SHOW_FLAG_VALUE
    }
 
    return currentFrame:expandTemplate{ title = TEMPLATE_LIST_ITEM, args = templateParameters }
end
 
---makeMarkupForListItems
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForListItems(listType, isShowingID)
 
    local markup = ""
    for _, id in ipairs(altarIDs) do
        markup = markup .. makeMarkupPerListItem(id, listType, isShowingID)
    end
 
    return markup
end
 
---renderList
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function renderList(listType, isShowingID)
 
    return makeMarkupForListItems(listType, isShowingID)
end
 
---makeMarkupPerLink
---@param id string the id of the cornerstone to display
---@param isShowingID boolean true when needing to show the ID column
---@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 makeMarkupPerLink(id, isShowingID)
 
    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_ICON_SIZE] = TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT,
    }
 
    local link = currentFrame:expandTemplate{ title = TEMPLATE_INLINE_LINK, args = templateParameters }
 
    if isShowingID then
        return link .. " ('" .. id .. "')"
    else
        return link
    end
end


    loadIDs(selectedID, searchName, searchTerm, excludeList)
---makeMarkupForInlineLinks
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForInlineLinks(isShowingID)


     if #altarIDs < 1 then
     local markup = ""
         return "No Stormforged Cornerstones found."
    for i, id in ipairs(altarIDs) do
        if i > 1 then
            markup = markup .. ", "
        end
         markup = markup .. makeMarkupPerLink(id, isShowingID)
     end
     end


     desiredCaption = resolveCaption(selectedID, searchName, searchTerm, desiredCaption)
     return markup
end
 
---renderInline
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function renderInlineLinks(isShowingID)


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


Line 298: Line 426:
function StormforgedController.main(frame)
function StormforgedController.main(frame)


     -- Extract template parameters
     -- Selection parameters
     local selectedID = frame.args[ARG_ID]
     local selectList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_ID_LIST], false)
    local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARGS_EXCLUDE_LIST], true)
     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 regularID = frame.args[ARG_UPGRADABLE_ID]
 
    -- Display parameters
     local desiredCaption = frame.args[ARG_CAPTION]
     local desiredCaption = frame.args[ARG_CAPTION]
     local skippingPrice = frame.args[ARG_SKIP_PRICES]
     local displayOverride = frame.args[ARG_DISPLAY_OVERRIDE]
    local listType = frame.args[ARG_LIST_TYPE]
    local isSkippingPrices = ARG_SKIP_FLAG_VALUE == frame.args[ARG_SKIP_PRICES]
    local isShowingID = ARG_SHOW_FLAG_VALUE == frame.args[ARG_SHOW_ID]


     --TODO make displayOverride == DISPLAY_OPTION_LIST
     -- For clarity, copy each value instead of reusing the whole args table.
    local argumentsToPassThroughToTable = {
        [ARG_SKIP_PRICES] = frame.args[ARG_SKIP_PRICES],
        [ARG_SHOW_ID] = frame.args[ARG_SHOW_ID],
        [ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION],
        [ARG_SHOW_REGULAR] = frame.args[ARG_SHOW_REGULAR]
    }


     local excludeList = {}
     currentFrame = frame
     if exclusions and exclusions ~= "" then
 
         excludeList = expandExclusions(exclusions)
    loadIDs(selectList, searchName, searchDesc, regularID, excludeList)
     if #altarIDs < 1 then
         return "No matching Stormforged Cornerstones found."
     end
     end


     currentFrame = frame
     if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_LIST then
     return renderTable(selectedID, searchName, searchTerm, excludeList, desiredCaption, skippingPrice)
        return renderList(listType, isShowingID)
    end
     if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_INLINE then
        return renderInlineLinks(isShowingID)
    end
 
    -- Default display is table.
    desiredCaption = resolveCaption(desiredCaption, selectList, searchName, searchDesc, regularID)
 
    return renderTable(desiredCaption, isSkippingPrices, argumentsToPassThroughToTable)
end
end



Latest revision as of 03:38, 5 May 2024

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

---
--- Serves the Stormforged Cornerstones template by capturing input and using it to control the display of data.
---
---@module StormforgedController
local StormforgedController = {}



--region Dependencies

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

local ControllerUtilities = require("Module:ControllerUtilities")

--endregion



--region Private constants

local ARG_ID_LIST = "id"
local ARG_NAME = "name"
local ARG_DESCRIPTION = "description"
local ARG_UPGRADABLE_ID = "upgrades_from_id"
local ARGS_EXCLUDE_LIST = "exclude"
local ARG_CAPTION = "caption"
local ARG_SKIP_PRICES = "skip_prices"
local ARG_SKIP_FLAG_VALUE = "skip"
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_DESCRIPTION = "show_description"
local ARG_SHOW_REGULAR = "show_regular"
local ARG_SHOW_FLAG_VALUE = "show"
local ARG_DISPLAY_OVERRIDE = "display"
local ARG_DISPLAY_OVERRIDE_OPTION_LIST = "list"
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_LIST_TYPE = "list_type"

local TEMPLATE_PARAMETER_CAPTION = ARG_CAPTION
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_DESC = ARG_DESCRIPTION
local TEMPLATE_PARAMETER_PRICE_META = "price_in_meta_resources"
local TEMPLATE_PARAMETER_PRICE_VILLAGER = "price_in_villagers"
local TEMPLATE_PARAMETER_UPGRADE_META = "price_for_upgrade_in_meta_resources"
local TEMPLATE_PARAMETER_UPGRADE_VILLAGER = "price_for_upgrade_in_villagers"
local TEMPLATE_PARAMETER_SHOW_ID = ARG_SHOW_ID
local TEMPLATE_PARAMETER_UPGRADE_FROM = "upgrades_from"
local TEMPLATE_PARAMETER_LIST_TYPE = ARG_LIST_TYPE
local TEMPLATE_PARAMETER_ICON_SIZE = "icon_size"
local TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT = "none"

local TEMPLATE_TABLE_BASE = "StormforgedCornerstonesTable"
local TEMPLATE_TABLE_SUFFIX_ROW = "/row"
local TEMPLATE_TABLE_SUFFIX_END = "/end"
local TEMPLATE_TABLE_SKIP_PRICES_VARIANT = "/SkipPrices"
local TEMPLATE_LIST_ITEM = "StormforgedCornerstonesList/item"
local TEMPLATE_INLINE_LINK = "pl"

--endregion



--region Private member variables

local currentFrame = {}

local altarIDs = {}
local isIDAlreadyAdded = {}

--endregion



--region Private methods

--- 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.
---
--- Works from the member variable altarIDs
---
local function validateNamesFromIDs()

    for _, id in ipairs(altarIDs) 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 altarIDs. 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] and not isIDAlreadyAdded[id] then

            table.insert(altarIDs, id)
            isIDAlreadyAdded[id] = true
        end
    end
end

--- Requests the desired lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variable altarIDs.
---
---@param selectList table list of ids of the cornerstone to include
---@param searchName string search term to look in cornerstone names
---@param searchDesc string search term to look in cornerstone descriptions
---@param regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@param excludeList table list of ids
local function loadIDs(selectList, searchName, searchDesc, regularID, excludeList)

    altarIDs = {}
    isIDAlreadyAdded = {}
    local attemptedSearch = false

    -- Load the selected IDs
    if selectList then
        loadStormforgedIDs(selectList, excludeList)
        -- It should always be true, but it can be blank.
        if #selectList > 0 then
            attemptedSearch = true
        end
    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

    -- Add any upgrade matches
    if regularID and regularID ~= "" then
        loadStormforgedIDs({ AltarEffectsData.getUpgradeWhereRegularID(regularID) }, excludeList)
        attemptedSearch = true
    end

    -- Or, if no searching was even attempted (regardless of result), show them all.
    if not attemptedSearch then
        local newIDs = AltarEffectsData.getAllAltarIDs()
        loadStormforgedIDs(newIDs, excludeList)
    end

    if #altarIDs > 0 then
        -- Remove all IDs that aren't for Stormforged Cornerstones--there will be extra regular Perks pulled from PerksData whenever there are search terms.
        validateNamesFromIDs()
        -- Alphabetize by name.
        table.sort(altarIDs, PerksData.compareNames)
    end
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 desiredCaption string the caption requested, if any
---@param selectList table list of ids of the cornerstone to include
---@param searchName string the second search parameter, a name
---@param searchDesc string the third search parameter, a term in a description
---@param regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@return string an appropriate caption, or blank if it should be default
local function resolveCaption(desiredCaption, selectList, searchName, searchDesc, regularID)

    -- No matter what, if the author provided a caption, use that.
    if desiredCaption and desiredCaption ~= "" then
        return desiredCaption
    end

    -- If no selection, delegate to template.
    local addedToText = false

    desiredCaption = "" .. #altarIDs .. " Stormforged Cornerstones"

    if selectList and #selectList > 0 then
        if #selectList == 1 then
            desiredCaption = desiredCaption .. " with ID '" .. selectList[1] .. "'"
        else
            desiredCaption = desiredCaption .. " with selected IDs"
        end
        addedToText = true
    end

    if searchName and searchName ~= "" then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " named '" .. searchName .. "'"
        addedToText = true
    end

    if searchDesc and searchDesc ~= "" then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " mentioning '" .. searchDesc .. "'"
        addedToText = true
    end

    if regularID and regularID ~= "" then
        if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " upgraded from '" .. regularID .. "'"
        addedToText = true
    end

    if excludeList and #excludeList > 0 then
        desiredCaption = desiredCaption .. " (with exclusions)"
        addedToText = true
    end

    if addedToText then
        return desiredCaption
    else
        -- Delegate to the template for the default
        return ""
    end
end

---resolveStartTemplate
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
local function resolveTableStartTemplate(isSkippingPrices)

    local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""

    return TEMPLATE_TABLE_BASE .. suffixIfSkipping
end

---resolveRowTemplate
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
local function resolveTableRowTemplate(isSkippingPrices)

    local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""

    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_ROW .. suffixIfSkipping
end

---resolveEndTemplate
---@return string the template name to use
local function resolveTableEndTemplate()

    return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_END
end

--- Calls the view (other templates) to render the beginning of the table.
---
---@param desiredCaption string the caption for the table, if any
---@param isSkippingPrices boolean true if skipping variant should be used
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableStart(desiredCaption, isSkippingPrices, argsForTableTemplate)

    argsForTableTemplate[TEMPLATE_PARAMETER_CAPTION] = desiredCaption

    return currentFrame:expandTemplate{ title = resolveTableStartTemplate(isSkippingPrices), args = argsForTableTemplate }
end

--- Calls the view (other templates) to render a single row of the table with data based on the provided identifier.
---
---@param id string the unique identifier of a Stormforged Cornerstone to use to add data to a new table row
---@param isSkippingPrices boolean true if skipping variant should be used
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerRow(id, isSkippingPrices, argsForTableTemplate)

    argsForTableTemplate[TEMPLATE_PARAMETER_ID] = id
    argsForTableTemplate[TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)

    argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_FROM] = AltarEffectsData.getRegularVersionEffectID(id)

    -- This is default, but adds a lot of extra template overhead if it's not needed, so only add it if necessary.
    if not isSkippingPrices then
        argsForTableTemplate[TEMPLATE_PARAMETER_PRICE_META] = AltarEffectsData.getPriceInMetaResources(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_PRICE_VILLAGER] = AltarEffectsData.getPriceInVillagers(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_META] = AltarEffectsData.getUpgradePriceInMetaResources(id)
        argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_VILLAGER] = AltarEffectsData.getUpgradePriceInVillagers(id)
    end

    return currentFrame:expandTemplate{ title = resolveTableRowTemplate(isSkippingPrices), args = argsForTableTemplate }
end

--- Handles assembling the rows for all of the IDs in the member variable list altarIDs.
---
---@param isSkippingPrices boolean true if skipping variant should be used
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableRows(isSkippingPrices, argsForTableTemplate)

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

    return markup
end

--- Calls the view (other templates) to render the end of the table.
---
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableEnd()

    return currentFrame:expandTemplate{ title = resolveTableEndTemplate(), 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 string override the table caption
---@param isSkippingPrices boolean true if skipping variant should be used
---@param argsForTableTemplate table a set of arguments ready to pass through to the called template
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(desiredCaption, isSkippingPrices, argsForTableTemplate)

    local startMarkup = makeMarkupForTableStart(desiredCaption, isSkippingPrices, argsForTableTemplate)
    local rowsMarkup =  makeMarkupForTableRows(isSkippingPrices, argsForTableTemplate)

    return startMarkup .. rowsMarkup .. makeMarkupForTableEnd()
end

---makeMarkupPerListITem
---@param id string the id of the cornerstone to display
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerListItem(id, listType, isShowingID)

    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_LIST_TYPE] = listType,
        [TEMPLATE_PARAMETER_SHOW_ID] = isShowingID and ARG_SHOW_FLAG_VALUE
    }

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

---makeMarkupForListItems
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForListItems(listType, isShowingID)

    local markup = ""
    for _, id in ipairs(altarIDs) do
        markup = markup .. makeMarkupPerListItem(id, listType, isShowingID)
    end

    return markup
end

---renderList
---@param listType string the type of list to create
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function renderList(listType, isShowingID)

    return makeMarkupForListItems(listType, isShowingID)
end

---makeMarkupPerLink
---@param id string the id of the cornerstone to display
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupPerLink(id, isShowingID)

    local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_ICON_SIZE] = TEMPLATE_PARAMETER_ICON_SIZE_DEFAULT,
    }

    local link = currentFrame:expandTemplate{ title = TEMPLATE_INLINE_LINK, args = templateParameters }

    if isShowingID then
        return link .. " ('" .. id .. "')"
    else
        return link
    end
end

---makeMarkupForInlineLinks
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForInlineLinks(isShowingID)

    local markup = ""
    for i, id in ipairs(altarIDs) do
        if i > 1 then
            markup = markup .. ", "
        end
        markup = markup .. makeMarkupPerLink(id, isShowingID)
    end

    return markup
end

---renderInline
---@param isShowingID boolean true when needing to show the ID column
---@return string the wiki markup assembled by the view (other templates)
local function renderInlineLinks(isShowingID)

    return makeMarkupForInlineLinks(isShowingID)
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)

    -- Selection parameters
    local selectList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARG_ID_LIST], false)
    local excludeList = ControllerUtilities.expandCommaSeparatedStringsIntoTable(frame.args[ARGS_EXCLUDE_LIST], true)
    local searchName = frame.args[ARG_NAME]
    local searchDesc = frame.args[ARG_DESCRIPTION]
    local regularID = frame.args[ARG_UPGRADABLE_ID]

    -- Display parameters
    local desiredCaption = frame.args[ARG_CAPTION]
    local displayOverride = frame.args[ARG_DISPLAY_OVERRIDE]
    local listType = frame.args[ARG_LIST_TYPE]
    local isSkippingPrices = ARG_SKIP_FLAG_VALUE == frame.args[ARG_SKIP_PRICES]
    local isShowingID = ARG_SHOW_FLAG_VALUE == frame.args[ARG_SHOW_ID]

    -- For clarity, copy each value instead of reusing the whole args table.
    local argumentsToPassThroughToTable = {
        [ARG_SKIP_PRICES] = frame.args[ARG_SKIP_PRICES],
        [ARG_SHOW_ID] = frame.args[ARG_SHOW_ID],
        [ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION],
        [ARG_SHOW_REGULAR] = frame.args[ARG_SHOW_REGULAR]
    }

    currentFrame = frame

    loadIDs(selectList, searchName, searchDesc, regularID, excludeList)
    if #altarIDs < 1 then
        return "No matching Stormforged Cornerstones found."
    end

    if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_LIST then
        return renderList(listType, isShowingID)
    end
    if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_INLINE then
        return renderInlineLinks(isShowingID)
    end

    -- Default display is table.
    desiredCaption = resolveCaption(desiredCaption, selectList, searchName, searchDesc, regularID)

    return renderTable(desiredCaption, isSkippingPrices, argumentsToPassThroughToTable)
end

--endregion

return StormforgedController