Module:PerksData: Difference between revisions
From Against the Storm Official Wiki
added isValid method, removed some verification to allow things to proceed if the user enters something invalid (reserving errors for coding problems) |
Anteprefix (talk | contribs) No edit summary |
||
| (13 intermediate revisions by 2 users not shown) | |||
| Line 122: | Line 122: | ||
PerksData.SOURCE_RELIC = "Relic" | PerksData.SOURCE_RELIC = "Relic" | ||
PerksData.SOURCE_TRADER = "Trader" | PerksData.SOURCE_TRADER = "Trader" | ||
PerksData.SOURCE_DECORATION = "Decoration" | |||
PerksData.SOURCE_PORT = "Port" | |||
PerksData.SOURCE_INSTITUTION = "Institution" | |||
PerksData.SOURCE_RACIAL = "Racial" | |||
PerksData.SOURCE_BIOME = "Biome" | |||
--endregion | --endregion | ||
| Line 133: | Line 139: | ||
local VALUE_DIVISOR = 10 | local VALUE_DIVISOR = 10 | ||
local INDEX_KEY = "key" | |||
local INDEX_ID = "id" | local INDEX_ID = "id" | ||
local INDEX_NAME = "displayName" | local INDEX_NAME = "displayName" | ||
| Line 155: | Line 162: | ||
["legendary"] = PerksData.RARITY_LEGENDARY, | ["legendary"] = PerksData.RARITY_LEGENDARY, | ||
["mythic"] = PerksData.RARITY_MYTHIC | ["mythic"] = PerksData.RARITY_MYTHIC | ||
} | |||
local RARITIES_SORT_ORDER = { | |||
[PerksData.RARITY_UNCOMMON] = 1, | |||
[PerksData.RARITY_RARE] = 2, | |||
[PerksData.RARITY_EPIC] = 3, | |||
[PerksData.RARITY_LEGENDARY] = 4, | |||
[PerksData.RARITY_MYTHIC] = 5, | |||
} | } | ||
| Line 163: | Line 178: | ||
[PerksData.SOURCE_RELIC] = true, | [PerksData.SOURCE_RELIC] = true, | ||
[PerksData.SOURCE_TRADER] = true, | [PerksData.SOURCE_TRADER] = true, | ||
[PerksData.SOURCE_DECORATION] = true, | |||
[PerksData.SOURCE_PORT] = true, | |||
[PerksData.SOURCE_INSTITUTION] = true, | |||
[PerksData.SOURCE_RACIAL] = true, | |||
[PerksData.SOURCE_BIOME] = true, | |||
} | } | ||
| Line 171: | Line 191: | ||
["relic"] = PerksData.SOURCE_RELIC, | ["relic"] = PerksData.SOURCE_RELIC, | ||
["trader"] = PerksData.SOURCE_TRADER, | ["trader"] = PerksData.SOURCE_TRADER, | ||
["decoration"] = PerksData.SOURCE_DECORATION, | |||
["port"] = PerksData.SOURCE_PORT, | |||
["institution"] = PerksData.SOURCE_INSTITUTION, | |||
["racial"] = PerksData.SOURCE_RACIAL, | |||
["biome"] = PerksData.SOURCE_BIOME, | |||
} | } | ||
| Line 190: | Line 215: | ||
local function cleanDescription(originalPerkDescription) | local function cleanDescription(originalPerkDescription) | ||
-- | -- replace any duplicated quotes | ||
originalPerkDescription = originalPerkDescription:gsub(" | originalPerkDescription = originalPerkDescription:gsub("""", '"') | ||
-- remove | -- remove any leftover quotes | ||
return originalPerkDescription:gsub(""", '"') | |||
return originalPerkDescription:gsub(" | |||
end | end | ||
| Line 254: | Line 276: | ||
-- Copy over the content, mapping unhelpful indexes into header keys | -- Copy over the content, mapping unhelpful indexes into header keys | ||
local newPerk = {} | local newPerk = {} | ||
newPerk[INDEX_KEY] = string.lower(originalPerk[INDEX_OLD_ID]) | |||
newPerk[INDEX_ID] = originalPerk[INDEX_OLD_ID] | newPerk[INDEX_ID] = originalPerk[INDEX_OLD_ID] | ||
newPerk[INDEX_NAME] = originalPerk[INDEX_OLD_NAME] | newPerk[INDEX_NAME] = originalPerk[INDEX_OLD_NAME] | ||
| Line 263: | Line 286: | ||
newPerk[INDEX_SOURCES] = makeSourcesSubtable(originalPerk[INDEX_OLD_SOURCES]) | newPerk[INDEX_SOURCES] = makeSourcesSubtable(originalPerk[INDEX_OLD_SOURCES]) | ||
newPerksTable[ newPerk[ | newPerksTable[ newPerk[INDEX_KEY] ] = newPerk | ||
table.insert(perksIDs, newPerk[INDEX_ID]) | table.insert(perksIDs, newPerk[INDEX_ID]) | ||
| Line 288: | Line 311: | ||
-- Utility module retrieves the data as basic, flat lua tables. | -- Utility module retrieves the data as basic, flat lua tables. | ||
-- Doesn't use the header lookup. | -- Doesn't use the header lookup. | ||
local originalPerksTable, _ = CsvUtils.luaTableFromCSV(CsvUtils.extractCSV(DATA_TEMPLATE_NAME)) | -- local originalPerksTable, _ = mw.loadData(CsvUtils.luaTableFromCSV(CsvUtils.extractCSV(DATA_TEMPLATE_NAME))) | ||
--local originalPerksTable, _ = mw.loadData("Module:PerksData/Data") | |||
local allDataTable = mw.loadData("Module:PerksData/Data") | |||
perksTable = allDataTable[1] | |||
perksIDs = allDataTable[2] | |||
mapNamesToIDs = allDataTable[3] | |||
-- Now restructure to be more conducive. | -- Now restructure to be more conducive. | ||
perksTable = restructurePerksTable(originalPerksTable) | -- perksTable = restructurePerksTable(originalPerksTable) | ||
end | end | ||
| Line 333: | Line 361: | ||
end | end | ||
return perksTable[id] | return perksTable[string.lower(id)] | ||
end | end | ||
| Line 491: | Line 519: | ||
---isFromAltarByID | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from the Forsaken Altar | |||
function PerksData.isFromAltarByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_ALTAR) | |||
end | |||
| Line 545: | Line 583: | ||
return PerksData.isAnySource(perkID, PerksData.SOURCE_TRADER) | return PerksData.isAnySource(perkID, PerksData.SOURCE_TRADER) | ||
end | |||
--- | |||
--- Retrieves whether the perk specified by its ID comes from a Decoration. | |||
--- | |||
--- Returns nil if the perk was not found | |||
--- | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from a Decoration | |||
function PerksData.isFromDecorationByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_DECORATION) | |||
end | |||
--- | |||
--- Retrieves whether the perk specified by its ID comes from the Strider Port. | |||
--- | |||
--- Returns nil if the perk was not found | |||
--- | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from the Strider Port | |||
function PerksData.isFromPortByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_PORT) | |||
end | |||
--- | |||
--- Retrieves whether the perk specified by its ID comes from a Species Bonus. | |||
--- | |||
--- Returns nil if the perk was not found | |||
--- | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from a Species Bonus | |||
function PerksData.isFromRacialByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_RACIAL) | |||
end | |||
--- | |||
--- Retrieves whether the perk specified by its ID comes from a Service Building. | |||
--- | |||
--- Returns nil if the perk was not found | |||
--- | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from a Service Building | |||
function PerksData.isFromInstitutionByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_INSTITUTION) | |||
end | |||
--- | |||
--- Retrieves whether the perk specified by its ID comes from a Biome. | |||
--- | |||
--- Returns nil if the perk was not found | |||
--- | |||
---@param perkID string the ID of the perk | |||
---@return boolean whether the perk comes from a Biome | |||
function PerksData.isFromBiomeByID(perkID) | |||
return PerksData.isAnySource(perkID, PerksData.SOURCE_BIOME) | |||
end | end | ||
| Line 679: | Line 787: | ||
local list = {} | local list = {} | ||
for | for _, perk in pairs(perksTable) do | ||
if string.lower(perk[INDEX_NAME]):find(string.lower(searchTerm)) then | if string.lower(perk[INDEX_NAME]):find(string.lower(searchTerm)) then | ||
table.insert(list, | table.insert(list, perk[INDEX_ID]) | ||
end | end | ||
end | end | ||
| Line 708: | Line 816: | ||
local list = {} | local list = {} | ||
for | for _, perk in pairs(perksTable) do | ||
if string.lower(perk[INDEX_DESCRIPTION]):find(string.lower(searchTerm)) then | if string.lower(perk[INDEX_DESCRIPTION]):find(string.lower(searchTerm)) then | ||
table.insert(list, | table.insert(list, perk[INDEX_ID]) | ||
end | end | ||
end | end | ||
| Line 744: | Line 852: | ||
-- Don't use the internal functions, because we need to return an empty list without any errors if one or the other wasn't provided. | -- Don't use the internal functions, because we need to return an empty list without any errors if one or the other wasn't provided. | ||
local filteredIDs = {} | local filteredIDs = {} | ||
for | for _, perk in pairs(perksTable) do | ||
if not rarityToFilter or "" == rarityToFilter or perk[INDEX_RARITY] == rarityToFilter then | if not rarityToFilter or "" == rarityToFilter or perk[INDEX_RARITY] == rarityToFilter then | ||
for _, perkSource in ipairs(perk[INDEX_SOURCES]) do | for _, perkSource in ipairs(perk[INDEX_SOURCES]) do | ||
if not sourceToFilter or "" == sourceToFilter or perkSource == sourceToFilter then | if not sourceToFilter or "" == sourceToFilter or perkSource == sourceToFilter then | ||
table.insert(filteredIDs, | table.insert(filteredIDs, perk[INDEX_ID]) | ||
break | break | ||
end | end | ||
| Line 777: | Line 885: | ||
end | end | ||
local perk1 = perksTable[perkID1] | local perk1 = perksTable[string.lower(perkID1)] | ||
local perk2 = perksTable[perkID2] | local perk2 = perksTable[string.lower(perkID2)] | ||
if not perk1 then | if not perk1 then | ||
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".") | error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".") | ||
| Line 789: | Line 896: | ||
local name1 = perk1[INDEX_NAME] | local name1 = perk1[INDEX_NAME] | ||
local name2 = perk2[INDEX_NAME] | local name2 = perk2[INDEX_NAME] | ||
if not name1 then | if not name1 then | ||
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".") | error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".") | ||
| Line 797: | Line 903: | ||
end | end | ||
return name1 < name2 | -- Sort by name, then rarity, then their ids (so it catches when we have Oil +2 and Oil +1 that are both Uncommon) | ||
if name1 == name2 then | |||
local rarity1 = RARITIES_SORT_ORDER[perk1[INDEX_RARITY]] | |||
local rarity2 = RARITIES_SORT_ORDER[perk2[INDEX_RARITY]] | |||
if rarity1 == rarity2 then | |||
return perk1[INDEX_ID] < perk2[INDEX_ID] | |||
else | |||
return rarity1 < rarity2 | |||
end | |||
else | |||
return name1 < name2 | |||
end | |||
end | end | ||
Latest revision as of 19:55, 31 August 2025
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"
PerksData.RARITY_MYTHIC = "Mythic"
PerksData.NONE = "None"
PerksData.SOURCE_ALTAR = "Altar"
PerksData.SOURCE_CORNERSTONE = "Cornerstone"
PerksData.SOURCE_ORDER = "Order"
PerksData.SOURCE_RELIC = "Relic"
PerksData.SOURCE_TRADER = "Trader"
PerksData.SOURCE_DECORATION = "Decoration"
PerksData.SOURCE_PORT = "Port"
PerksData.SOURCE_INSTITUTION = "Institution"
PerksData.SOURCE_RACIAL = "Racial"
PerksData.SOURCE_BIOME = "Biome"
--endregion
--region Private constants
local DATA_TEMPLATE_NAME = "Template:Perks_csv"
local VALUE_DIVISOR = 10
local INDEX_KEY = "key"
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 SWITCH_RARITIES_CHECK = {
[PerksData.RARITY_UNCOMMON] = true,
[PerksData.RARITY_RARE] = true,
[PerksData.RARITY_EPIC] = true,
[PerksData.RARITY_LEGENDARY] = true,
[PerksData.RARITY_MYTHIC] = true,
}
local NORMALIZED_TEXT_RARITIES = {
["uncommon"] = PerksData.RARITY_UNCOMMON,
["rare"] = PerksData.RARITY_RARE,
["epic"] = PerksData.RARITY_EPIC,
["legendary"] = PerksData.RARITY_LEGENDARY,
["mythic"] = PerksData.RARITY_MYTHIC
}
local RARITIES_SORT_ORDER = {
[PerksData.RARITY_UNCOMMON] = 1,
[PerksData.RARITY_RARE] = 2,
[PerksData.RARITY_EPIC] = 3,
[PerksData.RARITY_LEGENDARY] = 4,
[PerksData.RARITY_MYTHIC] = 5,
}
local SWITCH_SOURCES_CHECK = {
[PerksData.SOURCE_ALTAR] = true,
[PerksData.SOURCE_CORNERSTONE] = true,
[PerksData.SOURCE_ORDER] = true,
[PerksData.SOURCE_RELIC] = true,
[PerksData.SOURCE_TRADER] = true,
[PerksData.SOURCE_DECORATION] = true,
[PerksData.SOURCE_PORT] = true,
[PerksData.SOURCE_INSTITUTION] = true,
[PerksData.SOURCE_RACIAL] = true,
[PerksData.SOURCE_BIOME] = true,
}
local NORMALIZED_TEXT_SOURCES = {
["altar"] = PerksData.SOURCE_ALTAR,
["cornerstone"] = PerksData.SOURCE_CORNERSTONE,
["order"] = PerksData.SOURCE_ORDER,
["relic"] = PerksData.SOURCE_RELIC,
["trader"] = PerksData.SOURCE_TRADER,
["decoration"] = PerksData.SOURCE_DECORATION,
["port"] = PerksData.SOURCE_PORT,
["institution"] = PerksData.SOURCE_INSTITUTION,
["racial"] = PerksData.SOURCE_RACIAL,
["biome"] = PerksData.SOURCE_BIOME,
}
local ERROR_MESSAGE_ID_MISSING = "PerksData cannot compare IDs when one or more of the IDs were missing."
local ERROR_PREFIX_NAME_NOT_FOUND = "PerksData attempted to retrieve the display name of a Perk or Cornerstones, but it was not found. Double-check that the data is up-to-date and loaded correctly:"
--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)
-- replace any duplicated quotes
originalPerkDescription = originalPerkDescription:gsub("""", '"')
-- remove any leftover 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
sourceName = sourceName:gsub("%s+", "")
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_KEY] = string.lower(originalPerk[INDEX_OLD_ID])
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_KEY] ] = 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, _ = mw.loadData(CsvUtils.luaTableFromCSV(CsvUtils.extractCSV(DATA_TEMPLATE_NAME)))
--local originalPerksTable, _ = mw.loadData("Module:PerksData/Data")
local allDataTable = mw.loadData("Module:PerksData/Data")
perksTable = allDataTable[1]
perksIDs = allDataTable[2]
mapNamesToIDs = allDataTable[3]
-- Now restructure to be more conducive.
-- perksTable = restructurePerksTable(originalPerksTable)
end
--endregion
--region Public methods
---isPerkIDValid
---@param perkID string the unique identifier of a Perk or Cornerstone
---@return boolean true if there is a record with the specified ID
function PerksData.isPerkIDValid(perkID)
if not perkID or "" == perkID then
return false
else
local record = PerksData.getAllDataForPerkByID(perkID)
return record ~= nil
end
end
---
--- 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[string.lower(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
---isFromAltarByID
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from the Forsaken Altar
function PerksData.isFromAltarByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_ALTAR)
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, PerksData.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, PerksData.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, PerksData.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, PerksData.SOURCE_TRADER)
end
---
--- Retrieves whether the perk specified by its ID comes from a Decoration.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Decoration
function PerksData.isFromDecorationByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_DECORATION)
end
---
--- Retrieves whether the perk specified by its ID comes from the Strider Port.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from the Strider Port
function PerksData.isFromPortByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_PORT)
end
---
--- Retrieves whether the perk specified by its ID comes from a Species Bonus.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Species Bonus
function PerksData.isFromRacialByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_RACIAL)
end
---
--- Retrieves whether the perk specified by its ID comes from a Service Building.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Service Building
function PerksData.isFromInstitutionByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_INSTITUTION)
end
---
--- Retrieves whether the perk specified by its ID comes from a Biome.
---
--- Returns nil if the perk was not found
---
---@param perkID string the ID of the perk
---@return boolean whether the perk comes from a Biome
function PerksData.isFromBiomeByID(perkID)
return PerksData.isAnySource(perkID, PerksData.SOURCE_BIOME)
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)
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
if not perksTable then
loadData()
end
local list = {}
for _, perk in pairs(perksTable) do
if string.lower(perk[INDEX_NAME]):find(string.lower(searchTerm)) then
table.insert(list, perk[INDEX_ID])
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
if not perksTable then
loadData()
end
local list = {}
for _, perk in pairs(perksTable) do
if string.lower(perk[INDEX_DESCRIPTION]):find(string.lower(searchTerm)) then
table.insert(list, perk[INDEX_ID])
end
end
return list
end
function PerksData.getAllPerkIDs()
if not perksTable then
loadData()
end
return perksIDs
end
--- Gets all IDs for all perks, but filtered to the specified rarity and source.
---
---@param rarityToFilter string the rarity to filter to, should be normalized already
---@param sourceToFilter string the source to filter to, should be normalized already
---@return table an array of filtered IDs
function PerksData.getAllPerkIDsFilteredByRarityAndSource(rarityToFilter, sourceToFilter)
if not perksTable then
loadData()
end
-- Don't use the internal functions, because we need to return an empty list without any errors if one or the other wasn't provided.
local filteredIDs = {}
for _, perk in pairs(perksTable) do
if not rarityToFilter or "" == rarityToFilter or perk[INDEX_RARITY] == rarityToFilter then
for _, perkSource in ipairs(perk[INDEX_SOURCES]) do
if not sourceToFilter or "" == sourceToFilter or perkSource == sourceToFilter then
table.insert(filteredIDs, perk[INDEX_ID])
break
end
end
end
end
return filteredIDs
end
--- A custom sorting function so that perks can be listed alphabetically via table.sort(yourTable, PerksData.compareNames). Public so controllers and other data models might reuse this.
---
--- Throws errors if the IDs do not correspond to perks in the data.
---
---@param perkID1 string the ID of the first perk to compare
---@param perkID2 string the ID of the second perk to compare
---@return boolean true if the first perk should come first in the list
function PerksData.compareNames(perkID1, perkID2)
if not perkID1 or "" == perkID1 or not perkID2 or "" == perkID2 then
error(ERROR_MESSAGE_ID_MISSING)
end
if not perksTable then
loadData()
end
local perk1 = perksTable[string.lower(perkID1)]
local perk2 = perksTable[string.lower(perkID2)]
if not perk1 then
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".")
end
if not perk2 then
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID2 .. ".")
end
local name1 = perk1[INDEX_NAME]
local name2 = perk2[INDEX_NAME]
if not name1 then
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID1 .. ".")
end
if not name2 then
error(ERROR_PREFIX_NAME_NOT_FOUND .. " id=" .. perkID2 .. ".")
end
-- Sort by name, then rarity, then their ids (so it catches when we have Oil +2 and Oil +1 that are both Uncommon)
if name1 == name2 then
local rarity1 = RARITIES_SORT_ORDER[perk1[INDEX_RARITY]]
local rarity2 = RARITIES_SORT_ORDER[perk2[INDEX_RARITY]]
if rarity1 == rarity2 then
return perk1[INDEX_ID] < perk2[INDEX_ID]
else
return rarity1 < rarity2
end
else
return name1 < name2
end
end
--- Standardizes the display of a string name of one of Perks' rarities.
---
--- If the provided rarity is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param rarityString string the descriptive name of the rarity of a perk or cornerstone
---@return string that same rarity, normalized to title case
function PerksData.normalizeRarityText(rarityString)
if not rarityString or "" == rarityString then
return nil
end
local rarity = string.lower(rarityString)
if not rarity or SWITCH_RARITIES_CHECK[rarity] then
return nil
end
return NORMALIZED_TEXT_RARITIES[rarity]
end
--- Standardizes the display of a string name of one of Perks' sources.
---
--- If the provided source is nil or not an accepted term (for example misspelled), this will return nil.
---
---@param sourceString string the descriptive name of the source of a perk or cornerstone
---@return string that same source, normalized to title case
function PerksData.normalizeSourceText(sourceString)
if not sourceString or "" == sourceString then
return nil
end
local source = string.lower(sourceString)
if not source or SWITCH_SOURCES_CHECK[source] then
return nil
end
return NORMALIZED_TEXT_SOURCES[source]
end
--endregion
return PerksData


