Module:PerksData: Difference between revisions
From Against the Storm Official Wiki
m changed method names |
Changed a method to public; had to rewrite it. |
||
| Line 150: | Line 150: | ||
--region Private methods | --region Private methods | ||
--- | --- | ||
| Line 439: | Line 420: | ||
return #sourcesTable | return #sourcesTable | ||
end | end | ||
--- | |||
--- Checks all the sources to see whether the specified source is found. | |||
--- | |||
---@param | |||
function PerksData.isAnySource(perkID, sourceToCheck) | |||
local perk = PerksData.getAllDataForPerkByID(perkID) | |||
if not perk then | |||
return nil | |||
end | |||
for _, source in ipairs(perk[INDEX_SOURCES]) do | |||
if source == sourceToCheck then | |||
return true | |||
end | |||
end | |||
return false | |||
end | |||
| Line 451: | Line 457: | ||
function PerksData.isFromCornerstoneByID(perkID) | function PerksData.isFromCornerstoneByID(perkID) | ||
return PerksData.isAnySource(perkID, SOURCE_CORNERSTONE) | |||
end | end | ||
| Line 471: | Line 471: | ||
function PerksData.isFromOrderByID(perkID) | function PerksData.isFromOrderByID(perkID) | ||
return PerksData.isAnySource(perkID, SOURCE_ORDER) | |||
end | end | ||
| Line 491: | Line 485: | ||
function PerksData.isFromEventByID(perkID) | function PerksData.isFromEventByID(perkID) | ||
return PerksData.isAnySource(perkID, SOURCE_RELIC) | |||
end | end | ||
| Line 511: | Line 499: | ||
function PerksData.isFromTraderByID(perkID) | function PerksData.isFromTraderByID(perkID) | ||
return PerksData.isAnySource(perkID, SOURCE_TRADER) | |||
end | end | ||
| Line 549: | Line 531: | ||
---@param perkID string the ID of the perk | ---@param perkID string the ID of the perk | ||
---@param rarityToCheck string of the rarity constants | ---@param rarityToCheck string of the rarity constants | ||
---@return boolean whether the perk | ---@return boolean whether the perk has the specified rarity | ||
function PerksData.isRarityMatchByID(perkID, rarityToCheck) | function PerksData.isRarityMatchByID(perkID, rarityToCheck) | ||
Revision as of 14:56, 3 December 2023
Documentation for this module may be created at Module:PerksData/doc
---
--- Module for compiling perks information from wiki data sources. Restructures
--- the flat data tables that are produced from CsvUtils to make them more
--- conducive to Lua methods that need to display the information.
---
--- The standard way of using this module is a call like the following:
---
--- iconFilename = PerksData.getIconFilename(perkID)
---
--- This will return a string corresponding to the filename of the icon of the
--- perk, including the .png extension. It is preferable to call getter methods
--- with the ID of the perk rather than retrieving the entire record for the
--- perk, so that your code stays protected from variations in this module.
--- The getter methods are all called with the ID of the perk, not their
--- plain-language display name, so there are many duplicates.
---
--- * plainLanguageName = PerksData.getNameByID(id)
--- * longDescription = PerksData.getDescriptionByID(id)
--- * iconFilename = PerksData.getIconByID(id)
--- * isFromCornerstone = PerksData.isFromCornerstoneByID(id)
--- * isFromTrader = PerksData.isFromTraderByID(id)
--- * isFromOrder = PerksData.isFromOrderByID(id)
--- * isFromEvent = PerksData.isFromEventByID(id)
--- * rarityConstant = PerksData.getRarityByID(id)
--- * isMatch = PerksData.isRarityMatchByID(id, rarity)
--- * isUncommon = PerksData.isRarityUncommonByID(id)
--- * isRare = PerksData.isRarityRareByID(id)
--- * isEpic = PerksData.isRarityEpicByID(id)
--- * isLegendary = PerksData.isRarityLegendaryByID(id)
--- * price = PerksData.getPriceByID(id)
---
--- Note that display names, or plain language names, are very likely to have
--- duplicates in the list of perks.
---
--- You can also use this method, although it is more likely to break.
---
--- * enumValue = PerksData.getRarity(id)
---
--- The rarity constants are public and may be used. You are encouraged to
--- refer directly to the constants themselves rather than comparing them to
--- what might seem to be appropriate string literals, like `== "Epic"`. For
--- example, use this kind of statement instead.
---
--- if PerksData.getRarity(perkID) == PerksData.RARITY_EPIC then
---
--- Then render your own string literal to display. This guidance may change
--- in the future.
---
--- There is a method to retrieve the table of sources, but it is advised to
--- instead use the getter methods isFromTrader, isFromOrder, and isFromEvent
--- instead. If you must get the table, there is a getAll method:
---
--- * sourcesTable = PerksData.getAllSources(id)
---
--- As a last resort, or if you need to transform the data structure, you can
--- call the method getAllDataForPerkByID(id). This returns a whole record from
--- the data table.
---
--- The data table for perks has the following structure:
---
--- perksTable = {
--- ["perk1_ID"] = {
--- ["id"] = "perk1_ID",
--- ["displayName"] = "Plain Language Name",
--- ["description"] = "A long string with some HTML entities too.",
--- ["iconFilename"] = "Icon_filename.png",
--- ["sources"] = {
--- [1] = SOURCE_ORDER, for examples
--- [2] = SOURCE_RELIC,
--- [3] = SOURCE_CORNERSTONE,
--- [4] = ... or missing if fewer
--- },
--- ["rarity"] = PerksData.RARITY_EPIC, for example
--- ["price"] = 99
--- },
--- ["perk2_ID"] = {
--- ...
--- },
--- ["perk3_ID"] = {
--- ...
--- },
--- ...
--- }
---
--- @module PerksData
local PerksData = {}
local CsvUtils = require("Module:CsvUtils")
--region Private member variables
--- Main data tables, like this: table[ID] = table containing data for that ID
local perksTable
--- Supporting table, list of names table[index] = ID.
local perksIDs
--- Lookup maps. Built once and reused on all subsequent calls within this
--- session, like this: table[displayName] = ID
local mapNamesToIDs
--endregion
--region Public enums
PerksData.RARITY_UNCOMMON = "Uncommon"
PerksData.RARITY_RARE = "Rare"
PerksData.RARITY_EPIC = "Epic"
PerksData.RARITY_LEGENDARY = "Legendary"
--endregion
--region Private constants
local DATA_TEMPLATE_NAME = "Template:Perks_csv"
local VALUE_DIVISOR = 10
local INDEX_ID = "id"
local INDEX_NAME = "displayName"
local INDEX_DESCRIPTION = "description"
local INDEX_ICON_FILENAME = "iconFilename"
local INDEX_SOURCES = "sources"
local INDEX_RARITY = "rarity"
local INDEX_PRICE = "price"
local SOURCE_CORNERSTONE = "Cornerstone"
local SOURCE_ORDER = "Order"
local SOURCE_RELIC = "Relic"
local SOURCE_TRADER = "Trader"
local SWITCH_RARITIES_CHECK = {
[PerksData.RARITY_UNCOMMON] = true,
[PerksData.RARITY_RARE] = true,
[PerksData.RARITY_EPIC] = true,
[PerksData.RARITY_LEGENDARY] = true
}
--endregion
--region Private methods
---
--- Removes some markup and processing leftovers from the CSV extraction.
---
---@param originalPerkDescription string the perk's description
---@return string the cleaned description
local function cleanDescription(originalPerkDescription)
-- remove the <sprite> tags completely
originalPerkDescription = originalPerkDescription:gsub("<sprite[^>]+>%s?", "")
-- remove the HTML entities from the beginning and end
originalPerkDescription = originalPerkDescription:gsub("^"(.*)"$", "%1")
-- swap any last duplicated quotes
return originalPerkDescription:gsub("""", """)
end
---
--- Creates a new subtable containing the sources of the specified perk.
---
---@param originalPerkSources string list from which to extract sources
---@return table subtable with the sources
local function makeSourcesSubtable(originalPerkSources)
local sources = {}
-- Sub in any HTML entities for actual commas. This was originally critical
-- to have the CSV data correctly parsed, but now we have to undo it to
-- correctly split up these values.
originalPerkSources = originalPerkSources:gsub(",", ",")
originalPerkSources = originalPerkSources:gsub(""", "")
-- Split on commas. We can use the + here because there are no blanks.
for sourceName in originalPerkSources:gmatch("([^,]+)") do
table.insert(sources, sourceName)
end
return sources
end
---
--- Transforms the oldPerksTable returned from CSV processing to be more
--- conducive to member functions looking up data. Essentially, we convert the
--- text strings into tables with the same base name and an index.
---
---@param originalPerksTable table of CSV-based table, with a header row, data rows
---@return table better structured with IDs as keys
local function restructurePerksTable(originalPerksTable)
-- A few constants we'll need only within this function.
local DATA_ROWS = 2
local INDEX_OLD_ID = 1
local INDEX_OLD_NAME = 2
local INDEX_OLD_DESCRIPTION = 3
local INDEX_OLD_ICON_FILENAME = 4
local INDEX_OLD_SOURCES = 5
local INDEX_OLD_RARITY = 6
local INDEX_OLD_PRICE = 7
perksIDs = {}
mapNamesToIDs = {}
local newPerksTable = {}
for _, originalPerk in ipairs(originalPerksTable[DATA_ROWS]) do
-- Copy over the content, mapping unhelpful indexes into header keys
local newPerk = {}
newPerk[INDEX_ID] = originalPerk[INDEX_OLD_ID]
newPerk[INDEX_NAME] = originalPerk[INDEX_OLD_NAME]
newPerk[INDEX_DESCRIPTION] = cleanDescription(originalPerk[INDEX_OLD_DESCRIPTION])
newPerk[INDEX_ICON_FILENAME] = originalPerk[INDEX_OLD_ICON_FILENAME] .. ".png"
newPerk[INDEX_RARITY] = originalPerk[INDEX_OLD_RARITY]
newPerk[INDEX_PRICE] = originalPerk[INDEX_OLD_PRICE] / VALUE_DIVISOR
newPerk[INDEX_SOURCES] = makeSourcesSubtable(originalPerk[INDEX_OLD_SOURCES])
newPerksTable[ newPerk[INDEX_ID] ] = newPerk
table.insert(perksIDs, newPerk[INDEX_ID])
-- Also populate the map for looking up IDs with display names
if not mapNamesToIDs[ newPerk[INDEX_NAME] ] then
mapNamesToIDs[ newPerk[INDEX_NAME] ] = {}
end
table.insert(mapNamesToIDs[ newPerk[INDEX_NAME] ], newPerk[INDEX_ID])
end
return newPerksTable
end
---
--- Data loader function that uses the utility module and restructures the data
--- to be easier to access for invoking methods and later method calls. This
--- method is automatically called by all public member functions if the main
--- data table has not yet been populated in the current session.
local function loadData()
-- Utility module retrieves the data as basic, flat lua tables.
-- Doesn't use the header lookup.
local originalPerksTable, _ = CsvUtils.luaTableFromCSV(CsvUtils.extractCSV(DATA_TEMPLATE_NAME))
-- Now restructure to be more conducive.
perksTable = restructurePerksTable(originalPerksTable)
end
--endregion
--region Public methods
---
--- Retrieve the whole table of data for the specified perk. Instead of this,
--- you should probably be calling the individual getter methods.
---
--- Throws an error if called with nil or empty string. Returns nil if the
--- specified perk cannot be found.
---
--- @param id string ID of the perk
--- @return table containing the data for the specified perk with key-value pairs, or nil if not found
function PerksData.getAllDataForPerkByID(id)
-- At runtime, this should never be nil or empty.
if not id or id == "" then
error("Parameter is nil or empty for the perk's ID.")
end
if not perksTable then
loadData()
end
return perksTable[id]
end
---
--- Get the IDs of all the Perks that are named the same thing.
---
--- Returns a table of IDs, since it's unknown how many there could be.
---
--- @param displayName string plain-language name of the perk
--- @return table of IDs of the Perk found, or an empty table if none found
function PerksData.getAllPerkIDsByName(displayName)
if not perksTable then
loadData()
end
local foundPerkIDs = mapNamesToIDs[displayName]
if not foundPerkIDs or #foundPerkIDs == 0 then
return {}
end
return foundPerkIDs
end
---
--- Retrieves the display name of the perk specified by its ID.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return string the name of the perk
function PerksData.getNameByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_NAME]
end
---
--- Retrieves the description of the perk specified by its ID.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return string the description of the perk
function PerksData.getDescriptionByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_DESCRIPTION]
end
---
--- Retrieves the display name of the perk specified by its ID.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return string the icon filename of the perk, including the .png extension
function PerksData.getIconByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_ICON_FILENAME]
end
---
--- Retrieves all the sources of the perk specified by its ID.
---
--- Returns nil if the perk was not found.
---
---@param perkID string the ID of the perk
---@return table of sources, strings
function PerksData.getAllSourcesByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_SOURCES]
end
---
--- Retrieves the number of sources of the perk specified by its ID.
---
--- Returns nil if the perk was not found.
---
---@param perkID string the ID of the perk
---@return number of sources for this perk
function PerksData.getNumberOfSourcesByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
local sourcesTable = perk[INDEX_SOURCES]
if not sourcesTable then
return nil
end
return #sourcesTable
end
---
--- Checks all the sources to see whether the specified source is found.
---
---@param
function PerksData.isAnySource(perkID, sourceToCheck)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
for _, source in ipairs(perk[INDEX_SOURCES]) do
if source == sourceToCheck then
return true
end
end
return false
end
---
--- Retrieves whether the perk specified by its ID comes from a Cornerstone.
---
--- Returns nil if the perk was not found.
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Cornerstone
function PerksData.isFromCornerstoneByID(perkID)
return PerksData.isAnySource(perkID, SOURCE_CORNERSTONE)
end
---
--- Retrieves whether the perk specified by its ID comes from a Order.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Order
function PerksData.isFromOrderByID(perkID)
return PerksData.isAnySource(perkID, SOURCE_ORDER)
end
---
--- Retrieves whether the perk specified by its ID comes from an Event.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from an Event
function PerksData.isFromEventByID(perkID)
return PerksData.isAnySource(perkID, SOURCE_RELIC)
end
---
--- Retrieves whether the perk specified by its ID comes from a Trader.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Trader
function PerksData.isFromTraderByID(perkID)
return PerksData.isAnySource(perkID, SOURCE_TRADER)
end
---
--- Retrieves the rarity of the perk specified by its ID.
---
--- Returns nil if the perk was not found.
---
---@param perkID string the ID of the perk
---@return string one of the rarity constants
function PerksData.getRarityByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_RARITY]
end
---
--- Retrieves whether the perk specified by its ID is of the given rarity.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@param rarityToCheck string of the rarity constants
---@return boolean whether the perk has the specified rarity
function PerksData.isRarityMatchByID(perkID, rarityToCheck)
-- Do some basic verification on the rarity specified and throw an error
-- if it is wrong at runtime.
if not SWITCH_RARITIES_CHECK[rarityToCheck] then
error("Specified rarity is not valid.")
end
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_RARITY] == rarityToCheck
end
---
--- Retrieves whether the perk specified by its ID is of Uncommon rarity.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk is Uncommon
function PerksData.isRarityUncommonByID(perkID)
return PerksData.isRarityMatchByID(perkID, PerksData.RARITY_UNCOMMON)
end
---
--- Retrieves whether the perk specified by its ID is of Rare rarity.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk is Rare
function PerksData.isRarityRareByID(perkID)
return PerksData.isRarityMatchByID(perkID, PerksData.RARITY_RARE)
end
---
--- Retrieves whether the perk specified by its ID is of Epic rarity.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk is Epic
function PerksData.isRarityEpicByID(perkID)
return PerksData.isRarityMatchByID(perkID, PerksData.RARITY_EPIC)
end
---
--- Retrieves whether the perk specified by its ID is of Legendary rarity.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk is Legendary
function PerksData.isRarityLegendaryByID(perkID)
return PerksData.isRarityMatchByID(perkID, PerksData.RARITY_LEGENDARY)
end
---
--- Retrieves the price of the perk specified by its ID.
---
--- Returns nil if the perk was not found.
---
---@param perkID string the ID of the perk
---@return number the price of the perk from a trader
function PerksData.getPriceByID(perkID)
local perk = PerksData.getAllDataForPerkByID(perkID)
if not perk then
return nil
end
return perk[INDEX_PRICE]
end
---
--- Loop through all perks and return those whose names contain the
--- specified search term.
---
---@param searchTerm string terms to search for
---@return table list of perkIDs with a match in their description
function PerksData.getAllPerkIDsWhereName(searchTerm)
if not searchTerm then
return {}
end
local list = {}
for perkID, perk in pairs(perksTable) do
if perk[INDEX_NAME]:find(searchTerm) then
table.insert(list, perkID)
end
end
return list
end
---
--- Loop through all perks and return those whose descriptions contain the
--- specified search term.
---
---@param searchTerm string terms to search for
---@return table list of perkIDs with a match in their description
function PerksData.getAllPerkIDsWhereDescription(searchTerm)
if not searchTerm then
return {}
end
local list = {}
for perkID, perk in pairs(perksTable) do
if perk[INDEX_DESCRIPTION]:find(searchTerm) then
table.insert(list, perkID)
end
end
return list
end
--endregion
return PerksData


