Module:StormforgedController: Difference between revisions

From Against the Storm Official Wiki
(Fixed duplication)
(finished refactoring for list and inline display versions)
 
(10 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_DESCRIPTION = "description"
local ARG_DESCRIPTION = "description"
local ARG_UPGRADABLE_ID = "upgrade_from_id"
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 ARG_SHOW_DESCRIPTION = "show_description"
local ARG_SHOW_REGULAR = "show_regular"
local ARG_SHOW_REGULAR = "show_regular"
local ARG_SHOW_REGULAR_FLAG_VALUE = "show"
local ARG_SHOW_FLAG_VALUE = "show"
 
local ARG_DISPLAY_OVERRIDE = "display"
--local DISPLAY_OPTION_TABLE = "table"
local ARG_DISPLAY_OVERRIDE_OPTION_LIST = "list"
local ARG_DISPLAY_OVERRIDE_OPTION_INLINE = "inline"
local ARG_LIST_TYPE = "list_type"


local TEMPLATE_PARAMETER_CAPTION = "caption"
local TEMPLATE_PARAMETER_CAPTION = ARG_CAPTION
local TEMPLATE_PARAMETER_SKIP_PRICES = "skip_prices"
local TEMPLATE_PARAMETER_ID = ARG_ID_LIST
local TEMPLATE_PARAMETER_ID = "id"
local TEMPLATE_PARAMETER_DESC = ARG_DESCRIPTION
local TEMPLATE_PARAMETER_DESC = "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 56: Line 64:


local currentFrame = {}
local currentFrame = {}
local altarIDs = {}
local altarIDs = {}
local isIDAlreadyAdded = {}
local isIDAlreadyAdded = {}
Line 65: 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 validateNamesFromIDs()


Line 101: 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 116: Line 105:
end
end


--- Requests the lists of IDs from both AltarEffects and Perks data models, applies some post-processing, and stores them in the member variable.
--- 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 selectedID string the id of the cornerstone to include
---@param selectList table list of ids of the cornerstone to include
---@param searchName string search term to look in cornerstone names
---@param searchName string search term to look in cornerstone names
---@param searchDesc string search term to look in cornerstone descriptions
---@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 regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@param excludeList table list of ids
---@param excludeList table list of ids
local function loadIDs(selectedID, searchName, searchDesc, regularID, excludeList)
local function loadIDs(selectList, searchName, searchDesc, regularID, excludeList)
 
    if not altarIDs or #altarIDs < 1 then


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


        -- Load the selected ID
    -- Load the selected IDs
        if selectedID and selectedID ~= "" then
    if selectList then
            loadStormforgedIDs( { selectedID }, excludeList )
        loadStormforgedIDs(selectList, excludeList)
        -- It should always be true, but it can be blank.
        if #selectList > 0 then
             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 searchDesc and searchDesc ~= "" then
    if searchDesc and searchDesc ~= "" then
            local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDesc)
        local newIDs = PerksData.getAllPerkIDsWhereDescription(searchDesc)
            loadStormforgedIDs(newIDs, excludeList)
        loadStormforgedIDs(newIDs, excludeList)
            attemptedSearch = true
        attemptedSearch = true
        end
    end


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


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


     if #altarIDs > 0 then
     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.
         -- Remove all IDs that aren't for Stormforged Cornerstones--there will be extra regular Perks pulled from PerksData whenever there are search terms.
         validateNamesFromIDs()
         validateNamesFromIDs()
         --Alphabetize by name
         -- Alphabetize by name.
         table.sort(altarIDs, compareNames)
         table.sort(altarIDs, PerksData.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
     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 190: 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 searchDesc string the third search parameter, a term in a description
---@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
---@param regularID string the id of the cornerstone that gets upgraded to a Stormforged version
---@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, searchDesc, regularID, 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
 
    if searchName and searchName ~= "" then
         if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " named '" .. searchName .. "'"
        addedToText = true
     end
     end
    -- Do this first since mentioning could also imply name, but not the other way around.
 
     if searchDesc and searchDesc ~= "" then
     if searchDesc and searchDesc ~= "" then
         return "Stormforged Cornerstones mentioning '" .. searchDesc .. "'"
         if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " mentioning '" .. searchDesc .. "'"
        addedToText = true
     end
     end
     if searchName and searchName ~= "" then
 
         return "Stormforged Cornerstones named '" .. searchName .. "'"
     if regularID and regularID ~= "" then
         if addedToText then
            desiredCaption = desiredCaption .. " and"
        end
        desiredCaption = desiredCaption .. " upgraded from '" .. regularID .. "'"
        addedToText = true
     end
     end
     if regularID and regularID ~= "" then
 
         return "Stormforged Cornerstones upgraded from '" .. regularID .. "'"
     if excludeList and #excludeList > 0 then
         desiredCaption = desiredCaption .. " (with exclusions)"
        addedToText = true
     end
     end


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


---resolveStartTemplate
---resolveStartTemplate
---@param skippingPrice string the flag deciding whether to skip some columns
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
---@return string the template name to use
local function resolveStartTemplate(skippingPrice)
local function resolveTableStartTemplate(isSkippingPrices)


     local suffixIfSkipping = (skippingPrice == ARG_SKIP_PRICES_FLAG_VALUE and TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT) or ""
     local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""


     return TEMPLATE_CONTAINER_START .. suffixIfSkipping
     return TEMPLATE_TABLE_BASE .. suffixIfSkipping
end
end


---resolveRowTemplate
---resolveRowTemplate
---@param skippingPrice string the flag deciding whether to skip some columns
---@param isSkippingPrices boolean true if skipping variant should be used
---@return string the template name to use
---@return string the template name to use
local function resolveRowTemplate(skippingPrice)
local function resolveTableRowTemplate(isSkippingPrices)


     local suffixIfSkipping = (skippingPrice == ARG_SKIP_PRICES_FLAG_VALUE and TEMPLATE_CONTAINER_SKIP_PRICES_VARIANT) or ""
     local suffixIfSkipping = (isSkippingPrices and TEMPLATE_TABLE_SKIP_PRICES_VARIANT) or ""


     return TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_ROW .. suffixIfSkipping
     return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_ROW .. suffixIfSkipping
end
end


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


     return TEMPLATE_CONTAINER_START .. TEMPLATE_CONTAINER_SUFFIX_END
     return TEMPLATE_TABLE_BASE .. TEMPLATE_TABLE_SUFFIX_END
end
end


Line 250: 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, showingRegular)
---@return string the wiki markup assembled by the view (other templates)
local function makeMarkupForTableStart(desiredCaption, isSkippingPrices, argsForTableTemplate)


     local templateParameters = {
     argsForTableTemplate[TEMPLATE_PARAMETER_CAPTION] = desiredCaption
        [TEMPLATE_PARAMETER_CAPTION] = desiredCaption,
        [TEMPLATE_PARAMETER_SKIP_PRICES] = skippingPrice
    }


     return currentFrame:expandTemplate{ title = resolveStartTemplate(skippingPrice), args = templateParameters }
     return currentFrame:expandTemplate{ title = resolveTableStartTemplate(isSkippingPrices), args = argsForTableTemplate }
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 function makeMarkupPerRow(id, isSkippingPrices, argsForTableTemplate)


     local templateParameters = {
     argsForTableTemplate[TEMPLATE_PARAMETER_ID] = id
        [TEMPLATE_PARAMETER_ID] = id,
    argsForTableTemplate[TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)
        [TEMPLATE_PARAMETER_DESC] = PerksData.getDescriptionByID(id)
 
     }
     argsForTableTemplate[TEMPLATE_PARAMETER_UPGRADE_FROM] = AltarEffectsData.getRegularVersionEffectID(id)


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


     return currentFrame:expandTemplate{ title = resolveRowTemplate(skippingPrice), args = templateParameters }
     return currentFrame:expandTemplate{ title = resolveTableRowTemplate(isSkippingPrices), args = argsForTableTemplate }
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 300: 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()


     return currentFrame:expandTemplate{ title = resolveEndTemplate(), args = {} }
     return currentFrame:expandTemplate{ title = resolveTableEndTemplate(), args = {} }
