Module:List
Jump to navigation
Jump to search
Module used to list Items.
Arguments
There are three different functions you can invoke with this module. All can use the main arguments:
Filter arguments:
category
- either "Item", "Equipment", "Armor", "Weapon" or "Consumable". Defaults to "Item".class
- the class of the Item, valid for Equipment, Armor and Weapons.type
- the type of the item, valid for Weapons.set
- the item set
Output control arguments:
default
- if true, will show default text when no results are found, otherwise will show nothing.showClass
- if true it will show the class as a column in the table, defaults to falseshowDurability
- if true it will show durability as a column in the table, defaults to true
Main function
The function items
will return a list of all items which match the provided filter.
It has no extra arguments other than the main arguments.
- Example:
{{#invoke:List|items|category=Weapon|class=Axe|type=One-Handed}}
Manual function
The function manual
will list a manually supplied list of items through the argument itemsToList
.
- Example:
{{#invoke:List|manual|category=Weapon|itemsToList=Iron Sword,Tsar Axe,Lexicon}}
Element function
The function element
will list all items which have damage, damage bonus or damage resistance for the provided element
argument.
- Example:
{{#invoke:List|element|element=Fire}}
Test
{{#invoke:List|items|category=Armor|set=Tsar Set}}
Icon | Name | Resistances | Durability | Weight | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Tsar Armor | 28% 40% | 23% | 6 | 10 | 6% | – | -11% | ∞ | 23.0 | |
Tsar Boots | 20% 20% | 13% | 4 | 5 | 4% | – | -9% | ∞ | 17.0 | |
Tsar Helm | 20% 20% | 13% | 4 | 5 | 4% | 15% | -9% | ∞ | 13.0 |
The above documentation is transcluded from Module:List/doc. (edit | history)
local p = {}
category = "Item"
class = ""
itemType = ""
itemset = ""
default = true
showClass = false
showDurability = true
collapsible = false
collapseID = 0
-- the fields used for the Cargo query. Not all necessarily used as columns.
itemFields = '_pageName,name,image,class,type,DLC,effects,weight,buy,sell,durability,protection,barrier,statusResist,physical,ethereal,'
.. 'decay,lightning,frost,fire,impact,physicalBonus,etherealBonus,decayBonus,lightningBonus,frostBonus,fireBonus,manaCostReduction,'
.. 'staminaCostReduction,movespeed,coldresist,heatresist,pouch,itemset,corruptresist,cooldown,stamcost,attackspeed,impactResist,capacity,'
.. 'preservation,inventProt,thirst,hunger,reach'
-- the column headers used for the output table.
-- Key: field name
-- Value: displayed column header
-- NOTE: If you add a column to this, make sure you also add it to the 'columnsIndexed' table below as well.
-- This table does NOT determine the order of the columns, columnsIndexed does.
columnHeaders = {
image='Icon',
name='Name',
damage='Damage',
resistance='Resistances',
impact='{{impact}}',
impactResist='{{impact}} Resist',
protection='{{protection}}',
barrier='{{barrier}}',
statusResist='{{statusResist}}',
reach='Reach',
attackspeed='Speed',
stamcost='{{Stamina}}',
bonus='Damage Bonus%',
thirst='Thirst',
hunger='Hunger',
effects='Effects',
heatresist='{{HotWeather}}',
coldresist='{{ColdWeather}}',
staminaCostReduction='{{Stamina Cost}}',
corruptresist='{{corruptionResist}}',
cooldown='{{cooldown}}',
manaCostReduction='{{Mana Cost}}',
movespeed='{{Movement}}',
preservation='Preservation',
inventProt='Inventory Protection',
durability='Durability',
weight='Weight',
capacity='Capacity',
pouch='Pouch Bonus',
class='Class'
}
-- Determines the order of the columns. All keys of "columnHeaders" should be here.
-- we could instead have a table of tables to keep the index order, but just gonna leave it like this for now.
columnsIndexed = {
'image','name','damage','resistance','impact','impactResist','protection','barrier',
'statusResist','reach','stamcost','attackspeed','bonus','thirst','hunger','heatresist','coldresist','staminaCostReduction','manaCostReduction',
'corruptresist','cooldown','movespeed','pouch','capacity','durability','weight','effects','preservation','inventProt','class'
}
-- helper tables for the 6 elements since they are iterated over a few times
elementsIndexed = { 'physical', 'ethereal', 'decay', 'lightning', 'frost', 'fire' }
elements = {
physical = '{{phys}}',
ethereal = '{{ethereal}}',
decay = '{{decay}}',
lightning = '{{lightning}}',
frost = '{{frost}}',
fire = '{{fire}}'
}
-- some CSS styles
local styles = {
default = 'text-align: center; vertical-align: top',
red = 'text-align: center; vertical-align: top; color: #df756f',
green = 'text-align: center; vertical-align: top; color: #aedc99'
}
-- quick lookup for red/green colored values. Bool value is for %-based values
local redGreenValue = {
movespeed=true, staminaCostReduction=true, manaCostReduction=true,
preservation=true, cooldown=true, corruptresist=true, statusResist=true,
impactResist=true,heatresist=false,coldresist=false
}
-- cells which want an icon after the displayed value
local iconCells = {
impact='{{impact}}',
impactResist='{{impact}}',
protection='{{protection}}',
barrier='{{barrier}}',
statusResist='{{statusResist}}',
attackspeed='{{attack Speed}}',
stamcost='{{Stamina}}',
heatresist='{{HotWeather}}',
coldresist='{{ColdWeather}}',
staminaCostReduction='{{Stamina Cost}}',
corruptresist='{{corruptionResist}}',
cooldown='{{cooldown}}',
manaCostReduction='{{Mana Cost}}',
movespeed='{{Movement}}',
}
-- helper to return when no results are found
function p.defaultReturn()
if default == true then
return '<span style="color:#e77">\'\'No items found.\'\'</span>'
else
return ''
end
end
-------------------- Main Function -------------------
-- 'items' is the main function for listing all items of the provided 'category' (defaults to 'Item').
-- can filter further with 'class', 'itemType' and 'set' arguments.
function p.items(frame)
args = require('Module:ProcessArgs').merge(true)
category = args.category or "Item"
class = args.class or ""
itemType = args.type or ""
itemset = args.set or ""
default = args.default or true
showClass = args.showClass or args.showclass or false
showDurability = args.showDurability or args.showdurability or true
collapsible = args.collapsible or false
collapseID = args.collapseID or collapseID
local result = p.doQuery(args)
if not result then
return p.defaultReturn()
end
local html = ''
-- if collapsible, add the button
if collapsible then
html = '<div class="mw-customtoggle-' .. collapseID .. ' wds-button wds-is-secondary">Show/Hide table</div>'
end
html = html .. tostring(p.formatResult(result, frame, nil))
return html
end
function p.doQuery(args)
local cargoArgs = {
where = p.cargoWhere(args),
orderBy = 'name',
groupBy = 'name',
limit = 10000
}
return mw.ext.cargo.query('ItemData', itemFields, cargoArgs)
end
function p.cargoWhere(args)
local where = 'category="' .. category .. '"'
if not p.isempty(class) then
where = where .. ' AND class="' .. class .. '"'
end
if not p.isempty(itemType) then
where = where .. ' AND type="' .. itemType .. '"'
end
if not p.isempty(itemset) then
where = where .. ' AND itemset="' .. itemset .. '"'
end
if not p.isempty(args.where) then
where = where .. ' AND (' .. args.where .. ')'
end
return where
end
------------------- Element function -------------------
-- 'element' lists all items with the provided element argument
-- example: {{#invoke:list|element|element=ethereal}}
function p.element(frame)
args = require('Module:ProcessArgs').merge(true)
showClass = true
showDurability = false
collapsible = args.collapsible or true
local element = args.element
local elementBonus = element .. 'Bonus'
local html = mw.html.create()
category='Weapon'
p.elementQuery('Weapon', element, elementBonus, html, frame)
html:newline()
category='Armor'
p.elementQuery('Armor', element, elementBonus, html, frame)
html:newline()
category='Equipment'
p.elementQuery('Equipment', element, elementBonus, html, frame)
return html
end
-- query for the 'element' function
function p.elementQuery(category, element, elementBonus, html, frame)
-- do the query for this category and element
local cargoWhere = element .. ' IS NOT NULL OR ' .. elementBonus .. ' IS NOT NULL'
local cargoArgs = {
where = '(category="' .. category .. '") AND (' .. cargoWhere .. ')',
orderBy = 'name',
groupBy = 'name',
limit = 5000
}
local result = mw.ext.cargo.query('ItemData', itemFields, cargoArgs)
-- if results, append to html
if (result ~= nil and #result > 0) then
-- create the header for the results
html:wikitext(frame:preprocess('===' .. category .. 's===')):newline()
-- if collapsible, add the button
if collapsible then
html:wikitext('<div class="mw-customtoggle-' .. collapseID .. ' wds-button wds-is-secondary">Show/Hide table</div>')
end
-- use the normal table builder
local processedResult = p.formatResult(result, frame, element)
-- append the table to our html
html:node(processedResult)
end
end
------------------ Manual function -----------------
-- 'manual' function for listing a known list of items.
-- example: {{#invoke:list|manual|itemsToList=Item1,Item2,Item3}}
function p.manual(frame)
args = require('Module:ProcessArgs').merge(true)
showClass = true
showDurability = true
category = args.category or 'Item'
default = false
collapsible = args.collapsible or false
local items = p.split(args.itemsToList, ',')
local cargoWhere = ''
for _, v in ipairs(items) do
if cargoWhere ~= '' then cargoWhere = cargoWhere .. ' OR ' end
cargoWhere = cargoWhere .. '_pageName="' .. v .. '"'
end
local cargoArgs = {
where = '(category="' .. category .. '") AND (' .. cargoWhere .. ')',
orderBy = 'name',
groupBy = 'name',
limit = 5000
}
local result = mw.ext.cargo.query('ItemData', itemFields, cargoArgs)
if result == nil or #result < 1 then
return p.defaultReturn()
else
return p.formatResult(result, frame, nil)
end
end
-------------------------------------------------------------------------------
---------------------------- Formatting the result ----------------------------
--------------------------
-- Main output function --
--------------------------
function p.formatResult(result,frame,element)
-- create a table of flags to determine which columns are actually used
local columnUsedFlags = {}
for field,_ in pairs(columnHeaders) do
columnUsedFlags[field] = false
end
-- fill the columns with the item data
processed = {}
for i, row in pairs(result) do
if (string.find(row._pageName, "Module:") == nil and string.find(row._pageName, "Template:") == nil) then
processed[i] = p.processRow(columnUsedFlags, row, frame)
end
end
-- check showDurabilty and showClass overrides
if not showDurability then
columnUsedFlags['durability'] = false
end
if not showClass then
columnUsedFlags['class'] = false
end
-- remove 'attackspeed' for off-hands as it is redundant
if class == "Shield" or class == "Chakram" or class == "Dagger" or class == "Pistol" then
columnUsedFlags['attackspeed'] = false
columnUsedFlags['reach'] = false
end
-- remove 'reach' for weapons that don't have it
if class == "Bow" or class == "Chakram" or class == "Pistol" then
columnUsedFlags['reach'] = false
end
-- format the output with table headers and the result data
return p.makeTable(processed, columnUsedFlags, frame, element)
end
-----------------------------------------------------------
-- Process a result from the query into our output table --
-----------------------------------------------------------
-- This just has some processing for Resistances, Damage and Damage Bonuses.
-- For everything else, it just copies the value and sets the flag to true.
function p.processRow(columnUsedFlags, row, frame)
processedRow = {}
-- process each column for the result
for _,column in ipairs(columnsIndexed) do
-- total Resistances, Weapon Damage and Damage Bonus are combined into their own columns
if (column == 'damage' and category ~= 'Set' and category ~= 'Armor')
or (column == 'resistance' and (category == 'Set' or category == 'Armor'))
or column == 'bonus' then
local sum = 0
local any = false
local formatted = {}
-- iterate over each element and add it to the formatted table if there is a defined value
for element,icon in pairs(elements) do
local eleKey = element
if column == 'bonus' then eleKey = eleKey .. 'Bonus' end
if not p.isempty(row[eleKey]) then
any = true
-- format the output
local processed = '{{#expr:' .. row[eleKey] .. '}}'
if column ~= 'damage' then processed = processed .. '%' end
processed = processed .. ' ' .. icon .. '<br>'
formatted[element] = processed
-- add the sum
sum = sum + row[eleKey]
-- for element data-sort-value
processedRow[eleKey] = row[eleKey]
end
end
-- declare the sum value for data-sort
processedRow[column .. 'Sum'] = sum
-- if there is any defined, format the total output
if any then
columnUsedFlags[column] = true
processedRow[column] = ''
for _,type in ipairs(elementsIndexed) do
local val = formatted[type] or ''
processedRow[column] = processedRow[column] .. val
end
else
processedRow[column] = '–'
processedRow[column .. 'Sum'] = 0
end
else -- everything else is just defined automatically like so
if not p.isempty(row[column]) then
columnUsedFlags[column] = true
processedRow[column] = row[column]
else
processedRow[column] = '–'
end
end
end
-- values needed for formatting but not included as headers
processedRow._pageName = row._pageName
processedRow.type = row.type
processedRow.DLC = row.DLC
return processedRow
end
----------------------------------------------------------
-- Turn the processed results into an actual wiki table --
----------------------------------------------------------
function p.makeTable(processed, columnUsedFlags, frame, element)
-- make the output table
local tbl = mw.html.create('table'):addClass('wikitable'):addClass('sortable')
if collapsible then
tbl = tbl:addClass('mw-collapsible article-table')
:attr('id', 'mw-customcollapsible-' .. collapseID)
collapseID = collapseID + 1
end
--for armor and set, 'impact' is used as impactResist.
if category == 'Armor' or category == 'Set' then
redGreenValue.impact = true
end
-- add headers
local headerRow = tbl:tag('tr')
for _,k in ipairs(columnsIndexed) do
if columnUsedFlags[k] == true then
if k == "damage" or k == "bonus" then
headerRow:tag('th'):css('width:70px'):wikitext(frame:preprocess(columnHeaders[k]))
else
headerRow:tag('th'):wikitext(frame:preprocess(columnHeaders[k]))
end
end
end
--if category == "Set" then
-- headerRow:tag('th'):addClass('nomobile'):wikitext('Set items')
--end
-- fill table rows
for i,row in pairs(processed) do
-- add a row
tr = tbl:tag('tr')
-- iterate over each column header
for _,column in ipairs(columnsIndexed) do
if columnUsedFlags[column] == true then
-- add a cell
td = tr:tag('td')
-- format the row
p.addRow(column, row, td, frame, element)
end
end
---- the "Show Set Items" button for sets
--if category == "Set" then
-- tr:tag('td')
-- :addClass(string.format('mw-customtoggle-%s nomobile',i))
-- :wikitext('<div style="background-color: rgba(225,215,127,0.2); font-weight:bold; text-align: center; '
-- .. 'display:table; height:80px; width:80px"><span style="vertical-align:middle; display:table-cell">Show</span></div>')
-- tbl:tag('tr')
-- :addClass('expand-child mw-collapsible mw-collapsed nomobile')
-- :attr('id', string.format('mw-customcollapsible-%s',i))
-- :tag('td')
-- :attr('colspan', 19)
-- :wikitext(frame:expandTemplate{title="set items", args = {set=row.name}})
--end
end
return tbl
end
--------------------------------------------------------
-- Format a result and add a td-row to our html table --
--------------------------------------------------------
function p.addRow(column, row, td, frame, element)
-- the 'wikitext' value for the td will be defined by our 'output' variable.
local output = ''
-- css styles
local style = styles.default
-- cached tonumber since we use it a bit
local valAsNum = tonumber(row[column])
-- image
if column == 'image' then
local image = row.image or 'Placeholder.png'
output = string.format("[[File:%s|35x53px|link=%s]]", image, row._pageName)
-- displayed name/link
elseif column == 'name' then
local formattedName = '[[' .. row._pageName .. '|' .. row.name .. ']]'
if not p.isempty(row.DLC) then
formattedName = frame:preprocess(formattedName .. ' {{' .. row.DLC .. '}}')
end
output = formattedName
-- class
elseif column == 'class' then
if (row.class == "Shield" or row.class == "Bow" or row.class == "Spear") then
output = string.format("[[%ss|%s]]", row.class, row.class)
elseif(row.type == "One-Handed") then
output = string.format("[[%ss#%s|1H %s]]", row.class, row.type, row.class)
elseif (row.type == "Two-Handed") then
output = string.format("[[%ss#%s|2H %s]]", row.class, row.type, row.class)
elseif (row.type == "Staff" or row.type == "Halberd") then
output = string.format("[[%ss|%s]]", row.class, row.type)
elseif (row.class == "Chest") then
output = "[[Body Armor]]"
elseif (row.class == "Legs") then
output = "[[Boots]]"
elseif (row.class == "Head") then
output = "[[Helmets]]"
elseif (p.isempty(row.class)) then
if (row.type == "Other") then
output = "Other"
else
output = string.format("[[%ss|%s]]", row.type, row.type)
end
else
output = string.format("[[%ss|%s]]", row.class, row.class)
end
-- hunger and thirst
elseif column == "hunger" or column == "thirst" then
if (valAsNum ~= nil) then
output = tostring(valAsNum / 10) .. '%'
if valAsNum > 0 then style = styles.green
elseif valAsNum < 0 then style = styles.red
end
else
output = '–'
end
-- %-based values with red/green formatting
elseif redGreenValue[column] ~= nil then
if (valAsNum ~= nil) then
output = string.format("%.0f", valAsNum)
if redGreenValue[column] == true then
output = output .. '%'
end
-- for these values, negative is good and positive is bad
if column == 'manaCostReduction' or column == 'staminaCostReduction' then
if valAsNum < 0 then style = styles.green
elseif valAsNum > 0 then style = styles.red
end
else
if valAsNum < 0 then style = styles.red
elseif valAsNum > 0 then style = styles.green
end
end
else
output = '–'
end
-- weight and attack speed (always decimal)
elseif column == 'weight' or column == 'attackspeed' then
if valAsNum ~= nil then
if (column == 'attackspeed' and row[column] == "1") then
row[column] = "1.0"
end
output = string.format("%.1f", row[column])
else
output = '–'
end
else
output = row[column]
end
-- check if we want an icon after the value
if output ~= '–' and not p.isempty(output) then
local cellIcon = iconCells[column]
if cellIcon ~= nil then
output = output .. ' ' .. cellIcon
end
end
-- data sort value
local dataSortValue = valAsNum or 0
-- resistances, damage and damage bonus use a special data-sort-value
if column == 'damage' or column == 'bonus' or column == 'resistance' then
local sum = 0
-- if a specific element was defined, use only that element to sort.
if element ~= nil then
if column == 'damage' or column == 'resistance' then
sum = row[element]
elseif column == 'bonus' then
sum = row[element .. 'Bonus']
end
else
-- use the sum we defined in processRow
sum = row[column .. 'Sum']
end
dataSortValue = tonumber(sum) or 0
end
if column == 'name' or column == 'effects' then
dataSortValue = row[column]
end
-- if food durability, calculate data-sort-value
if (column == 'durability' and (string.find(row[column], 'Day') ~= nil or string.find(row[column], 'Hour') ~= nil)) then
dataSortValue = 0
local digit, _ = string.match(row[column], "(%d+)( Day)")
dataSortValue = dataSortValue + ((tonumber(digit) or 0) * 24)
digit, _ = string.match(row[column], "(%d+)( Hour)")
dataSortValue = dataSortValue + (tonumber(digit) or 0)
elseif (string.find(row[column], '∞') ~= nil) then
dataSortValue = 999999
end
-- actually add our output and data-sort-value
td:attr('data-sort-value', dataSortValue)
td:cssText(style)
-- for everything except name and effects, make whitespace no-wrap
if column ~= 'name' and column ~= 'effects' then
td:cssText('white-space: nowrap; ')
end
td:wikitext(frame:preprocess(output))
end
------------- HELPERS --------------
-- removes the key from the table if it was found
function p.removeIfFound(table, key)
local index = p.tablefind(table, key)
if (index ~= -1) then
table.remove(table, index)
end
end
-- searches the table for a key. if found, returns the index of that key, otherwise returns -1.
function p.tablefind(table,key)
for index, entry in pairs(table) do
if entry == key then
return index
end
end
return -1
end
-- returns true if the provided object is nil, blank, 0 or false, otherwise returns false.
function p.isempty(s)
return s == nil or s == '' or s == 0 or s == false
end
-- splits the input string into a table by the provided seperator
function p.split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
if p.notempty(inputstr) then
for str in string.gmatch(inputstr .. ',', "([^"..sep.."]+)") do
table.insert(t, str)
end
end
return t
end
-- searches the provided table for a key. if found, returns that key's value.
function p.find(tbl, val)
for k, v in pairs(tbl) do
if k == val then return v end
end
return nil
end
-- returns true if the string does not equal nil or '', otherwise returns false.
function p.notempty(string)
return string ~= nil and string ~= ''
end
-- returns true if any value in table1 matches a value in table2
function p.hasmatch(table1, table2)
for _,v in ipairs(table1) do
if contains(table2, v) then
return true
end
end
return false
end
-- returns true if any value in the table matches the provided value
function p.contains(table, value)
for _,v in ipairs(table) do
if v == value then
return true
end
end
return false
end
return p