end
end


Line 310: 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 selectedID string the id 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
---@param desiredCaption string override the table caption
---@param desiredCaption string override the table caption
---@param skippingPrice string flag if some columns should be skipped
---@param isSkippingPrices boolean true if skipping variant should be used
---@param showingRegular string flag if columns should be added
---@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)
---@return string the wiki markup assembled by the view (other templates)
local function renderTable(selectedID, searchName, searchDesc, regularID, excludeList, desiredCaption, skippingPrice, showingRegular)
local function makeMarkupPerListItem(id, listType, isShowingID)


     loadIDs(selectedID, searchName, searchDesc, regularID, excludeList)
     local templateParameters = {
        [TEMPLATE_PARAMETER_ID] = id,
        [TEMPLATE_PARAMETER_LIST_TYPE] = listType,
        [TEMPLATE_PARAMETER_SHOW_ID] = isShowingID and ARG_SHOW_FLAG_VALUE
    }


     if #altarIDs < 1 then
    return currentFrame:expandTemplate{ title = TEMPLATE_LIST_ITEM, args = templateParameters }
         return "No matching Stormforged Cornerstones found."
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
     end


     desiredCaption = resolveCaption(selectedID, searchName, searchDesc, regularID, desiredCaption)
     return markup
end


    local startMarkup = makeMarkupForTableStart(desiredCaption, skippingPrice, showingRegular)
---renderInline
    local rowsMarkup =  makeMarkupForTableRows(skippingPrice, showingRegular)
---@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 startMarkup .. rowsMarkup .. makeMarkupForTableEnd()
     return makeMarkupForInlineLinks(isShowingID)
end
end


Line 346: 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 searchDesc = frame.args[ARG_DESCRIPTION]
     local searchDesc = frame.args[ARG_DESCRIPTION]
     local regularID = frame.args[ARG_UPGRADABLE_ID]
     local regularID = frame.args[ARG_UPGRADABLE_ID]
     local exclusions = frame.args[ARGS_EXCLUDE_LIST]
 
     -- 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 showingRegular = frame.args[ARG_SHOW_REGULAR]
    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]
    }


     --TODO make displayOverride == DISPLAY_OPTION_LIST
     currentFrame = frame


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


     currentFrame = frame
     if displayOverride == ARG_DISPLAY_OVERRIDE_OPTION_LIST then
     return renderTable(selectedID, searchName, searchDesc, regularID, excludeList, desiredCaption, skippingPrice, showingRegular)
        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