Path of Exile Wiki

Please consider helping keep the wiki up to date. Check the to-do list of updates needed for version 3.14.0.

Game data exports will becoming later as the technical changes in addition to regular changes take some more time.

READ MORE

Path of Exile Wiki
m (Allow partial searches. Should still require full mod id to be valid.)
m (Call it veiled mods instead.)
(44 intermediate revisions by 3 users not shown)
Line 8: Line 8:
 
local m_game = require('Module:Game')
 
local m_game = require('Module:Game')
 
local f_item_link = require('Module:Item link').item_link
 
local f_item_link = require('Module:Item link').item_link
  +
local m_cargo = require('Module:Cargo')
   
 
local cargo = mw.ext.cargo
 
local cargo = mw.ext.cargo
Line 29: Line 30:
 
generation_type = '[[Modifiers#Mod_Generation_Type|Generation Type]]',
 
generation_type = '[[Modifiers#Mod_Generation_Type|Generation Type]]',
 
required_level = '[[Image:Level_up_icon_small.png|link=|For generated item/monster modifiers the minimum item/monster level respectively. Some generation types may not require this condition to be met, however item level restrictions may be raised to 80% of this value.]]',
 
required_level = '[[Image:Level_up_icon_small.png|link=|For generated item/monster modifiers the minimum item/monster level respectively. Some generation types may not require this condition to be met, however item level restrictions may be raised to 80% of this value.]]',
  +
labyrinth = '[[The Lord\'s Labyrinth|Labyrinth]]',
 
stat_text = m_util.html.abbr('Stats', 'Stats of the modifier and the range they can roll in (if applicable)'),
 
stat_text = m_util.html.abbr('Stats', 'Stats of the modifier and the range they can roll in (if applicable)'),
 
buff = m_util.html.abbr('Buff', 'ID of the buff granted and the values associated'),
 
buff = m_util.html.abbr('Buff', 'ID of the buff granted and the values associated'),
 
granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
 
granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
tags = '[[Tags]]',
+
tags = '[[Modifiers#Tags|Tags]]',
  +
 
 
iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
 
iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
 
iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
 
iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
 
pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),
 
pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),
  +
 
 
spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
 
spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
 
generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
 
generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
  +
  +
normal_labyrinth = 'Normal Labyrinth',
  +
cruel_labyrinth = 'Cruel Labyrinth',
  +
merciless_labyrinth = 'Merciless Labyrinth',
  +
eternal_labyrinth = 'Eternal Labyrinth',
 
},
 
},
  +
 
 
drop_down_table = {
 
drop_down_table = {
 
collapse_all = 'Collapse all',
 
collapse_all = 'Collapse all',
Line 48: Line 55:
 
prefix = 'Prefix',
 
prefix = 'Prefix',
 
suffix = 'Suffix',
 
suffix = 'Suffix',
corrupted = 'Corrupted'
+
corrupted = 'Corrupted',
  +
enchant = 'Enchantment',
  +
elder_prefix = 'Elder prefix',
  +
elder_suffix = 'Elder suffix',
  +
shaper_prefix = 'Shaper prefix',
  +
shaper_suffix = 'Shaper suffix',
  +
delve_prefix = 'Delve prefix',
  +
delve_suffix = 'Delve suffix',
  +
crusader_prefix = 'Crusader prefix',
  +
crusader_suffix = 'Crusader suffix',
  +
eyrie_prefix = 'Redeemer prefix',
  +
eyrie_suffix = 'Redeemer suffix',
  +
basilisk_prefix = 'Hunter prefix',
  +
basilisk_suffix = 'Hunter suffix',
  +
adjudicator_prefix = 'Warlord prefix',
  +
adjudicator_suffix = 'Warlord suffix',
  +
veiled_prefix = 'Veiled prefix',
  +
veiled_suffix = 'Veiled suffix',
  +
mod_group = 'Mod group:',
  +
back_to_top = 'Back to top',
 
},
 
},
  +
 
 
errors = {
 
errors = {
 
--
 
--
Line 57: Line 83:
 
sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
 
sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
 
sell_price_missing_argument = 'Both %s and %s must be specified',
 
sell_price_missing_argument = 'Both %s and %s must be specified',
  +
 
 
--
 
--
 
-- Modifier link template
 
-- Modifier link template
Line 74: Line 100:
   
 
local h = {}
 
local h = {}
  +
  +
function h.header(str)
  +
--[[
  +
This function replaces specific numbers with a generic #.
  +
]]
  +
  +
local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
  +
s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
  +
s = table.concat(m_util.string.split(s, '<br>'), ', ')
  +
s = table.concat(m_util.string.split(s, '<br />'), ' ')
  +
  +
return s
  +
end
   
 
function h.query_weights(table_name, page_ids)
 
function h.query_weights(table_name, page_ids)
  +
return m_cargo.map_results_to_id{
results = cargo.query(
 
  +
results=m_cargo.query(
string.format('mods,%s', table_name),
 
string.format('mods._pageID,%s.tag,%s.weight', table_name, table_name),
+
{'mods', table_name},
{
+
{
where=page_ids,
+
'mods._pageID',
join=string.format('mods._pageID=%s._pageID', table_name),
+
table_name .. '.tag',
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
+
table_name .. '.weight'
limit=5000,
+
},
}
+
{
  +
where=page_ids,
)
 
  +
join=string.format('mods._pageID=%s._pageID', table_name),
if #results == 5000 then
 
  +
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
error('Hit maximum cargo results')
 
end
+
}
+
),
  +
field='mods._pageID',
return m_util.cargo.map_results_to_id{results=results, table_name='mods'}
 
  +
}
 
end
 
end
   
Line 100: Line 140:
 
for i,v in pairs(results) do
 
for i,v in pairs(results) do
 
str[#str+1] = string.format(
 
str[#str+1] = string.format(
'%s - %s ([[%s|page]])',
+
'%s - %s ([[%s|page]])',
v['mods.id'] or v['mods._pageName'],
+
v['mods.id'] or v['mods._pageName'] or '',
 
string.gsub(
 
string.gsub(
v['mods.stat_text_raw'],
+
v['mods.stat_text_raw'] or 'N/A',
'<br>',
+
'<br>',
 
', '
 
', '
 
) or '',
 
) or '',
v['mods._pageName']
+
v['mods._pageName'] or ''
 
)
 
)
end
+
end
 
return table.concat(str, '<br>')
 
return table.concat(str, '<br>')
 
end
 
end
   
  +
h.tbl = {}
function h.cargo_query(tpl_args)
 
  +
h.tbl.display = {}
--[[
 
  +
h.tbl.display.factory = {}
Returns a Cargo query of all the results.
 
  +
function h.tbl.display.factory.value(args)
 
  +
-- Format options for each field:
tpl_args should include these keys:
 
  +
args.options = args.options or {}
* tpl_args.tables
 
  +
* tpl_args.fields
 
  +
-- Separator between fields:
* tpl_args.q_*
 
  +
args.delimiter = args.delimiter or ', '
 
  +
]]
 
  +
return function(tpl_args, frame, tr, data, fields)
 
  +
local values = {}
-- Split strings into tables:
 
  +
local fmt_values = {}
local tables = m_util.string.split(tpl_args.tables, ', ')
 
  +
local fields = m_util.string.split(tpl_args.fields, ', ')
 
  +
for index, field in ipairs(fields) do
 
  +
local value = {
-- Parse query arguments
 
  +
min=data[field],
local query = {
 
limit = 5000,
+
max=data[field],
offset = 0,
+
base=data[field],
}
+
}
  +
if value.min then
for key, value in pairs(tpl_args) do
 
  +
values[#values+1] = value.max
if string.sub(key, 0, 2) == 'q_' then
 
query[string.sub(key, 3)] = value
+
local opts = args.options[index] or {}
  +
-- Global colour is set, no overrides.
  +
if args.color ~= nil or opts.color == nil then
  +
opts.no_color = true
  +
end
  +
fmt_values[#fmt_values+1] = m_util.html.format_value(nil, nil, value, opts)
  +
end
 
end
 
end
end
 
 
-- Query cargo table. If there are too many results then repeat,
 
-- offset, re-query and add the remaining results:
 
local results = {}
 
repeat
 
local result = cargo.query(
 
table.concat(tables, ', '),
 
table.concat(fields, ', '),
 
query
 
)
 
query.offset = query.offset + #result
 
   
for _,v in ipairs(result) do
+
if #values == 0 then
results[#results + 1] = v
+
tr
  +
:wikitext(m_util.html.td.na())
  +
else
  +
local td = tr:tag('td')
  +
td
  +
:attr('data-sort-value', table.concat(values, args.delimiter))
  +
:wikitext(table.concat(fmt_values, args.delimiter))
  +
if args.color then
  +
td:attr('class', 'tc -' .. args.color)
  +
end
 
end
 
end
  +
end
until #result < query.limit or true -- Remove true when offset works.
 
 
return results
 
 
end
 
end
   
Line 165: Line 206:
 
mod_table.data = {
 
mod_table.data = {
 
{
 
{
arg = nil,
+
arg = 'name',
 
header = i18n.mod_table.name,
 
header = i18n.mod_table.name,
 
fields = {'mods._pageName', 'mods.id', 'mods.name'},
 
fields = {'mods._pageName', 'mods.id', 'mods.name'},
Line 173: Line 214:
 
},
 
},
 
},
 
},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, frame, tr, data, fields)
 
local name
 
local name
 
if data['mods.name'] then
 
if data['mods.name'] then
Line 180: Line 221:
 
name = data['mods.id']
 
name = data['mods.id']
 
end
 
end
  +
 
tr
 
tr
 
:tag('td')
 
:tag('td')
Line 191: Line 233:
 
header = i18n.mod_table.domain,
 
header = i18n.mod_table.domain,
 
fields = {'mods.domain'},
 
fields = {'mods.domain'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, frame, tr, data, fields)
local value = data['mods.domain']
+
local k = 'mods.domain'
tr
+
local i = tonumber(data[k])
:tag('td')
+
data[k] = m_game.constants.mod.domains[i]['short_upper']
:attr('data-sort-value', value)
+
h.tbl.display.factory.value({})(tpl_args, frame, tr, data, fields)
:wikitext(m_game.constants.mod.domains[tonumber(value)]['short_upper'])
 
 
end,
 
end,
 
order = 2000,
 
order = 2000,
Line 205: Line 246:
 
header = i18n.mod_table.generation_type,
 
header = i18n.mod_table.generation_type,
 
fields = {'mods.generation_type'},
 
fields = {'mods.generation_type'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, frame, tr, data, fields)
local value = data['mods.generation_type']
+
local k = 'mods.generation_type'
tr
+
local i = tonumber(data[k])
:tag('td')
+
data[k] = m_game.constants.mod.generation_types[i]['short_upper']
:attr('data-sort-value', value)
+
h.tbl.display.factory.value{}(tpl_args, frame, tr, data, fields)
:wikitext(m_game.constants.mod.generation_types[tonumber(value)]['short_upper'])
 
 
end,
 
end,
 
order = 2001,
 
order = 2001,
Line 219: Line 259:
 
header = i18n.mod_table.mod_group,
 
header = i18n.mod_table.mod_group,
 
fields = {'mods.mod_group'},
 
fields = {'mods.mod_group'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.mod_group'])
 
end,
 
 
order = 2002,
 
order = 2002,
 
sort_type = 'text',
 
sort_type = 'text',
Line 231: Line 267:
 
header = i18n.mod_table.mod_type,
 
header = i18n.mod_table.mod_type,
 
fields = {'mods.mod_type'},
 
fields = {'mods.mod_type'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.mod_type'])
 
end,
 
 
order = 2003,
 
order = 2003,
 
sort_type = 'text',
 
sort_type = 'text',
Line 243: Line 275:
 
header = i18n.mod_table.required_level,
 
header = i18n.mod_table.required_level,
 
fields = {'mods.required_level'},
 
fields = {'mods.required_level'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.required_level'])
 
end,
 
 
order = 2004,
 
order = 2004,
  +
},
  +
{
  +
arg = {'enchantment', 'labyrinth'},
  +
header = i18n.mod_table.labyrinth,
  +
fields = {string.format([[
  +
CONCAT(
  +
CASE mods.required_level
  +
WHEN 32 THEN "%s"
  +
WHEN 53 THEN "%s"
  +
WHEN 66 THEN "%s"
  +
WHEN 75 THEN "%s"
  +
END
  +
)=labyrinth_text]],
  +
i18n.mod_table.normal_labyrinth,
  +
i18n.mod_table.cruel_labyrinth,
  +
i18n.mod_table.merciless_labyrinth,
  +
i18n.mod_table.eternal_labyrinth)
  +
},
  +
display = h.tbl.display.factory.value{},
  +
order = 2005,
  +
sort_type = 'text',
 
},
 
},
 
{
 
{
Line 254: Line 303:
 
header = i18n.mod_table.stat_text,
 
header = i18n.mod_table.stat_text,
 
fields = {'mods.stat_text'},
 
fields = {'mods.stat_text'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, frame, tr, data, fields)
local text
+
local value
 
-- map display type shows this in another column, remove this text to avoid clogging up the list
 
-- map display type shows this in another column, remove this text to avoid clogging up the list
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
 
local texts = m_util.string.split(data['mods.stat_text'], '<br>')
 
local texts = m_util.string.split(data['mods.stat_text'], '<br>')
 
local out = {}
 
local out = {}
  +
 
 
local valid
 
local valid
 
for _, v in ipairs(texts) do
 
for _, v in ipairs(texts) do
Line 270: Line 319:
 
end
 
end
 
end
 
end
  +
 
 
if valid then
 
if valid then
 
table.insert(out, v)
 
table.insert(out, v)
 
end
 
end
 
end
 
end
  +
 
text = table.concat(out, '<br>')
+
value = table.concat(out, '<br>')
 
else
 
else
text = data['mods.stat_text']
+
value = data['mods.stat_text']
 
end
 
end
+
data['mods.stat_text'] = value
  +
h.tbl.display.factory.value{color='mod'}(tpl_args, frame, tr, data, fields)
tr
 
:tag('td')
 
:wikitext(text)
 
 
end,
 
end,
 
order = 3000,
 
order = 3000,
 
sort_type = 'text',
 
sort_type = 'text',
 
},
 
},
 
 
{
 
{
 
arg = 'buff',
 
arg = 'buff',
 
header = i18n.mod_table.buff,
 
header = i18n.mod_table.buff,
 
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
 
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{delimiter=' '},
tr
 
:tag('td')
 
:wikitext(string.format('%s %s', data['mods.granted_buff_id'], data['mods.granted_buff_value']))
 
end,
 
 
order = 4000,
 
order = 4000,
 
sort_type = 'text',
 
sort_type = 'text',
Line 305: Line 347:
 
header = i18n.mod_table.granted_skill,
 
header = i18n.mod_table.granted_skill,
 
fields = {'mods.granted_skill'},
 
fields = {'mods.granted_skill'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.granted_skill'])
 
end,
 
 
order = 4001,
 
order = 4001,
 
sort_type = 'text',
 
sort_type = 'text',
Line 317: Line 355:
 
header = i18n.mod_table.tags,
 
header = i18n.mod_table.tags,
 
fields = {'mods.tags'},
 
fields = {'mods.tags'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, frame, tr, data, fields)
tr
+
local k = 'mods.tags'
:tag('td')
+
data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ')
:wikitext(table.concat(m_util.string.split(data['mods.tags'], ','), ', '))
+
h.tbl.display.factory.value{}(tpl_args, frame, tr, data, fields)
 
end,
 
end,
 
order = 5000,
 
order = 5000,
Line 343: Line 381:
   
 
function p.mod_table(frame)
 
function p.mod_table(frame)
  +
--[[
  +
Creates a generic table for modifiers.
  +
  +
Examples
  +
--------
  +
= p.mod_table{
  +
q_tables='spawn_weights',
  +
q_join='mods._pageID=spawn_weights._pageID',
  +
q_where='mods.generation_type = 10 AND spawn_weights.tag = "boots" AND spawn_weights.weight > 0',
  +
q_orderBy='mods.id, mods.required_level',
  +
q_limit=100,
  +
stat_text=1,
  +
enchantment=1,
  +
}
  +
  +
]]
  +
  +
 
tpl_args = getArgs(frame, {
 
tpl_args = getArgs(frame, {
 
parentFirst = true
 
parentFirst = true
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
  +
-- default to enabled
  +
tpl_args.name = tpl_args.name or true
  +
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
 
tpl_args[key] = m_util.cast.boolean(tpl_args[key])
 
tpl_args[key] = m_util.cast.boolean(tpl_args[key])
 
end
 
end
  +
 
 
if string.find(tpl_args.q_where, '%[%[') ~= nil then
 
if string.find(tpl_args.q_where, '%[%[') ~= nil then
 
error('SMW leftover in where clause')
 
error('SMW leftover in where clause')
 
end
 
end
  +
 
 
local row_infos = {}
 
local row_infos = {}
 
for _, row_info in ipairs(mod_table.data) do
 
for _, row_info in ipairs(mod_table.data) do
Line 363: Line 422:
 
elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
 
elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
 
enabled = true
 
enabled = true
elseif type(row_info.arg) == 'table' then
+
elseif type(row_info.arg) == 'table' then
 
for _, argument in ipairs(row_info.arg) do
 
for _, argument in ipairs(row_info.arg) do
 
if m_util.cast.boolean(tpl_args[argument]) then
 
if m_util.cast.boolean(tpl_args[argument]) then
Line 371: Line 430:
 
end
 
end
 
end
 
end
  +
 
 
if enabled then
 
if enabled then
 
row_info.options = row_info.options or {}
 
row_info.options = row_info.options or {}
Line 377: Line 436:
 
end
 
end
 
end
 
end
  +
 
 
-- sort the rows
 
-- sort the rows
 
table.sort(row_infos, function (a, b)
 
table.sort(row_infos, function (a, b)
 
return (a.order or 0) < (b.order or 0)
 
return (a.order or 0) < (b.order or 0)
 
end)
 
end)
  +
 
-- Set tables
+
-- Set required and extra tables:
local tables = 'mods'
+
local tables = {'mods'}
if tpl_args.q_tables then
+
for _, v in ipairs(m_util.string.split_args(tpl_args.q_tables, {',%s*'})) do
tables = tables .. ',' .. tpl_args.q_tables
+
tables[#tables+1] = v
 
end
 
end
  +
 
  +
-- Set required and extra fields:
 
-- Set required fields
 
 
local fields = {
 
local fields = {
 
'mods._pageID',
 
'mods._pageID',
 
}
 
}
for _, rowinfo in ipairs(row_infos) do
+
for _, row_info in ipairs(row_infos) do
if type(rowinfo.fields) == 'function' then
+
if type(row_info.fields) == 'function' then
rowinfo.fields = rowinfo.fields()
+
row_info.fields = row_info.fields()
 
end
 
end
for index, field in ipairs(rowinfo.fields) do
+
for index, field in ipairs(row_info.fields) do
rowinfo.options[index] = rowinfo.options[index] or {}
+
row_info.options[index] = row_info.options[index] or {}
 
fields[#fields+1] = field
 
fields[#fields+1] = field
 
end
 
end
 
end
 
end
  +
tpl_args._extra_fields = m_util.string.split_args(tpl_args.q_fields, {',%s*'})
 
  +
for _, v in ipairs(tpl_args._extra_fields) do
-- Parse query arguments
 
  +
fields[#fields+1] = v
  +
end
  +
  +
-- Parse query arguments:
 
local query = {
 
local query = {
 
-- Workaround: fix duplicates
 
-- Workaround: fix duplicates
 
groupBy='mods._pageID',
 
groupBy='mods._pageID',
 
}
 
}
for key, value in pairs(tpl_args) do
+
for key, value in pairs(tpl_args) do
 
if string.sub(key, 0, 2) == 'q_' then
 
if string.sub(key, 0, 2) == 'q_' then
 
query[string.sub(key, 3)] = value
 
query[string.sub(key, 3)] = value
 
end
 
end
 
end
 
end
  +
 
local results = cargo.query(
+
local results = m_cargo.query(tables, fields, query)
  +
tables,
 
table.concat(fields, ','),
 
query
 
)
 
 
 
if #results == 0 then
 
if #results == 0 then
 
if tpl_args.default ~= nil then
 
if tpl_args.default ~= nil then
Line 428: Line 486:
 
end
 
end
 
end
 
end
  +
 
-- this might be needed in other queries, currently not checking if it's actually needed
+
-- this might be needed in other queries, currently not checking if
-- because performance impact should be neglible
+
-- it's actually needed because performance impact should be neglible
 
local page_ids = {}
 
local page_ids = {}
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
page_ids[#page_ids+1] = string.format('mods._pageID="%s"', row['mods._pageID'])
+
page_ids[#page_ids+1] = string.format(
  +
'mods._pageID="%s"',
  +
row['mods._pageID']
  +
)
 
end
 
end
 
page_ids = table.concat(page_ids, ' OR ')
 
page_ids = table.concat(page_ids, ' OR ')
  +
 
 
local weights = {}
 
local weights = {}
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
Line 443: Line 504:
 
end
 
end
 
end
 
end
  +
 
 
local stats
 
local stats
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
Line 450: Line 511:
 
query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
 
query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
 
end
 
end
  +
 
local stat_results = cargo.query(
+
stats = m_cargo.map_results_to_id{
'mods,mod_stats',
+
results=m_cargo.query(
'mods._pageID,mod_stats.id,mod_stats.min,mod_stats.max',
+
{'mods', 'mod_stats'},
{
+
{
where=string.format('(%s) AND (%s)', page_ids, table.concat(query_stat_ids, ' OR ')),
+
'mods._pageID',
join='mods._pageID=mod_stats._pageID',
+
'mod_stats.id',
orderBy='mods.id ASC',
+
'mod_stats.min',
limit=5000,
+
'mod_stats.max',
}
+
},
)
+
{
  +
where=string.format(
if #stat_results == 5000 then
 
error('Hit maximum cargo results')
+
'(%s) AND (%s)',
end
+
page_ids,
  +
table.concat(query_stat_ids, ' OR ')
 
  +
),
stats = m_util.cargo.map_results_to_id{results=stat_results, table_name='mods'}
 
  +
join='mods._pageID=mod_stats._pageID',
  +
orderBy='mods.id ASC',
  +
}
  +
),
  +
field='mods._pageID'
  +
}
  +
 
-- In addition map stats to stat <-> min/max pairs
 
-- In addition map stats to stat <-> min/max pairs
 
for page_id, rows in pairs(stats) do
 
for page_id, rows in pairs(stats) do
 
local stat_id_map = {}
 
local stat_id_map = {}
 
for _, row in ipairs(rows) do
 
for _, row in ipairs(rows) do
stat_id_map[row['mod_stats.id']] = {min=tonumber(row['mod_stats.min']), max=tonumber(row['mod_stats.max'])}
+
stat_id_map[row['mod_stats.id']] = {
  +
min=tonumber(row['mod_stats.min']),
  +
max=tonumber(row['mod_stats.max'])
  +
}
 
end
 
end
 
stats[page_id] = stat_id_map
 
stats[page_id] = stat_id_map
 
end
 
end
 
end
 
end
  +
 
  +
--
  +
-- Display
  +
--
  +
  +
-- Preformance optimization
  +
for index, field in ipairs(tpl_args._extra_fields) do
  +
field = m_util.string.split(field, '%s*=%s*')
  +
-- field[2] will be nil if there is no alias
  +
tpl_args._extra_fields[index] = field[2] or field[1]
  +
end
  +
 
local tbl = mw.html.create('table')
 
local tbl = mw.html.create('table')
 
tbl:attr('class', 'wikitable sortable modifier-table')
 
tbl:attr('class', 'wikitable sortable modifier-table')
 
 
-- Header
 
-- Header
  +
 
 
local tr = tbl:tag('tr')
 
local tr = tbl:tag('tr')
  +
local display_fields = {}
for _, row_info in ipairs(row_infos) do
 
  +
for i, row_info in ipairs(row_infos) do
  +
for j, field in ipairs(row_info.fields) do
  +
-- Aliased name is used as keys in the results:
  +
field = m_util.string.split(field, '%s*=%s*')
  +
field = field[2] or field[1]
  +
  +
-- Make a new field table since mod_table.data will remain
  +
-- modified the 2nd time a function is run and then crash.
  +
if j == 1 then
  +
display_fields[i] = {}
  +
end
  +
display_fields[i][j] = field
  +
end
  +
 
tr
 
tr
 
:tag('th')
 
:tag('th')
Line 489: Line 584:
 
:done()
 
:done()
 
end
 
end
  +
 
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
 
for stat_id, data in pairs(mod_table.stat_ids) do
 
for stat_id, data in pairs(mod_table.stat_ids) do
Line 499: Line 594:
 
end
 
end
 
end
 
end
  +
 
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
 
if tpl_args[key] then
 
if tpl_args[key] then
Line 507: Line 602:
 
end
 
end
 
end
 
end
  +
 
  +
for _, field in ipairs(tpl_args._extra_fields) do
  +
tr
  +
:tag('th')
  +
:wikitext(field)
  +
end
  +
 
-- Body
 
-- Body
  +
 
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
 
tr = tbl:tag('tr')
 
tr = tbl:tag('tr')
  +
 
for _, rowinfo in ipairs(row_infos) do
+
for i, row_info in ipairs(row_infos) do
 
-- this has been cast from a function in an earlier step
 
-- this has been cast from a function in an earlier step
 
local display = true
 
local display = true
for index, field in ipairs(rowinfo.fields) do
+
for j, field in ipairs(display_fields[i]) do
 
-- this will bet set to an empty value not nil confusingly
 
-- this will bet set to an empty value not nil confusingly
if row[field] == '' then
+
if row[field] == nil or row[field] == '' then
if rowinfo.options[index].optional ~= true then
+
if row_info.options[j].optional ~= true then
 
display = false
 
display = false
 
break
 
break
Line 528: Line 629:
 
end
 
end
 
if display then
 
if display then
rowinfo.display(tpl_args, frame, tr, row, rowinfo.fields)
+
row_info.display(tpl_args, frame, tr, row, display_fields[i])
 
else
 
else
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
 
for stat_id, data in pairs(mod_table.stat_ids) do
 
for stat_id, data in pairs(mod_table.stat_ids) do
Line 555: Line 656:
 
end
 
end
 
end
 
end
  +
 
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
 
if tpl_args[key] then
 
if tpl_args[key] then
 
local weight_out = {}
 
local weight_out = {}
 
for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
 
for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
weight_out[#weight_out+1] = string.format('%s %s', wrow[key .. '.tag'], wrow[key .. '.weight'])
+
if wrow[key .. '.tag'] and wrow[key .. '.weight'] then
  +
weight_out[#weight_out+1] = string.format(
  +
'%s %s',
  +
wrow[key .. '.tag'],
  +
wrow[key .. '.weight']
  +
)
  +
end
 
end
 
end
  +
 
if #weight_out > 0 then
 
if #weight_out > 0 then
 
tr
 
tr
:tag('td')
+
:tag('td')
:wikitext(table.concat(weight_out, '<br>'))
+
:wikitext(table.concat(weight_out, '<br>'))
:done()
+
:done()
else
+
else
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
 
end
 
end
  +
end
  +
end
  +
  +
for _, field in ipairs(tpl_args._extra_fields) do
  +
if row[field] then
  +
tr
  +
:tag('td')
  +
:wikitext(row[field])
  +
else
  +
tr:wikitext(m_util.html.td.na())
 
end
 
end
 
end
 
end
 
end
 
end
  +
 
 
return tostring(tbl)
 
return tostring(tbl)
 
end
 
end
Line 583: Line 701:
 
-- Modifier link
 
-- Modifier link
 
-- ---------------------------------------------------------------------
 
-- ---------------------------------------------------------------------
  +
   
 
function p.modifier_link(frame)
 
function p.modifier_link(frame)
 
--[[
 
--[[
 
Finds and links to a modifier in formatted form.
 
Finds and links to a modifier in formatted form.
  +
 
Examples:
+
To do list
  +
----------
  +
* Standardize hoverbox so it can be used in multiple places easily
  +
and make it easy to add rows of data.
  +
  +
Examples
  +
--------
 
= p.modifier_link{"Tyrannical"}
 
= p.modifier_link{"Tyrannical"}
 
= p.modifier_link{"Flaring"}
 
= p.modifier_link{"Flaring"}
 
= p.modifier_link{"Dictator's"}
 
= p.modifier_link{"Dictator's"}
  +
= p.modifier_link{"StrDexMaster%"}
 
= p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}
 
= p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}
  +
 
 
]]
 
]]
   
Line 599: Line 725:
 
local tpl_args = getArgs(frame, {parentFirst = true})
 
local tpl_args = getArgs(frame, {parentFirst = true})
 
local frame = m_util.misc.get_frame(frame)
 
local frame = m_util.misc.get_frame(frame)
  +
 
 
-- Aliases:
 
-- Aliases:
 
tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''
 
tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''
  +
 
-- Define query arguments:
+
-- Query cargo rows:
  +
tpl_args.results = m_cargo.map_results_to_id{
local tables = {'mods', 'mod_stats', 'spawn_weights'}
 
  +
results=m_cargo.query(
local fields = {'mods.name', 'mods.stat_text', 'mods.stat_text_raw', 'mods.generation_type', 'mods._pageName', 'mod_stats.max', 'mod_stats.min', 'spawn_weights.tag', 'spawn_weights.weight', 'mods.id', 'mod_stats.id'}
 
  +
{'mods', 'mod_stats', 'spawn_weights'},
local query = {
 
  +
{
join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
 
where = string.format(
+
'mods.name',
'(mods.name LIKE "%s" or mods.id LIKE "%s" or mods.stat_text LIKE "%s" or mods.stat_text_raw LIKE "%s") AND mod_stats.id LIKE "%%%s%%"',
+
'mods.stat_text',
tpl_args.modid,
+
'mods.stat_text_raw',
tpl_args.modid,
+
'mods.generation_type',
tpl_args.modid,
+
'mods.tags',
tpl_args.modid,
+
'mods._pageName',
tpl_args.statid or '%'
+
'mod_stats.max',
  +
'mod_stats.min',
  +
'spawn_weights.tag',
  +
'spawn_weights.weight',
  +
'mods.id',
  +
'mod_stats.id'
  +
},
  +
{
  +
join = [[
  +
mods._pageName=mod_stats._pageName,
  +
mods._pageName=spawn_weights._pageName
  +
]],
  +
where = string.format(
  +
[[
  +
(
  +
mods.name LIKE "%s" or
  +
mods.id LIKE "%s" or
  +
mods.stat_text LIKE "%s" or
  +
mods.stat_text_raw LIKE "%s"
  +
)
  +
AND mod_stats.id LIKE "%%%s%%"
  +
]],
  +
tpl_args.modid,
  +
tpl_args.modid,
  +
tpl_args.modid,
  +
tpl_args.modid,
  +
tpl_args.statid or '%'
  +
),
  +
-- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
  +
}
 
),
 
),
-- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
+
field='mods._pageName',
  +
keep_id_field=true,
  +
append_id_field=true,
 
}
 
}
  +
 
  +
-- Get a sorted list that only has unique page names:
-- Query cargo rows:
 
  +
tpl_args.results_unique = {{}}
local results = m_util.cargo.query(tables, fields, query, args)
 
  +
for i, v in ipairs(tpl_args.results) do
 
  +
tpl_args.results_unique[i] = tpl_args.results[v][1]
-- Create own list for each cargo table and group by page name:
 
tpl_args.tbl = {}
 
for _,v in ipairs(tables) do
 
tpl_args.tbl[v] = {}
 
 
end
 
end
  +
 
tpl_args.results_unique = {}
 
local hash = {}
 
for _,v in ipairs(results) do
 
for ii, vv in pairs(tpl_args.tbl) do
 
if tpl_args.tbl[ii][v['mods._pageName']] == nil then
 
tpl_args.tbl[ii][v['mods._pageName']] = {}
 
end
 
local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
 
tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
 
 
-- Get a sorted list that only has unique page names:
 
if hash[v['mods._pageName']] ~= true then
 
local m = #tpl_args.results_unique
 
tpl_args.results_unique[m+1] = v
 
hash[v['mods._pageName']] = true
 
end
 
end
 
end
 
 
 
-- Helpful error handling:
 
-- Helpful error handling:
 
local err_tbl = {
 
local err_tbl = {
 
{
 
{
bool = #results == 0,
+
bool = #tpl_args.results == 0,
 
disp = {
 
disp = {
 
i18n.errors.no_results,
 
i18n.errors.no_results,
Line 666: Line 801:
 
disp = {
 
disp = {
 
string.gsub(
 
string.gsub(
i18n.errors.incorrect_modid,
+
i18n.errors.incorrect_modid,
 
'%%s',
 
'%%s',
 
tpl_args.modid,
 
tpl_args.modid,
Line 675: Line 810:
 
},
 
},
 
}
 
}
for _,v in ipairs(err_tbl) do
+
for _, v in ipairs(err_tbl) do
 
if v.bool then
 
if v.bool then
 
local cats = {'Pages with modifier link errors'}
 
local cats = {'Pages with modifier link errors'}
Line 683: Line 818:
 
end
 
end
 
end
 
end
  +
 
 
-- Display formats:
 
-- Display formats:
 
local display = {
 
local display = {
 
abbr = {
 
abbr = {
 
display = function(tpl_args, frame)
 
display = function(tpl_args, frame)
return string.format(
+
local name = m_util.html.poe_color(
'%s%s',
+
'mod',
m_util.html.poe_color(
+
string.format(
'mod',
+
'[[%s|%s]]',
string.format(
+
tpl_args.results_unique[1]['mods._pageName'],
'[[%s|%s]]',
+
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
tpl_args.results_unique[1]['mods._pageName'],
 
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
 
)
 
),
 
m_util.html.abbr(
 
'[?]',
 
string.gsub(
 
tpl_args.results_unique[1]['mods.stat_text_raw'],
 
'<br>',
 
', '
 
),
 
class
 
 
)
 
)
 
)
 
)
  +
  +
local tooltip_table = {
  +
m_util.html.poe_color(
  +
'mod',
  +
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
  +
),
  +
m_util.html.poe_color(
  +
'help',
  +
m_game.constants.mod.generation_types[
  +
tonumber(
  +
tpl_args.results_unique[1]['mods.generation_type']
  +
)
  +
].full
  +
),
  +
-- m_util.html.poe_color(
  +
-- 'help',
  +
-- table.concat(
  +
-- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
  +
-- ', '
  +
-- )
  +
-- ),
  +
m_util.html.poe_color(
  +
'normal',
  +
tpl_args.results_unique[1]['mods.stat_text_raw']
  +
),
  +
}
  +
  +
local tt_tbl_fltrd = {}
  +
for _,v in ipairs(tooltip_table) do
  +
if v ~= nil and v ~= '' then
  +
tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
  +
end
  +
end
  +
local tooltip = table.concat(tt_tbl_fltrd, '<br>')
  +
  +
return m_util.html.tooltip(name, tooltip, class)
 
end,
 
end,
 
},
 
},
Line 715: Line 874:
 
'%s - %s (%s)',
 
'%s - %s (%s)',
 
m_util.html.poe_color(
 
m_util.html.poe_color(
'mod',
+
'mod',
 
string.format(
 
string.format(
'[[%s|%s]]',
+
'[[%s|%s]]',
tpl_args.results_unique[1]['mods._pageName'],
+
tpl_args.results_unique[1]['mods._pageName'],
 
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
 
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
 
)
 
)
 
),
 
),
 
m_util.html.poe_color(
 
m_util.html.poe_color(
'mod',
+
'mod',
 
string.gsub(
 
string.gsub(
tpl_args.results_unique[1]['mods.stat_text'],
+
tpl_args.results_unique[1]['mods.stat_text'],
'<br>',
+
'<br>',
 
', '
 
', '
 
)
 
)
Line 741: Line 900:
 
display = function(tpl_args, frame)
 
display = function(tpl_args, frame)
 
return m_util.html.poe_color(
 
return m_util.html.poe_color(
'mod',
+
'mod',
 
tpl_args.results_unique[1]['mods.stat_text']
 
tpl_args.results_unique[1]['mods.stat_text']
 
)
 
)
Line 749: Line 908:
 
display = function(tpl_args, frame)
 
display = function(tpl_args, frame)
 
local statid = {}
 
local statid = {}
for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
+
for _, v in ipairs(tpl_args.results[tpl_args.results_unique[1]['mods._pageName']]) do
 
statid[#statid+1] = v['mod_stats.id']
 
statid[#statid+1] = v['mod_stats.id']
 
if tpl_args.statid == v['mod_stats.id'] then
 
if tpl_args.statid == v['mod_stats.id'] then
Line 755: Line 914:
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.statid == nil then
 
if tpl_args.statid == nil then
 
return m_util.html.error(
 
return m_util.html.error(
 
{
 
{
 
msg = string.format(
 
msg = string.format(
i18n.errors.undefined_statid,
+
i18n.errors.undefined_statid,
 
table.concat(statid, ', ')
 
table.concat(statid, ', ')
 
)
 
)
Line 771: Line 930:
 
display = function(tpl_args, frame)
 
display = function(tpl_args, frame)
 
local statid = {}
 
local statid = {}
for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
+
for _, v in ipairs(tpl_args.results[tpl_args.results_unique[1]['mods._pageName']]) do
 
statid[#statid+1] = v['mod_stats.id']
 
statid[#statid+1] = v['mod_stats.id']
 
if tpl_args.statid == v['mod_stats.id'] then
 
if tpl_args.statid == v['mod_stats.id'] then
Line 777: Line 936:
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.statid == nil then
 
if tpl_args.statid == nil then
 
return m_util.html.error(
 
return m_util.html.error(
 
{
 
{
 
msg = string.format(
 
msg = string.format(
i18n.errors.undefined_statid,
+
i18n.errors.undefined_statid,
 
table.concat(statid, ', ')
 
table.concat(statid, ', ')
 
)
 
)
Line 791: Line 950:
 
},
 
},
 
}
 
}
  +
 
 
return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
 
return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
 
end
 
end
Line 801: Line 960:
 
-- Drop down list
 
-- Drop down list
 
-- ---------------------------------------------------------------------
 
-- ---------------------------------------------------------------------
 
function h.get_mod_domain(cargo_query)
 
--[[
 
Gets the mod domain based on the item class.
 
]]
 
 
local out = cargo_query
 
local mod_domains = m_game.constants.mod.domains
 
 
-- Set the item class as key and the corresponding mod domain as
 
-- value:
 
local class_to_domain = {
 
['Life Flasks']=2,
 
['Mana Flasks']=2,
 
['Hybrid Flasks']=2,
 
['Utility Flasks']=2,
 
['Critical Utility Flasks']=2,
 
['Maps']=5,
 
['Jewel']=11,
 
['Leaguestones']=13,
 
['Abyss Jewel']=14,
 
}
 
 
for i,_ in ipairs(out) do
 
-- Get the domain, if it's not defined in the table assume it's
 
-- in the item domain.
 
out[i]['items.domain'] = class_to_domain[out[i]['items.class']] or 1
 
 
-- Convert the mod domain number to understandable text:
 
out[i]['items.domain_text'] = mod_domains[out[i]['items.domain']]['short_lower']
 
end
 
 
return out
 
end
 
 
function h.get_item_tags(frame)
 
--[[
 
This function queries for the tags of a specific item.
 
]]
 
 
-- Get template arguments:
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local frame = m_util.misc.get_frame(frame)
 
 
-- Format the cargo query:
 
tpl_args.tables = 'items'
 
tpl_args.fields = 'items.name, items.tags, items.class, items.inventory_icon, items.html, items._pageName'
 
local tbl = {
 
{tpl_args.page, 'items._pageName = "%s"'},
 
{tpl_args.item, 'items.name = "%s"'},
 
}
 
for _,v in ipairs(tbl) do
 
if v[1] ~= nil then
 
condition = string.format(v[2], v[1])
 
break
 
end
 
end
 
tpl_args.q_where = condition
 
tpl_args.q_groupBy = 'items._pageName'
 
tpl_args.q_orderBy = 'items.name'
 
 
-- Query mods with cargo:
 
results = h.cargo_query(tpl_args)
 
 
-- Find out what mod domain an item class is in:
 
results = h.get_mod_domain(results)
 
 
return results
 
end
 
 
function h.header(str)
 
--[[
 
This function replaces specific numbers with a generic #.
 
]]
 
 
local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
 
s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
 
s = table.concat(m_util.string.split(s, '<br>'), ', ')
 
 
return s
 
end
 
   
 
function h.get_spawn_chance(frame)
 
function h.get_spawn_chance(frame)
 
--[[
 
--[[
Calculates the spawn chance of a set of mods that all have a
+
Calculates the spawn chance of a set of mods that all have a
 
spawn weight.
 
spawn weight.
  +
 
 
]]
 
]]
  +
 
 
-- Get template arguments:
 
-- Get template arguments:
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local frame = m_util.misc.get_frame(frame)
 
local frame = m_util.misc.get_frame(frame)
  +
 
 
-- Get the table:
 
-- Get the table:
 
local tbl = tpl_args['tbl']
 
local tbl = tpl_args['tbl']
  +
 
 
-- Probabilities affecting the result besides the spawn weight:
 
-- Probabilities affecting the result besides the spawn weight:
local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
+
local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
  +
 
 
-- Total number of outcomes.
 
-- Total number of outcomes.
 
local N = 0
 
local N = 0
Line 905: Line 983:
 
N = N + tbl[i]['spawn_weights.weight']
 
N = N + tbl[i]['spawn_weights.weight']
 
end
 
end
  +
 
 
for i,_ in ipairs(tbl) do
 
for i,_ in ipairs(tbl) do
 
-- Number of ways it can happen:
 
-- Number of ways it can happen:
Line 912: Line 990:
 
-- Truncated value:
 
-- Truncated value:
 
tbl[i]['spawn_weights.chance'] = string.format(
 
tbl[i]['spawn_weights.chance'] = string.format(
"%0.2f%%",
+
"%0.2f%%",
 
n/N * chance_multiplier*100
 
n/N * chance_multiplier*100
 
)
 
)
end
+
end
  +
 
 
return tbl
 
return tbl
 
end
 
end
  +
 
 
function p.drop_down_table(frame)
 
function p.drop_down_table(frame)
 
--[[
 
--[[
This function queries mods that can spawn on an item. It compares
+
This function queries mods that can spawn on an item. It compares
the item tags and the spawn weight tags. If there's a match and
+
the item tags and the spawn weight tags. If there's a match and
the spawn weight is larger than zero, then that mod is added to a
+
the spawn weight is larger than zero, then that mod is added to a
 
drop down list.
 
drop down list.
  +
 
To Do
+
To Do:
  +
* Add support to:
* Misses forsaken masters currently.
 
  +
* Forsaken masters
* Add a proper expand/collapse toggle for the entire header row so
 
  +
* Bestiary
it reacts together with mw-collapsible.
 
  +
* Add a proper expand/collapse toggle for the entire header row so
* Show Mod group in a better way perhaps:
 
Mod group (expanded)
+
it reacts together with mw-collapsible.
  +
* Show Mod group in a better way perhaps:
# to Damage (Collapsed)
 
3 to Damage
+
Mod group [Collapsible, default=Expanded]
5 to Damage
+
# to Damage [Collapsible, default=Collapsed]
  +
3 to Damage
 
  +
5 to Damage
 
  +
* Add a where condition that somehow filters out mods that obviously
Examples:
 
  +
wont match with the item. spawn_weights.weight>0 isn't enough due
  +
to possible edge cases.
  +
  +
  +
Examples:
 
Weapons
 
Weapons
p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
+
= p.drop_down_table{item='Rusted Hatchet', header='One Handed Axes'}
p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
+
= p.drop_down_table{item='Stone Axe', header='Two Handed Axes'}
   
 
Accessories
 
Accessories
p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
+
= p.drop_down_table{item='Amber Amulet', header='Amulets'}
  +
 
 
Jewels
 
Jewels
p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
+
= p.drop_down_table{item='Cobalt Jewel', header='Jewels'}
  +
 
 
Armour
 
Armour
p.drop_down_table{item = 'Plate Vest', header = 'Body armours'}
+
= p.drop_down_table{item='Plate Vest', header='Body armours'}
  +
 
  +
Boots
p.drop_down_table{
 
item = 'Fishing Rod',
+
= p.drop_down_table{item='Iron Greaves', header='Boots'}
  +
header = 'FISH PLEASE',
 
  +
= p.drop_down_table{
item_tags = 'fishing_rod',
 
  +
item='Fishing Rod',
extra_fields = 'Has spawn weight, Has spawn chance'
 
  +
header='FISH PLEASE',
  +
item_tags='fishing_rod',
  +
extra_fields='mods.tags'
 
}
 
}
  +
 
 
= p.drop_down_table{
 
= p.drop_down_table{
item = 'Fishing Rod',
+
item='Fishing Rod',
item_tags = 'axe, one_hand_weapon, onehand, weapon, default',
+
item_tags='axe, one_hand_weapon, onehand, weapon, default',
extra_item_tags = 'fishing_rod'
+
extra_item_tags='fishing_rod'
 
}
 
}
  +
 
 
= p.drop_down_table{
 
= p.drop_down_table{
item = 'Vaal Blade',
+
item='Vaal Blade',
 
}
 
}
  +
 
 
]]
 
]]
   
Line 974: Line 1,060:
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local frame = m_util.misc.get_frame(frame)
 
local frame = m_util.misc.get_frame(frame)
  +
 
-- Get the items tags:
+
-- Format the cargo query:
  +
local where
local get_item_tags = h.get_item_tags(tpl_args)[1]
 
  +
for _,v in ipairs({
 
  +
{tpl_args.page, 'items._pageName = "%s"'},
-- For some reason cargo queried item tags, are not comma-space
 
  +
{tpl_args.item, 'items.name = "%s"'},
-- separated any more.
 
local item_tags = {}
+
}) do
if tpl_args.item_tags ~= nil then
+
if v[1] ~= nil then
item_tags = m_util.string.split(tpl_args.item_tags, ', ')
+
where = string.format(v[2], v[1])
else
+
break
item_tags = m_util.string.split(get_item_tags['items.tags'], ',')
 
end
 
if tpl_args.extra_item_tags then
 
local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
 
for _,v in ipairs(extra_item_tags) do
 
item_tags[#item_tags+1] = v
 
 
end
 
end
 
end
 
end
  +
-- local item_tags = m_util.string.split(
 
-- table.concat(
+
local item_info = m_cargo.query(
-- {
+
{'items'},
  +
{
-- tpl_args.item_tags or get_item_tags['items.tags'],
 
-- tpl_args.extra_item_tags,
+
'items.name',
-- },
+
'items.tags',
-- ', '
+
'items.class_id',
-- ),
+
'items.inventory_icon',
-- ', '
+
'items.html',
  +
'items.release_version',
-- )
 
  +
'items._pageName'
 
+
},
  +
{
  +
where=where,
  +
groupBy='items._pageName',
  +
orderBy='items.name, items.release_version DESC',
  +
}
  +
)[1]
  +
  +
-- Set the item class as key and the corresponding mod domain as
  +
-- value:
  +
local class_to_domain = {
  +
['LifeFlask']=2,
  +
['ManaFlask']=2,
  +
['HybridFlask']=2,
  +
['UtilityFlask']=2,
  +
['UtilityFlaskCritical']=2,
  +
['Map']=5,
  +
['Jewel']=10,
  +
['Leaguestone']=12,
  +
['AbyssJewel']=13,
  +
}
  +
-- Get the domain, if it's not defined in the table assume it's
  +
-- in the item domain.
  +
item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1
  +
  +
-- Convert the mod domain number to understandable text:
  +
item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
  +
  +
-- Format item tags:
  +
tpl_args.item_tags = m_util.string.split_args(
  +
tpl_args.item_tags or item_info['items.tags'],
  +
{sep=',%s*'}
  +
)
  +
for _,v in ipairs(m_util.string.split_args(tpl_args.extra_item_tags, {sep=',%s*'})) do
  +
tpl_args.item_tags[#tpl_args.item_tags+1] = v
  +
end
  +
  +
-- Format extra fields:
  +
local extra_fields = m_util.string.split_args(tpl_args.extra_fields, {sep=',%s*'})
  +
  +
  +
-- Get tags that are appended to special items:
  +
local elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
  +
local shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
  +
local crusader_tag = m_game.constants.item.classes[item_info['items.class_id']]['crusader_tag']
  +
local eyrie_tag = m_game.constants.item.classes[item_info['items.class_id']]['eyrie_tag']
  +
local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_tag']
  +
local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']
  +
 
-- Create drop down lists in these sections and query in these
 
-- Create drop down lists in these sections and query in these
 
-- generation types.
 
-- generation types.
local section = {}
+
local section = {
section = {
+
{
[1] = {
 
 
header = i18n.drop_down_table.prefix,
 
header = i18n.drop_down_table.prefix,
generation_type = 1,
+
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
 
},
 
},
[2] = {
+
{
 
header = i18n.drop_down_table.suffix,
 
header = i18n.drop_down_table.suffix,
generation_type = 2,
+
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
 
},
 
},
[3] = {
+
{
  +
tags = {elder_tag},
  +
header = i18n.drop_down_table.elder_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
elder_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {elder_tag},
  +
header = i18n.drop_down_table.elder_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
elder_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {shaper_tag},
  +
header = i18n.drop_down_table.shaper_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
shaper_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {shaper_tag},
  +
header = i18n.drop_down_table.shaper_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
shaper_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.delve_prefix,
  +
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
1,
  +
16
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.delve_suffix,
  +
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
2,
  +
16
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
},
  +
{
  +
tags = {crusader_tag},
  +
header = i18n.drop_down_table.crusader_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
crusader_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {crusader_tag},
  +
header = i18n.drop_down_table.crusader_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
crusader_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {eyrie_tag},
  +
header = i18n.drop_down_table.eyrie_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
eyrie_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {eyrie_tag},
  +
header = i18n.drop_down_table.eyrie_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
eyrie_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {basilisk_tag},
  +
header = i18n.drop_down_table.basilisk_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
basilisk_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {basilisk_tag},
  +
header = i18n.drop_down_table.basilisk_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
basilisk_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {adjudicator_tag},
  +
header = i18n.drop_down_table.adjudicator_prefix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
adjudicator_tag,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {adjudicator_tag},
  +
header = i18n.drop_down_table.adjudicator_suffix,
  +
where = function(tpl_args, frame, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
adjudicator_tag,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.veiled_prefix,
  +
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL
  +
AND mods.id LIKE "%%Master%%")
  +
]],
  +
item_tag,
  +
1,
  +
9
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.veiled_suffix,
  +
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL
  +
AND mods.id LIKE "%%Master%%")
  +
]],
  +
item_tag,
  +
2,
  +
9
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
},
  +
{
 
header = i18n.drop_down_table.corrupted,
 
header = i18n.drop_down_table.corrupted,
generation_type = 5,
+
where = function(tpl_args, frame, value)
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
+
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
5,
  +
item_info['items.domain']
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
  +
is_implicit = true,
  +
},
  +
{
  +
header = i18n.drop_down_table.enchant,
  +
where = function(tpl_args, frame, value)
  +
local where = {}
  +
for _, item_tag in ipairs(tpl_args.item_tags) do
  +
where[#where+1] = string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text IS NOT NULL)
  +
]],
  +
item_tag,
  +
10,
  +
item_info['items.domain']
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
is_implicit = true,
 
},
 
},
-- [4] = {
 
-- header = 'Forsaken masters',
 
-- generation_type = 'master',
 
-- },
 
 
}
 
}
  +
 
  +
-- Save the original tag format:
 
  +
local item_tags_orig = {}
  +
for i,v in ipairs(tpl_args.item_tags) do
  +
item_tags_orig[i] = v
  +
end
  +
  +
local item_mods = {}
  +
local mod_group_counter = {}
  +
mod_group_counter['all'] = {}
  +
local extra_fieldss = {}
  +
local table_index_base = -1
  +
for _, sctn in ipairs(section) do
  +
item_mods[sctn['header']] = {}
  +
  +
-- Preallocate the mod group counter, implicit and explicit mods
  +
-- are counted separetely because they can spawn together:
  +
mod_group_counter[sctn['header']] = {}
  +
local adj = 'explicit'
  +
if sctn['is_implicit'] then
  +
adj = 'implicit'
  +
end
  +
for _, header in ipairs({sctn['header'], 'all'}) do
  +
if mod_group_counter[header][adj] == nil then
  +
mod_group_counter[header][adj] = {}
  +
end
  +
end
  +
  +
local continue = true
  +
local current_tags
  +
if sctn['tags'] then
  +
-- some item classes do not have shaper/elder items, so the table
  +
-- will not contain any tags:
  +
if #sctn['tags'] == 0 then
  +
continue = false
  +
else
  +
current_tags = sctn['tags']
  +
end
  +
else
  +
current_tags = {}
  +
-- Reset to original tags:
  +
for i,v in ipairs(item_tags_orig) do
  +
current_tags[i] = v
  +
end
  +
end
  +
  +
if continue then
  +
-- Cargo preparation:
  +
local q_fields = {
  +
'mods._pageName',
  +
'mods.name',
  +
'mods.id',
  +
'mods.required_level',
  +
'mods.generation_type',
  +
'mods.domain',
  +
'mods.mod_group',
  +
'mods.mod_type',
  +
'mods.stat_text',
  +
'mods.stat_text_raw',
  +
'mods.tags',
  +
'mod_stats.id',
  +
'spawn_weights.tag',
  +
'spawn_weights.weight',
  +
'spawn_weights.ordinal',
  +
'spawn_weights._pageName'
  +
}
  +
for i, v in ipairs(extra_fields) do
  +
q_fields[#q_fields+1] = v
  +
end
  +
  +
-- Query mods and map the results to the pagename:
  +
local results = m_cargo.map_results_to_id{
  +
results=m_cargo.query(
  +
{'mods', 'spawn_weights', 'mod_stats'},
  +
q_fields,
  +
{
  +
join = [[
  +
mods._pageName=spawn_weights._pageName,
  +
mods._pageName=mod_stats._pageName
  +
]],
  +
where = sctn['where'](tpl_args, frame, value),
  +
groupBy = [[
  +
mods._pageName,
  +
spawn_weights.tag,
  +
spawn_weights.weight
  +
]],
  +
orderBy = [[
  +
mods.generation_type,
  +
mods.mod_group,
  +
mods.mod_type,
  +
mods.required_level,
  +
mods._pageName,
  +
spawn_weights.ordinal
  +
]],
  +
}
  +
),
  +
field='mods._pageName',
  +
keep_id_field=true,
  +
append_id_field=true,
  +
}
  +
  +
if #results > 0 then
  +
-- Loop through all found modifiers:
  +
local last
  +
for _, id in ipairs(results) do
  +
-- Loop through all the modifier tags until they match
  +
-- the item tags:
  +
local j = 0
  +
local tag_match_stop
  +
repeat
  +
j = j+1
  +
local mod_tag = results[id][j]['spawn_weights.tag']
  +
local mod_tag_weight = tonumber(
  +
results[id][j]['spawn_weights.weight']
  +
)
  +
  +
-- Loop through the item tags until it matches the
  +
-- spawn weight tag and the mod tag has a value larger than
  +
-- zero:
  +
local y = 0
  +
local tag_match_add = false
  +
repeat
  +
y = y+1
  +
tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil)
  +
tag_match_add = (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
  +
until tag_match_stop or y == #current_tags
  +
  +
-- If there's a match then save that mod and other
  +
-- interesting information:
  +
if tag_match_add then
  +
  +
-- Assume that the mod is global then go through
  +
-- all the stat ids and check if any of the
  +
-- stats are local:
  +
local mod_scope = 'Global'
  +
for _, vv in ipairs(results[id]) do
  +
if vv['mod_stats.id']:find('.*local.*') ~= nil then
  +
mod_scope = 'Local'
  +
end
  +
end
  +
  +
-- Save the matching modifier tag:
  +
local a = #item_mods[sctn['header']]
  +
item_mods[sctn['header']][a+1] = results[id][j]
  +
  +
-- Save other interesting fields:
  +
item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
  +
item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j
  +
item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
  +
item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
  +
  +
-- Count the mod groups:
  +
local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
  +
for _, header in ipairs({sctn['header'], 'all'}) do
  +
if mod_group_counter[header][adj][group] == nil then
  +
mod_group_counter[header][adj][group] = {}
  +
end
  +
local tp = results[id][j]['mods.mod_type']
  +
local bef = mod_group_counter[header][adj][group][tp] or 0
  +
mod_group_counter[header][adj][group][tp] = 1 + bef
  +
end
  +
end
  +
until tag_match_stop
  +
end
  +
  +
-- If the user wants to see the spawn chance then do the
  +
-- calculations and save that result as well:
  +
if tpl_args.spawn_chance ~= nil then
  +
extra_fields[#extra_fields+1] = 'spawn_weights.chance'
  +
item_mods[sctn['header']] = h.get_spawn_chance{
  +
tbl = item_mods[sctn['header']],
  +
chance_multiplier = sctn['chance_multiplier']
  +
}
  +
end
  +
  +
extra_fieldss[sctn['header']] = extra_fields
  +
end
  +
end
  +
end
  +
  +
  +
--
  +
-- Display the item mods
  +
--
  +
 
-- Introductory text:
 
-- Introductory text:
 
local out = {}
 
local out = {}
 
out[#out+1] = string.format(
 
out[#out+1] = string.format(
'==%s== \n',
+
'==%s== \n',
tpl_args['header'] or table.concat(item_tags, ', ')
+
tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
 
)
 
)
out[#out+1] = string.format(
+
local expand_button = string.format(
 
'<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
 
'<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
 
i18n.drop_down_table.collapse_all,
 
i18n.drop_down_table.collapse_all,
 
i18n.drop_down_table.expand_all
 
i18n.drop_down_table.expand_all
 
)
 
)
out[#out+1] = string.format('%s %s.<br><br><br>',
+
out[#out+1] = expand_button
  +
out[#out+1] = string.format('%s %s.<br><br><br>',
i18n.drop_down_table.table_intro,
 
  +
i18n.drop_down_table.table_intro,
 
f_item_link{
 
f_item_link{
page=get_item_tags['items._pageName'],
+
page=item_info['items._pageName'],
name=get_item_tags['items.name'],
+
name=item_info['items.name'],
inventory_icon=get_item_tags['items.inventory_icon'] or '',
+
inventory_icon=item_info['items.inventory_icon'] or '',
html=get_item_tags['items.html'] or '',
+
html=item_info['items.html'] or '',
 
skip_query=true
 
skip_query=true
 
}
 
}
 
)
 
)
  +
 
  +
-- Loop through the sections:
local item_mods = {}
 
local tableIndex = -1
 
 
for _, sctn in ipairs(section) do
 
for _, sctn in ipairs(section) do
  +
local extra_fields = extra_fieldss[sctn['header']]
  +
local adj = 'explicit'
  +
if sctn['is_implicit'] then
  +
adj = 'implicit'
  +
end
  +
  +
-- Create html container:
 
local container = mw.html.create('div')
 
local container = mw.html.create('div')
 
:attr('style', 'vertical-align:top; display:inline-block;')
 
:attr('style', 'vertical-align:top; display:inline-block;')
 
-- Format the where condition:
 
local generation_type = sctn['generation_type']
 
local where = {}
 
for _, item_tag in ipairs(item_tags) do
 
where[#where+1] = string.format(
 
'(spawn_weights.tag="%s" AND mods.generation_type=%s AND mods.domain=%s)',
 
item_tag,
 
sctn['generation_type'],
 
get_item_tags['items.domain']
 
)
 
end
 
 
tpl_args.tables = 'mods, spawn_weights, mod_stats'
 
tpl_args.fields = 'mods.name, mods.id, mods.required_level, mods.generation_type, mods.domain, mods.mod_group, mods.mod_type, mods.stat_text, mods._pageName, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal, spawn_weights._pageName'
 
tpl_args.q_join = 'mods._pageName=spawn_weights._pageName, mods._pageName=mod_stats._pageName'
 
tpl_args.q_where = table.concat(where, ' OR ')
 
tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
 
tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods._pageName, mods.required_level, spawn_weights.ordinal'
 
 
local extra_fields = {}
 
if tpl_args.extra_fields ~= nil then
 
extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
 
tpl_args.fields = string.format(
 
'%s, %s',
 
tpl_args.fields,
 
table.concat(extra_fields, ', ')
 
)
 
end
 
 
-- Query mods:
 
results = h.cargo_query(tpl_args)
 
 
-- Create own list for spawn weights and group by page name:
 
local spawn_weights = {}
 
local results_unique = {}
 
local hash = {}
 
for _,v in ipairs(results) do
 
if spawn_weights[v['mods._pageName']] == nil then
 
spawn_weights[v['mods._pageName']] = {}
 
end
 
local n = #spawn_weights[v['mods._pageName']] or 0
 
spawn_weights[v['mods._pageName']][n+1] = v
 
 
-- Get a sorted list that only has unique page names:
 
if hash[v['mods._pageName']] ~= true then
 
results_unique[#results_unique+1] = v
 
hash[v['mods._pageName']] = true
 
end
 
end
 
 
if #results_unique > 0 then
 
item_mods[generation_type] = {}
 
 
-- Loop through all the modifiers from the concept pages:
 
local last
 
for _, v in ipairs(results_unique) do
 
local pagename = v['spawn_weights._pageName']
 
 
-- Loop through all the modifier tags until they match
 
-- the item tags:
 
local j = 0
 
local tag_match_stop
 
repeat
 
j = j+1
 
local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
 
local mod_tag_weight = tonumber(
 
spawn_weights[pagename][j]['spawn_weights.weight']
 
)
 
   
-- Loop through the item tags until it matches the
+
-- Create the drop down table with <table></table>:
  +
local headers = container
-- spawn weight tag and the mod tag has a value larger than
 
-- zero:
+
if #item_mods[sctn['header']] > 0 then
local y = 0
 
local tag_match_add = false
 
repeat
 
y = y+1
 
tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
 
tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
 
until tag_match_stop or y == #item_tags
 
 
-- If there's a match then save that mod and other
 
-- interesting information:
 
if tag_match_add then
 
 
-- Assume that the mod is global then go through
 
-- all the stat ids and check if any of the
 
-- stats are local:
 
local mod_scope = 'Global'
 
for _, vv in ipairs(spawn_weights[pagename]) do
 
if vv['mod_stats.id']:find('.*local.*') ~= nil then
 
mod_scope = 'Local'
 
end
 
end
 
 
-- Save the matching modifier tag:
 
local a = #item_mods[generation_type]
 
item_mods[generation_type][a+1] = spawn_weights[pagename][j]
 
 
-- Save other interesting fields:
 
item_mods[generation_type][a+1]['mods.scope'] = mod_scope
 
item_mods[generation_type][a+1]['spawn_weight.idx_match'] = j
 
item_mods[generation_type][a+1]['mods.add'] = tag_match_add
 
item_mods[generation_type][a+1]['mods.stop'] = tag_match_stop
 
end
 
until tag_match_stop
 
end
 
 
-- If the user wants to see the spawn chance then do the
 
-- calculations and save that result as well:
 
if tpl_args.spawn_chance ~= nil then
 
extra_fields[#extra_fields+1] = 'spawn_weights.chance'
 
item_mods[generation_type] = h.get_spawn_chance{
 
tbl = item_mods[generation_type],
 
chance_multiplier = sctn['chance_multiplier']
 
}
 
end
 
 
-- Create the drop down table with <table></table>:
 
local headers = container
 
 
headers
 
headers
 
:tag('h3')
 
:tag('h3')
:wikitext(string.format(
+
:wikitext(string.format('%s', sctn['header']))
'%s',
 
sctn['header']
 
)
 
)
 
 
:done()
 
:done()
 
:done()
 
:done()
+
end
  +
-- Loop through and add all matching mods to the <table>.
 
local tbl, last
+
local total_mod_groups = 0
for _, rows in ipairs(item_mods[generation_type]) do
+
for _ in pairs(mod_group_counter[sctn['header']][adj]) do
+
total_mod_groups = 1+total_mod_groups
  +
end
-- If the last mod group is different to the current
 
  +
-- mod group then assume the mod isn't related and start
 
-- a new drop down list:
+
-- Loop through and add all matching mods to the <table>.
  +
local tbl, last_group, last_type
if rows['mods.mod_group'] ~= last then
 
+
for _, rows in ipairs(item_mods[sctn['header']]) do
  +
-- Check through all the mods and see if there are
 
-- multiple mod types within the same mod group:
+
-- If the last mod group is different to the current
local count = {}
+
-- mod group then assume the mod isn't related and start
for _, n in ipairs(item_mods[generation_type]) do
+
-- a new drop down list, if there's only one mod group
+
-- then use mod type instead:
  +
if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) then
-- If the mod has the same mod group, then add
 
-- the mod type to the counter. Only unique mod
+
-- Check through all the mods and see if there are
-- types matter so the number is just a dummy
+
-- multiple mod types within the same mod group:
-- value:
+
local count = {}
if n['mods.mod_group'] == rows['mods.mod_group'] then
+
for _, n in ipairs(item_mods[sctn['header']]) do
  +
count[n['mods.mod_type']] = 1
 
end
+
-- If the mod has the same mod group, then add
  +
-- the mod type to the counter. Only unique mod
  +
-- types matter so the number is just a dummy
  +
-- value:
  +
if n['mods.mod_group'] == rows['mods.mod_group'] then
  +
count[n['mods.mod_type']] = 1
 
end
 
end
+
end
  +
-- Calculate how many unique mod types with the
 
-- same mod group there are:
+
-- Calculate how many unique mod types with the
number_of_mod_types = 0
+
-- same mod group there are for all explicit or implicit
for _ in pairs(count) do
+
-- sections since a mod with the same mod group can't
number_of_mod_types = number_of_mod_types + 1
+
-- spawn. Doesn't matter if it's prefix or suffix.
end
+
local number_of_mod_types = 0
+
for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
-- If there are multiple unique mod types with the
+
number_of_mod_types = 1 + number_of_mod_types
-- same mod group then change the style of the drop
+
end
  +
-- down list to indicate it:
 
if number_of_mod_types > 1 then
+
-- If there are multiple unique mod types with the
tbl_caption = string.format(
+
-- same mod group then change the style of the drop
'%s',
+
-- down list to indicate it, if there's only one
m_util.html.poe_color(
+
-- mod group in the generation type then ignore it:
'mod',
+
local table_index_mod_group
'Mod group: ' .. rows['mods.mod_group']
+
if number_of_mod_types > 1 and total_mod_groups > 1 then
  +
table_index_mod_group = table.concat(
  +
{string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
  +
''
  +
)
  +
  +
tbl_caption = string.format(
  +
'%s',
  +
m_util.html.poe_color(
  +
'stat',
  +
string.format(
  +
'%s %s',
  +
i18n.drop_down_table.mod_group,
  +
rows['mods.mod_group']
 
)
 
)
  +
) or ''
  +
)
  +
  +
else
  +
tbl_caption = string.format(
  +
'%s (%s)',
  +
m_util.html.poe_color(
  +
'mod',
  +
h.header(rows['mods.stat_text_raw'])
  +
) or '',
  +
rows['mods.scope']
  +
)
  +
end
  +
  +
-- Create a table index for handling the collapsible:
  +
table_index_base = table_index_base+1
  +
if table_index_mod_group ~= nil then
  +
table_index = table_index_mod_group
  +
else
  +
table_index = table_index_base
  +
end
  +
  +
-- Add class and style to the <table>:
  +
tbl = container:tag('table')
  +
tbl
  +
:attr('class', 'mw-collapsible mw-collapsed')
  +
:attr('style',
  +
'text-align:left; line-height:1.60em; width:810px;'
  +
)
  +
:tag('th')
  +
:attr('class',
  +
string.format('mw-customtoggle-%s', table_index)
 
)
 
)
else
+
:attr('style',
tbl_caption = string.format(
+
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
'%s (%s)',
 
m_util.html.poe_color(
 
'mod',
 
h.header(rows['mods.stat_text'])
 
),
 
rows['mods.scope']
 
 
)
 
)
end
+
:attr('colspan', '3' .. #extra_fields)
+
:wikitext(tbl_caption)
-- Add class and style to the <table>:
+
:done()
tableIndex = tableIndex+1
+
:done()
tbl = container:tag('table')
+
end
  +
tbl
 
:attr('class', 'mw-collapsible mw-collapsed')
+
-- If the mod has no name then use the mod type:
:attr('style',
+
local mod_name = rows['mods.name'] or ''
'text-align:left; line-height:1.60em; width:810px;'
+
if mod_name == '' then
)
+
mod_name = rows['mods.mod_type']
:tag('th')
+
end
  +
:attr('class',
 
string.format(
+
-- Check if there are any extra properties to show in
'mw-customtoggle-%s',
+
-- the drop down list and then add a cell for that,
tableIndex
+
-- add this node at the end of the table row:
)
+
local td = mw.html.create('td')
  +
if extra_fields ~= nil then
  +
for _, extra_field in ipairs(extra_fields) do
  +
td
  +
:attr('width', '*')
  +
:wikitext(string.format(
  +
'%s:&nbsp;%s ',
  +
extra_field,
  +
rows[extra_field] or ''
 
)
 
)
:attr('style',
+
)
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
+
:done()
)
 
:attr('colspan', '3' .. #extra_fields)
 
:wikitext(tbl_caption)
 
:done()
 
:done()
 
 
end
 
end
+
end
  +
-- If the mod has no name then use the mod id:
 
local mod_name = rows['mods.name']
+
-- Style mods.tags:
if mod_name == '' or mod_name == nil then
+
local mods_tags = table.concat(
mod_name = rows['mods.id']
+
m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}),
end
+
', '
+
)
-- Check if there are any extra properties to show in
+
if mods_tags ~= '' then
-- the drop down list and then add a cell for that,
+
mods_tags = m_util.html.tooltip('*', mods_tags, class)
-- add this node at the end of the table row:
+
end
  +
local td = mw.html.create('td')
 
if extra_fields ~= nil then
+
-- Add a table row with the interesting properties that
  +
-- modifier has:
for _, extra_field in ipairs(extra_fields) do
 
td
+
tbl
:attr('width', '*')
+
:tag('tr')
:wikitext(string.format(
+
:attr('class', 'mw-collapsible mw-collapsed')
'%s:&nbsp;%s ',
+
:attr(
extra_field,
+
'id',
rows[extra_field]
+
string.format('mw-customcollapsible-%s', table_index)
)
+
)
  +
:tag('td')
  +
:attr('width', '160')
  +
:wikitext(
  +
string.format(
  +
'&nbsp;&nbsp;&nbsp;[[%s|%s]]',
  +
rows['mods._pageName'],
  +
mod_name:gsub('%s', '&nbsp;')
 
)
 
)
:done()
+
)
end
+
:done()
end
+
:tag('td')
+
:attr('width', '1')
-- Add a table row with the interesting properties that
+
:wikitext(
-- modifier has:
 
tbl
 
:tag('tr')
 
:attr('class', 'mw-collapsible mw-collapsed')
 
:attr(
 
'id',
 
 
string.format(
 
string.format(
'mw-customcollapsible-%s',
+
'%s&nbsp;%s',
tableIndex
+
m_game.level_requirement['short_upper']
  +
:gsub('%s', '&nbsp;'),
  +
rows['mods.required_level']
 
)
 
)
 
)
 
)
:tag('td')
+
:done()
:attr('width', '160')
+
:tag('td')
:wikitext(
+
:attr('width', '*')
string.format(
+
:wikitext(
'&nbsp;&nbsp;&nbsp;[[%s|%s]]',
+
string.format(
rows['mods._pageName'],
+
'%s%s',
mod_name:gsub('%s', '&nbsp;')
+
m_util.html.poe_color(
)
+
'mod',
  +
rows['mods.stat_text']
  +
:gsub('<br>', ', ')
  +
:gsub('<br />', ' ')
  +
) or '',
  +
mods_tags
 
)
 
)
:done()
+
)
:tag('td')
 
:attr('width', '1')
 
:wikitext(
 
string.format(
 
'%s&nbsp;%s',
 
m_game.level_requirement['short_upper']:gsub('%s', '&nbsp;'),
 
rows['mods.required_level']
 
)
 
)
 
:done()
 
:tag('td')
 
:attr('width', '*')
 
:wikitext(
 
string.format(
 
'%s',
 
m_util.html.poe_color(
 
'mod',
 
rows['mods.stat_text']:gsub('<br>', ', ')
 
)
 
)
 
)
 
:done()
 
:node(td)
 
 
:done()
 
:done()
  +
:node(td)
 
:done()
 
:done()
+
:done()
  +
-- Save the last mod group for later comparison:
 
last = rows['mods.mod_group']
+
-- Save the last mod group for later comparison:
end
+
last_group = rows['mods.mod_group']
  +
last_type = rows['mods.mod_type']
 
end
 
end
 
 
out[#out+1] = tostring(container)
 
out[#out+1] = tostring(container)
 
end
 
end
  +
 
  +
-- Outro text:
  +
out[#out+1] = '<br>'
  +
out[#out+1] = m_util.html.poe_color(
  +
'normal',
  +
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
  +
)
  +
 
return table.concat(out,'')
 
return table.concat(out,'')
 
end
 
end
 
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
Line 1,353: Line 1,949:
 
keys = {}
 
keys = {}
 
for _, data in ipairs(mod_table.data) do
 
for _, data in ipairs(mod_table.data) do
if type(data.arg) == 'string' then
+
if type(data.arg) == 'string' then
 
keys[data.arg] = 1
 
keys[data.arg] = 1
 
elseif type(data.arg) == 'table' then
 
elseif type(data.arg) == 'table' then
Line 1,361: Line 1,957:
 
end
 
end
 
end
 
end
  +
 
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
 
keys[key] = 1
 
keys[key] = 1
 
end
 
end
  +
 
 
local out = {}
 
local out = {}
 
for key, _ in pairs(keys) do
 
for key, _ in pairs(keys) do
 
out[#out+1] = string.format("['%s'] = '1'", key)
 
out[#out+1] = string.format("['%s'] = '1'", key)
 
end
 
end
  +
 
 
return table.concat(out, ', ')
 
return table.concat(out, ', ')
 
end
 
end

Revision as of 18:39, 7 December 2020

Template info icon Module documentation[view] [edit] [history] [purge]

Module implementing {{Modifier table}} with Cargo support.

--[[
Module responsible for displaying modifiers in various ways.

]]

local m_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link
local m_cargo = require('Module:Cargo')

local cargo = mw.ext.cargo

local p = {}

-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------
-- This section contains strings used by this module.
-- Add new strings here instead of in-code directly, this will help other
-- people to correct spelling mistakes easier and help with translation to
-- other PoE wikis.

local i18n = {
    mod_table = {
        name = m_util.html.abbr('Name', 'Name of the modifier if available or its internal identifier instead'),
        mod_group = m_util.html.abbr('Group', 'Only one modifier from the specified group can appear at a time under normal circumstances'),
        mod_type = 'Type',
        domain = '[[Modifiers#Mod_Domain|Domain]]',
        generation_type = '[[Modifiers#Mod_Generation_Type|Generation Type]]',
        required_level = '[[Image:Level_up_icon_small.png|link=|For generated item/monster modifiers the minimum item/monster level respectively. Some generation types may not require this condition to be met, however item level restrictions may be raised to 80% of this value.]]',
        labyrinth = '[[The Lord\'s Labyrinth|Labyrinth]]',
        stat_text = m_util.html.abbr('Stats', 'Stats of the modifier and the range they can roll in (if applicable)'),
        buff = m_util.html.abbr('Buff', 'ID of the buff granted and the values associated'),
        granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
        tags = '[[Modifiers#Tags|Tags]]',

        iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
        iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
        pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),

        spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
        generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),

        normal_labyrinth = 'Normal Labyrinth',
        cruel_labyrinth = 'Cruel Labyrinth',
        merciless_labyrinth = 'Merciless Labyrinth',
        eternal_labyrinth = 'Eternal Labyrinth',
    },

    drop_down_table = {
        collapse_all = 'Collapse all',
        expand_all = 'Expand all',
        table_intro = 'The table below displays the available [[modifiers]] for [[item]]s such as',
        prefix = 'Prefix',
        suffix = 'Suffix',
        corrupted = 'Corrupted',
        enchant = 'Enchantment',
        elder_prefix = 'Elder prefix',
        elder_suffix = 'Elder suffix',
        shaper_prefix = 'Shaper prefix',
        shaper_suffix = 'Shaper suffix',
        delve_prefix = 'Delve prefix',
        delve_suffix = 'Delve suffix',
        crusader_prefix = 'Crusader prefix',
        crusader_suffix = 'Crusader suffix',
        eyrie_prefix = 'Redeemer prefix',
        eyrie_suffix = 'Redeemer suffix',
        basilisk_prefix = 'Hunter prefix',
        basilisk_suffix = 'Hunter suffix',
        adjudicator_prefix = 'Warlord prefix',
        adjudicator_suffix = 'Warlord suffix',
        veiled_prefix = 'Veiled prefix',
        veiled_suffix = 'Veiled suffix',
        mod_group = 'Mod group:',
        back_to_top = 'Back to top',
    },

    errors = {
        --
        -- Mod template
        --
        sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
        sell_price_missing_argument = 'Both %s and %s must be specified',

        --
        -- Modifier link template
        --
        undefined_statid = 'Please define any of these stat ids: %s',
        incorrect_modid = 'Please change the name from "%s" to any of these modifier ids:<br>%s',
        multiple_results = 'Please choose only one of these modifier ids:<br>%s',
        no_results = 'No results found.',
    },
}


--
-- Helper/Utility functions
--

local h = {}

function h.header(str)
    --[[
    This function replaces specific numbers with a generic #.
    ]]

    local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
    s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
    s = table.concat(m_util.string.split(s, '<br>'), ', ')
    s = table.concat(m_util.string.split(s, '<br />'), ' ')

   return s
end

function h.query_weights(table_name, page_ids)
    return m_cargo.map_results_to_id{
        results=m_cargo.query(
            {'mods', table_name},
            {
                'mods._pageID',
                table_name .. '.tag',
                table_name .. '.weight'
            },
            {
                where=page_ids,
                join=string.format('mods._pageID=%s._pageID', table_name),
                orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
            }
        ),
        field='mods._pageID',
    }
end

function h.disambiguate_mod_name(results)
    --[[
        Disambiguates results from a mods query.
    ]]
    local str = {}
    for i,v in pairs(results) do
        str[#str+1] = string.format(
            '%s - %s ([[%s|page]])',
            v['mods.id'] or v['mods._pageName'] or '',
            string.gsub(
                v['mods.stat_text_raw'] or 'N/A',
                '<br>',
                ', '
            ) or '',
            v['mods._pageName'] or ''
        )
    end
    return table.concat(str, '<br>')
end

h.tbl = {}
h.tbl.display = {}
h.tbl.display.factory = {}
function h.tbl.display.factory.value(args)
    -- Format options for each field:
    args.options = args.options or {}

    -- Separator between fields:
    args.delimiter = args.delimiter or ', '

    return function(tpl_args, frame, tr, data, fields)
        local values = {}
        local fmt_values = {}

        for index, field in ipairs(fields) do
            local value = {
                min=data[field],
                max=data[field],
                base=data[field],
            }
            if value.min then
                values[#values+1] = value.max
                local opts = args.options[index] or {}
                -- Global colour is set, no overrides.
                if args.color ~= nil or opts.color == nil then
                    opts.no_color = true
                end
                fmt_values[#fmt_values+1] = m_util.html.format_value(nil, nil, value, opts)
            end
        end

        if #values == 0 then
            tr
                :wikitext(m_util.html.td.na())
        else
            local td = tr:tag('td')
            td
                :attr('data-sort-value', table.concat(values, args.delimiter))
                :wikitext(table.concat(fmt_values, args.delimiter))
            if args.color then
                td:attr('class', 'tc -' .. args.color)
            end
        end
    end
end

-- ----------------------------------------------------------------------------
-- Template: Mod table
-- ----------------------------------------------------------------------------

local mod_table = {}
mod_table.data = {
    {
        arg = 'name',
        header = i18n.mod_table.name,
        fields = {'mods._pageName', 'mods.id', 'mods.name'},
        options = {
            [3] = {
                optional=true,
           },
        },
        display = function(tpl_args, frame, tr, data, fields)
            local name
            if data['mods.name'] then
                name = data['mods.name']
            else
                name = data['mods.id']
            end

            tr
                :tag('td')
                    :wikitext(string.format('[[%s|%s]]', data['mods._pageName'], name))
        end,
        order = 1000,
        sort_type = 'text',
    },
    {
        arg = 'domain',
        header = i18n.mod_table.domain,
        fields = {'mods.domain'},
        display = function(tpl_args, frame, tr, data, fields)
            local k = 'mods.domain'
            local i = tonumber(data[k])
            data[k] = m_game.constants.mod.domains[i]['short_upper']
            h.tbl.display.factory.value({})(tpl_args, frame, tr, data, fields)
        end,
        order = 2000,
        sort_type = 'text',
    },
    {
        arg = 'generation_type',
        header = i18n.mod_table.generation_type,
        fields = {'mods.generation_type'},
        display = function(tpl_args, frame, tr, data, fields)
            local k = 'mods.generation_type'
            local i = tonumber(data[k])
            data[k] = m_game.constants.mod.generation_types[i]['short_upper']
            h.tbl.display.factory.value{}(tpl_args, frame, tr, data, fields)
        end,
        order = 2001,
        sort_type = 'text',
    },
    {
        arg = {'group', 'mod_group'},
        header = i18n.mod_table.mod_group,
        fields = {'mods.mod_group'},
        display = h.tbl.display.factory.value{},
        order = 2002,
        sort_type = 'text',
    },
    {
        arg = {'mod_type'},
        header = i18n.mod_table.mod_type,
        fields = {'mods.mod_type'},
        display = h.tbl.display.factory.value{},
        order = 2003,
        sort_type = 'text',
    },
    {
        arg = {'level', 'required_level'},
        header = i18n.mod_table.required_level,
        fields = {'mods.required_level'},
        display = h.tbl.display.factory.value{},
        order = 2004,
    },
    {
        arg = {'enchantment', 'labyrinth'},
        header = i18n.mod_table.labyrinth,
        fields = {string.format([[
                CONCAT(
                    CASE mods.required_level
                        WHEN 32 THEN "%s"
                        WHEN 53 THEN "%s"
                        WHEN 66 THEN "%s"
                        WHEN 75 THEN "%s"
                    END
                )=labyrinth_text]],
            i18n.mod_table.normal_labyrinth,
            i18n.mod_table.cruel_labyrinth,
            i18n.mod_table.merciless_labyrinth,
            i18n.mod_table.eternal_labyrinth)
        },
        display = h.tbl.display.factory.value{},
        order = 2005,
        sort_type = 'text',
    },
    {
        arg = {'stat_text'},
        header = i18n.mod_table.stat_text,
        fields = {'mods.stat_text'},
        display = function(tpl_args, frame, tr, data, fields)
            local value
            -- map display type shows this in another column, remove this text to avoid clogging up the list
            if tpl_args.type == 'map' then
                local texts = m_util.string.split(data['mods.stat_text'], '<br>')
                local out = {}

                local valid
                for _, v in ipairs(texts) do
                    valid = true
                    for _, data in pairs(mod_table.stat_ids) do
                        if string.find(v, data.pattern) ~= nil then
                            valid = false
                            break
                        end
                    end

                    if valid then
                        table.insert(out, v)
                    end
                end

                value = table.concat(out, '<br>')
            else
                value = data['mods.stat_text']
            end
            data['mods.stat_text'] = value
            h.tbl.display.factory.value{color='mod'}(tpl_args, frame, tr, data, fields)
        end,
        order = 3000,
        sort_type = 'text',
    },
    {
        arg = 'buff',
        header = i18n.mod_table.buff,
        fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
        display = h.tbl.display.factory.value{delimiter=' '},
        order = 4000,
        sort_type = 'text',
    },
    {
        arg = {'skill', 'granted_skill'},
        header = i18n.mod_table.granted_skill,
        fields = {'mods.granted_skill'},
        display = h.tbl.display.factory.value{},
        order = 4001,
        sort_type = 'text',
    },
    {
        arg = {'tags'},
        header = i18n.mod_table.tags,
        fields = {'mods.tags'},
        display = function(tpl_args, frame, tr, data, fields)
            local k = 'mods.tags'
            data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ')
            h.tbl.display.factory.value{}(tpl_args, frame, tr, data, fields)
        end,
        order = 5000,
        sort_type = 'text',
    },
}
mod_table.stat_ids = {
    ['map_item_drop_quantity_+%'] = {
        header = i18n.mod_table.iiq,
        pattern = '%d+%% increased Quantity of Items found in this Area',
    },
    ['map_item_drop_rarity_+%'] = {
        header = i18n.mod_table.iir,
        pattern = '%d+%% increased Rarity of Items found in this Area',
    },
    ['map_pack_size_+%'] = {
        header = i18n.mod_table.pack_size,
        pattern = '%+%d+%% Monster pack size',
    }
}
mod_table.weights = {'spawn_weights', 'generation_weights'}

function p.mod_table(frame)
    --[[
    Creates a generic table for modifiers.

    Examples
    --------
    = p.mod_table{
        q_tables='spawn_weights',
        q_join='mods._pageID=spawn_weights._pageID',
        q_where='mods.generation_type = 10 AND spawn_weights.tag = "boots" AND spawn_weights.weight > 0',
        q_orderBy='mods.id, mods.required_level',
        q_limit=100,
        stat_text=1,
        enchantment=1,
    }

    ]]


    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)

    -- default to enabled
    tpl_args.name = tpl_args.name or true

    for _, key in ipairs(mod_table.weights) do
        tpl_args[key] = m_util.cast.boolean(tpl_args[key])
    end

    if string.find(tpl_args.q_where, '%[%[') ~= nil then
        error('SMW leftover in where clause')
    end

    local row_infos = {}
    for _, row_info in ipairs(mod_table.data) do
        local enabled = false
        if row_info.arg == nil then
            enabled = true
        elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
            enabled = true
        elseif type(row_info.arg) == 'table' then
            for _, argument in ipairs(row_info.arg) do
                if m_util.cast.boolean(tpl_args[argument]) then
                    enabled = true
                    break
                end
            end
        end

        if enabled then
            row_info.options = row_info.options or {}
            row_infos[#row_infos+1] = row_info
        end
    end

    -- sort the rows
    table.sort(row_infos, function (a, b)
        return (a.order or 0) < (b.order or 0)
    end)

    -- Set required and extra tables:
    local tables = {'mods'}
    for _, v in ipairs(m_util.string.split_args(tpl_args.q_tables, {',%s*'})) do
        tables[#tables+1] = v
    end

    -- Set required and extra fields:
    local fields = {
        'mods._pageID',
    }
    for _, row_info in ipairs(row_infos) do
        if type(row_info.fields) == 'function' then
            row_info.fields = row_info.fields()
        end
        for index, field in ipairs(row_info.fields) do
            row_info.options[index] = row_info.options[index] or {}
            fields[#fields+1] = field
        end
    end
    tpl_args._extra_fields = m_util.string.split_args(tpl_args.q_fields, {',%s*'})
    for _, v in ipairs(tpl_args._extra_fields) do
        fields[#fields+1] = v
    end

    -- Parse query arguments:
    local query = {
        -- Workaround: fix duplicates
        groupBy='mods._pageID',
    }
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end

    local results = m_cargo.query(tables, fields, query)

    if #results == 0 then
        if tpl_args.default ~= nil then
            return tpl_args.default
        else
            return 'No results found'
        end
    end

    -- this might be needed in other queries, currently not checking if
    -- it's actually needed because performance impact should be neglible
    local page_ids = {}
    for _, row in ipairs(results) do
        page_ids[#page_ids+1] = string.format(
            'mods._pageID="%s"',
            row['mods._pageID']
        )
    end
    page_ids = table.concat(page_ids, ' OR ')

    local weights = {}
    for _, key in ipairs(mod_table.weights) do
        if tpl_args[key] then
            weights[key] = h.query_weights(key, page_ids)
        end
    end

    local stats
    if tpl_args.type == 'map' then
        local query_stat_ids = {}
        for k, _ in pairs(mod_table.stat_ids) do
            query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
        end

        stats = m_cargo.map_results_to_id{
            results=m_cargo.query(
                {'mods', 'mod_stats'},
                {
                    'mods._pageID',
                    'mod_stats.id',
                    'mod_stats.min',
                    'mod_stats.max',
                },
                {
                    where=string.format(
                        '(%s) AND (%s)',
                        page_ids,
                        table.concat(query_stat_ids, ' OR ')
                    ),
                    join='mods._pageID=mod_stats._pageID',
                    orderBy='mods.id ASC',
                }
            ),
            field='mods._pageID'
        }

        -- In addition map stats to stat <-> min/max pairs
        for page_id, rows in pairs(stats) do
            local stat_id_map = {}
            for _, row in ipairs(rows) do
                stat_id_map[row['mod_stats.id']] = {
                    min=tonumber(row['mod_stats.min']),
                    max=tonumber(row['mod_stats.max'])
                }
            end
            stats[page_id] = stat_id_map
        end
    end

    --
    -- Display
    --

    -- Preformance optimization
    for index, field in ipairs(tpl_args._extra_fields) do
        field = m_util.string.split(field, '%s*=%s*')
        -- field[2] will be nil if there is no alias
        tpl_args._extra_fields[index] = field[2] or field[1]
    end

    local tbl = mw.html.create('table')
    tbl:attr('class', 'wikitable sortable modifier-table')
    -- Header

    local tr = tbl:tag('tr')
    local display_fields = {}
    for i, row_info in ipairs(row_infos) do
        for j, field in ipairs(row_info.fields) do
            -- Aliased name is used as keys in the results:
            field = m_util.string.split(field, '%s*=%s*')
            field = field[2] or field[1]

            -- Make a new field table since mod_table.data will remain
            -- modified the 2nd time a function is run and then crash.
            if j == 1 then
                display_fields[i] = {}
            end
            display_fields[i][j] = field
        end

        tr
            :tag('th')
                :attr('data-sort-type', row_info.sort_type or 'number')
                :wikitext(row_info.header)
                :done()
    end

    if tpl_args.type == 'map' then
        for stat_id, data in pairs(mod_table.stat_ids) do
            tr
                :tag('th')
                    :attr('data-sort-type', 'number')
                    :wikitext(data.header)
                    :done()
        end
    end

    for _, key in ipairs(mod_table.weights) do
        if tpl_args[key] then
            tr
                :tag('th')
                    :wikitext(i18n.mod_table[key])
        end
    end

    for _, field in ipairs(tpl_args._extra_fields) do
        tr
            :tag('th')
                :wikitext(field)
    end

    -- Body

    for _, row in ipairs(results) do
        tr = tbl:tag('tr')

        for i, row_info in ipairs(row_infos) do
            -- this has been cast from a function in an earlier step
            local display = true
            for j, field in ipairs(display_fields[i]) do
                -- this will bet set to an empty value not nil confusingly
                if row[field] == nil or row[field] == '' then
                    if row_info.options[j].optional ~= true then
                        display = false
                        break
                    else
                        row[field] = nil
                    end
                end
            end
            if display then
                row_info.display(tpl_args, frame, tr, row, display_fields[i])
            else
                tr:wikitext(m_util.html.td.na())
            end
        end

        if tpl_args.type == 'map' then
            for stat_id, data in pairs(mod_table.stat_ids) do
                local stat_data = stats[row['mods._pageID']]
                if stat_data and stat_data[stat_id] then
                    local v = stat_data[stat_id]
                    local text
                    if v.min == v.max then
                        text = v.min
                    else
                        text = string.format('(%s to %s)', v.min, v.max)
                    end
                    tr
                        :tag('td')
                            :attr('data-sort-value', (v.min+v.max)/2)
                            :wikitext(string.format('%s%%', text))
                            :done()
                else
                    tr:wikitext(m_util.html.td.na())
                end
            end
        end

        for _, key in ipairs(mod_table.weights) do
            if tpl_args[key] then
                local weight_out = {}
                for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
                    if wrow[key .. '.tag'] and wrow[key .. '.weight'] then
                        weight_out[#weight_out+1] = string.format(
                            '%s %s',
                            wrow[key .. '.tag'],
                            wrow[key .. '.weight']
                        )
                    end
                end

                if #weight_out > 0 then
                    tr
                        :tag('td')
                            :wikitext(table.concat(weight_out, '<br>'))
                            :done()
                else
                    tr:wikitext(m_util.html.td.na())
                end
            end
        end

        for _, field in ipairs(tpl_args._extra_fields) do
            if row[field] then
                tr
                    :tag('td')
                        :wikitext(row[field])
            else
                tr:wikitext(m_util.html.td.na())
            end
        end
    end

    return tostring(tbl)
end




-- ---------------------------------------------------------------------
-- Modifier link
-- ---------------------------------------------------------------------


function p.modifier_link(frame)
    --[[
    Finds and links to a modifier in formatted form.

    To do list
    ----------
    * Standardize hoverbox so it can be used in multiple places easily
    and make it easy to add rows of data.

    Examples
    --------
    = p.modifier_link{"Tyrannical"}
    = p.modifier_link{"Flaring"}
    = p.modifier_link{"Dictator's"}
    = p.modifier_link{"StrDexMaster%"}
    = p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}

    ]]

    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)

    -- Aliases:
    tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''

    -- Query cargo rows:
    tpl_args.results = m_cargo.map_results_to_id{
        results=m_cargo.query(
            {'mods', 'mod_stats', 'spawn_weights'},
            {
                'mods.name',
                'mods.stat_text',
                'mods.stat_text_raw',
                'mods.generation_type',
                'mods.tags',
                'mods._pageName',
                'mod_stats.max',
                'mod_stats.min',
                'spawn_weights.tag',
                'spawn_weights.weight',
                'mods.id',
                'mod_stats.id'
            },
            {
                join = [[
                    mods._pageName=mod_stats._pageName,
                    mods._pageName=spawn_weights._pageName
                ]],
                where = string.format(
                    [[
                        (
                            mods.name LIKE "%s" or
                            mods.id LIKE "%s" or
                            mods.stat_text LIKE "%s" or
                            mods.stat_text_raw LIKE "%s"
                        )
                        AND mod_stats.id LIKE "%%%s%%"
                    ]],
                    tpl_args.modid,
                    tpl_args.modid,
                    tpl_args.modid,
                    tpl_args.modid,
                    tpl_args.statid or '%'
                ),
                -- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
            }
        ),
        field='mods._pageName',
        keep_id_field=true,
        append_id_field=true,
    }

    -- Get a sorted list that only has unique page names:
    tpl_args.results_unique = {{}}
    for i, v in ipairs(tpl_args.results) do
        tpl_args.results_unique[i] = tpl_args.results[v][1]
    end

    -- Helpful error handling:
    local err_tbl = {
        {
            bool = #tpl_args.results == 0,
            disp = {
                i18n.errors.no_results,
            }
        },
        {
            bool = #tpl_args.results_unique > 1,
            disp = {
                i18n.errors.multiple_results,
                h.disambiguate_mod_name(tpl_args.results_unique),
            },
        },
        {
            bool = tpl_args.modid ~= tpl_args.results_unique[1]['mods.id'],
            disp = {
                string.gsub(
                    i18n.errors.incorrect_modid,
                    '%%s',
                    tpl_args.modid,
                    1
                ),
                h.disambiguate_mod_name(tpl_args.results_unique),
            },
        },
    }
    for _, v in ipairs(err_tbl) do
        if v.bool then
            local cats = {'Pages with modifier link errors'}
            return m_util.html.error(
                {msg = string.format(v.disp[1], v.disp[2]) .. m_util.misc.add_category(cats)}
            )
        end
    end

    -- Display formats:
    local display = {
        abbr = {
            display = function(tpl_args, frame)
                local name = m_util.html.poe_color(
                    'mod',
                    string.format(
                        '[[%s|%s]]',
                        tpl_args.results_unique[1]['mods._pageName'],
                        tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                    )
                )

                local tooltip_table = {
                    m_util.html.poe_color(
                        'mod',
                        tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                    ),
                    m_util.html.poe_color(
                        'help',
                        m_game.constants.mod.generation_types[
                            tonumber(
                                tpl_args.results_unique[1]['mods.generation_type']
                            )
                        ].full
                    ),
                    -- m_util.html.poe_color(
                        -- 'help',
                       -- table.concat(
                            -- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
                            -- ', '
                        -- )
                    -- ),
                    m_util.html.poe_color(
                        'normal',
                        tpl_args.results_unique[1]['mods.stat_text_raw']
                    ),
                }

                local tt_tbl_fltrd = {}
                for _,v in ipairs(tooltip_table) do
                    if v ~= nil and v ~= '' then
                        tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
                    end
                end
                local tooltip = table.concat(tt_tbl_fltrd, '<br>')

                return m_util.html.tooltip(name, tooltip, class)
            end,
        },
        verbose = {
            display = function(tpl_args, frame)
                return string.format(
                    '%s - %s (%s)',
                    m_util.html.poe_color(
                        'mod',
                        string.format(
                            '[[%s|%s]]',
                            tpl_args.results_unique[1]['mods._pageName'],
                            tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                        )
                    ),
                    m_util.html.poe_color(
                        'mod',
                        string.gsub(
                            tpl_args.results_unique[1]['mods.stat_text'],
                            '<br>',
                            ', '
                        )
                    ),
                    m_game.constants.mod.generation_types[
                        tonumber(
                            tpl_args.results_unique[1]['mods.generation_type']
                        )
                    ].full
                )
            end,
        },
        stat_text = {
            display = function(tpl_args, frame)
                return m_util.html.poe_color(
                    'mod',
                    tpl_args.results_unique[1]['mods.stat_text']
                )
            end,
        },
        max = {
            display = function(tpl_args, frame)
                local statid = {}
                for _, v in ipairs(tpl_args.results[tpl_args.results_unique[1]['mods._pageName']]) do
                    statid[#statid+1] = v['mod_stats.id']
                    if tpl_args.statid == v['mod_stats.id'] then
                        return v['mod_stats.max']
                    end
                end

                if tpl_args.statid == nil then
                    return m_util.html.error(
                        {
                            msg = string.format(
                                i18n.errors.undefined_statid,
                                table.concat(statid, ', ')
                            )
                        }
                    )
                end
            end
        },
        min = {
            display = function(tpl_args, frame)
                local statid = {}
                for _, v in ipairs(tpl_args.results[tpl_args.results_unique[1]['mods._pageName']]) do
                    statid[#statid+1] = v['mod_stats.id']
                    if tpl_args.statid == v['mod_stats.id'] then
                        return v['mod_stats.min']
                    end
                end

                if tpl_args.statid == nil then
                    return m_util.html.error(
                        {
                            msg = string.format(
                                i18n.errors.undefined_statid,
                                table.concat(statid, ', ')
                            )
                        }
                    )
                end
            end
        },
    }

    return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
end




-- ---------------------------------------------------------------------
-- Drop down list
-- ---------------------------------------------------------------------

function h.get_spawn_chance(frame)
    --[[
    Calculates the spawn chance of a set of mods that all have a
    spawn weight.

    ]]

    -- Get template arguments:
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)

    -- Get the table:
    local tbl = tpl_args['tbl']

    -- Probabilities affecting the result besides the spawn weight:
    local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1

    -- Total number of outcomes.
    local N = 0
    for i,_ in ipairs(tbl) do
        N = N + tbl[i]['spawn_weights.weight']
    end

    for i,_ in ipairs(tbl) do
        -- Number of ways it can happen:
        local n = tbl[i]['spawn_weights.weight']

        -- Truncated value:
        tbl[i]['spawn_weights.chance'] = string.format(
            "%0.2f%%",
            n/N * chance_multiplier*100
        )
    end

    return tbl
end

function p.drop_down_table(frame)
    --[[
    This function queries mods that can spawn on an item. It compares
    the item tags and the spawn weight tags. If there's a match and
    the spawn weight is larger than zero, then that mod is added to a
    drop down list.

    To Do:
    * Add support to:
        * Forsaken masters
        * Bestiary
    * Add a proper expand/collapse toggle for the entire header row so
        it reacts together with mw-collapsible.
    * Show Mod group in a better way perhaps:
        Mod group [Collapsible, default=Expanded]
            # to Damage [Collapsible, default=Collapsed]
                3 to Damage
                5 to Damage
    * Add a where condition that somehow filters out mods that obviously
      wont match with the item. spawn_weights.weight>0 isn't enough due
      to possible edge cases.


    Examples:
    Weapons
    = p.drop_down_table{item='Rusted Hatchet', header='One Handed Axes'}
    = p.drop_down_table{item='Stone Axe', header='Two Handed Axes'}

    Accessories
    = p.drop_down_table{item='Amber Amulet', header='Amulets'}

    Jewels
    = p.drop_down_table{item='Cobalt Jewel', header='Jewels'}

    Armour
    = p.drop_down_table{item='Plate Vest', header='Body armours'}

    Boots
    = p.drop_down_table{item='Iron Greaves', header='Boots'}

    = p.drop_down_table{
        item='Fishing Rod',
        header='FISH PLEASE',
        item_tags='fishing_rod',
        extra_fields='mods.tags'
    }

    = p.drop_down_table{
        item='Fishing Rod',
        item_tags='axe, one_hand_weapon, onehand, weapon, default',
        extra_item_tags='fishing_rod'
    }

    = p.drop_down_table{
        item='Vaal Blade',
    }

    ]]

    -- Get template arguments:
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)

    -- Format the cargo query:
    local where
    for _,v in ipairs({
            {tpl_args.page, 'items._pageName = "%s"'},
            {tpl_args.item, 'items.name = "%s"'},
    }) do
        if v[1] ~= nil then
            where = string.format(v[2], v[1])
            break
        end
    end

    local item_info = m_cargo.query(
        {'items'},
        {
            'items.name',
            'items.tags',
            'items.class_id',
            'items.inventory_icon',
            'items.html',
            'items.release_version',
            'items._pageName'
        },
        {
            where=where,
            groupBy='items._pageName',
            orderBy='items.name, items.release_version DESC',
        }
    )[1]

    -- Set the item class as key and the corresponding mod domain as
    -- value:
    local class_to_domain = {
        ['LifeFlask']=2,
        ['ManaFlask']=2,
        ['HybridFlask']=2,
        ['UtilityFlask']=2,
        ['UtilityFlaskCritical']=2,
        ['Map']=5,
        ['Jewel']=10,
        ['Leaguestone']=12,
        ['AbyssJewel']=13,
    }
    -- Get the domain, if it's not defined in the table assume it's
    -- in the item domain.
    item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1

    -- Convert the mod domain number to understandable text:
    item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']

    -- Format item tags:
    tpl_args.item_tags = m_util.string.split_args(
        tpl_args.item_tags or item_info['items.tags'],
        {sep=',%s*'}
    )
    for _,v in ipairs(m_util.string.split_args(tpl_args.extra_item_tags, {sep=',%s*'})) do
        tpl_args.item_tags[#tpl_args.item_tags+1] = v
    end

    -- Format extra fields:
    local extra_fields = m_util.string.split_args(tpl_args.extra_fields, {sep=',%s*'})


    -- Get tags that are appended to special items:
    local elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
    local shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
    local crusader_tag = m_game.constants.item.classes[item_info['items.class_id']]['crusader_tag']
    local eyrie_tag = m_game.constants.item.classes[item_info['items.class_id']]['eyrie_tag']
    local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_tag']
    local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']

    -- Create drop down lists in these sections and query in these
    -- generation types.
    local section = {
        {
            header = i18n.drop_down_table.prefix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        1,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.suffix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        2,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {elder_tag},
            header = i18n.drop_down_table.elder_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    elder_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {elder_tag},
            header = i18n.drop_down_table.elder_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    elder_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {shaper_tag},
            header = i18n.drop_down_table.shaper_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    shaper_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {shaper_tag},
            header = i18n.drop_down_table.shaper_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    shaper_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.delve_prefix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        1,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.delve_suffix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        2,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {crusader_tag},
            header = i18n.drop_down_table.crusader_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    crusader_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {crusader_tag},
            header = i18n.drop_down_table.crusader_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    crusader_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {eyrie_tag},
            header = i18n.drop_down_table.eyrie_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    eyrie_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {eyrie_tag},
            header = i18n.drop_down_table.eyrie_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    eyrie_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {basilisk_tag},
            header = i18n.drop_down_table.basilisk_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    basilisk_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {basilisk_tag},
            header = i18n.drop_down_table.basilisk_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    basilisk_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {adjudicator_tag},
            header = i18n.drop_down_table.adjudicator_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    adjudicator_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {adjudicator_tag},
            header = i18n.drop_down_table.adjudicator_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    adjudicator_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.veiled_prefix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL
                            AND mods.id LIKE "%%Master%%")
                        ]],
                        item_tag,
                        1,
                        9
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.veiled_suffix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL
                            AND mods.id LIKE "%%Master%%")
                        ]],
                        item_tag,
                        2,
                        9
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.corrupted,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        5,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
            chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
            is_implicit = true,
        },
        {
            header = i18n.drop_down_table.enchant,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        10,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
            is_implicit = true,
        },
    }

    -- Save the original tag format:
    local item_tags_orig = {}
    for i,v in ipairs(tpl_args.item_tags) do
        item_tags_orig[i] = v
    end

    local item_mods = {}
    local mod_group_counter = {}
    mod_group_counter['all'] = {}
    local extra_fieldss = {}
    local table_index_base = -1
    for _, sctn in ipairs(section) do
        item_mods[sctn['header']] = {}

        -- Preallocate the mod group counter, implicit and explicit mods
        -- are counted separetely because they can spawn together:
        mod_group_counter[sctn['header']] = {}
        local adj = 'explicit'
        if sctn['is_implicit'] then
            adj = 'implicit'
        end
        for _, header in ipairs({sctn['header'], 'all'}) do
            if mod_group_counter[header][adj] == nil then
                mod_group_counter[header][adj] = {}
            end
        end

        local continue = true
        local current_tags
        if sctn['tags'] then
            -- some item classes do not have shaper/elder items, so the table
            -- will not contain any tags:
            if #sctn['tags'] == 0 then
                continue = false
            else
                current_tags = sctn['tags']
            end
        else
            current_tags = {}
            -- Reset to original tags:
            for i,v in ipairs(item_tags_orig) do
                current_tags[i] = v
            end
        end

        if continue then
            -- Cargo preparation:
            local q_fields = {
                'mods._pageName',
                'mods.name',
                'mods.id',
                'mods.required_level',
                'mods.generation_type',
                'mods.domain',
                'mods.mod_group',
                'mods.mod_type',
                'mods.stat_text',
                'mods.stat_text_raw',
                'mods.tags',
                'mod_stats.id',
                'spawn_weights.tag',
                'spawn_weights.weight',
                'spawn_weights.ordinal',
                'spawn_weights._pageName'
            }
            for i, v in ipairs(extra_fields) do
                q_fields[#q_fields+1] = v
            end

            -- Query mods and map the results to the pagename:
            local results = m_cargo.map_results_to_id{
                results=m_cargo.query(
                    {'mods', 'spawn_weights', 'mod_stats'},
                    q_fields,
                    {
                        join = [[
                            mods._pageName=spawn_weights._pageName,
                            mods._pageName=mod_stats._pageName
                        ]],
                        where = sctn['where'](tpl_args, frame, value),
                        groupBy = [[
                            mods._pageName,
                            spawn_weights.tag,
                            spawn_weights.weight
                        ]],
                        orderBy = [[
                            mods.generation_type,
                            mods.mod_group,
                            mods.mod_type,
                            mods.required_level,
                            mods._pageName,
                            spawn_weights.ordinal
                        ]],
                    }
                ),
                field='mods._pageName',
                keep_id_field=true,
                append_id_field=true,
            }

            if #results > 0 then
                -- Loop through all found modifiers:
                local last
                for _, id in ipairs(results) do
                    -- Loop through all the modifier tags until they match
                    -- the item tags:
                    local j = 0
                    local tag_match_stop
                    repeat
                        j = j+1
                        local mod_tag = results[id][j]['spawn_weights.tag']
                        local mod_tag_weight = tonumber(
                            results[id][j]['spawn_weights.weight']
                        )

                        -- Loop through the item tags until it matches the
                        -- spawn weight tag and the mod tag has a value larger than
                        -- zero:
                        local y = 0
                        local tag_match_add = false
                        repeat
                            y = y+1
                            tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil)
                            tag_match_add  =  (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
                        until tag_match_stop or y == #current_tags

                        -- If there's a match then save that mod and other
                        -- interesting information:
                        if tag_match_add then

                            -- Assume that the mod is global then go through
                            -- all the stat ids and check if any of the
                            -- stats are local:
                            local mod_scope = 'Global'
                            for _, vv in ipairs(results[id]) do
                                if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                    mod_scope = 'Local'
                                end
                            end

                            -- Save the matching modifier tag:
                            local a = #item_mods[sctn['header']]
                            item_mods[sctn['header']][a+1] = results[id][j]

                            -- Save other interesting fields:
                            item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
                            item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j
                            item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
                            item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop

                            -- Count the mod groups:
                            local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
                            for _, header in ipairs({sctn['header'], 'all'}) do
                                if mod_group_counter[header][adj][group] == nil then
                                    mod_group_counter[header][adj][group] = {}
                                end
                                local tp = results[id][j]['mods.mod_type']
                                local bef = mod_group_counter[header][adj][group][tp] or 0
                                mod_group_counter[header][adj][group][tp] = 1 + bef
                            end
                        end
                    until tag_match_stop
                end

                -- If the user wants to see the spawn chance then do the
                -- calculations and save that result as well:
                if tpl_args.spawn_chance ~= nil then
                    extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                    item_mods[sctn['header']] = h.get_spawn_chance{
                        tbl = item_mods[sctn['header']],
                        chance_multiplier = sctn['chance_multiplier']
                    }
                end

                extra_fieldss[sctn['header']] = extra_fields
            end
        end
    end


    --
    -- Display the item mods
    --

    -- Introductory text:
    local out = {}
    out[#out+1] = string.format(
        '==%s== \n',
        tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
    )
    local expand_button = string.format(
        '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
        i18n.drop_down_table.collapse_all,
        i18n.drop_down_table.expand_all
    )
    out[#out+1] = expand_button
    out[#out+1] = string.format('%s %s.<br><br><br>',
        i18n.drop_down_table.table_intro,
        f_item_link{
            page=item_info['items._pageName'],
            name=item_info['items.name'],
            inventory_icon=item_info['items.inventory_icon'] or '',
            html=item_info['items.html'] or '',
            skip_query=true
        }
    )

    -- Loop through the sections:
    for _, sctn in ipairs(section) do
        local extra_fields = extra_fieldss[sctn['header']]
        local adj = 'explicit'
        if sctn['is_implicit'] then
            adj = 'implicit'
        end

        -- Create html container:
        local container = mw.html.create('div')
            :attr('style', 'vertical-align:top; display:inline-block;')

        -- Create the drop down table with <table></table>:
        local headers = container
        if #item_mods[sctn['header']] > 0 then
            headers
                :tag('h3')
                    :wikitext(string.format('%s', sctn['header']))
                    :done()
                :done()
        end

        local total_mod_groups = 0
        for _ in pairs(mod_group_counter[sctn['header']][adj]) do
            total_mod_groups = 1+total_mod_groups
        end

        -- Loop through and add all matching mods to the <table>.
        local tbl, last_group, last_type
        for _, rows in ipairs(item_mods[sctn['header']]) do

            -- If the last mod group is different to the current
            -- mod group then assume the mod isn't related and start
            -- a new drop down list, if there's only one mod group
            -- then use mod type instead:
            if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) then
                -- Check through all the mods and see if there are
                -- multiple mod types within the same mod group:
                local count = {}
                for _, n in ipairs(item_mods[sctn['header']]) do

                    -- If the mod has the same mod group, then add
                    -- the mod type to the counter. Only unique mod
                    -- types matter so the number is just a dummy
                    -- value:
                    if n['mods.mod_group'] == rows['mods.mod_group'] then
                        count[n['mods.mod_type']] = 1
                    end
                end

                -- Calculate how many unique mod types with the
                -- same mod group there are for all explicit or implicit
                -- sections since a mod with the same mod group can't
                -- spawn. Doesn't matter if it's prefix or suffix.
                local number_of_mod_types = 0
                for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
                    number_of_mod_types = 1 + number_of_mod_types
                end

                -- If there are multiple unique mod types with the
                -- same mod group then change the style of the drop
                -- down list to indicate it, if there's only one
                -- mod group in the generation type then ignore it:
                local table_index_mod_group
                if number_of_mod_types > 1 and total_mod_groups > 1 then
                    table_index_mod_group = table.concat(
                        {string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
                        ''
                    )

                    tbl_caption = string.format(
                        '%s',
                        m_util.html.poe_color(
                            'stat',
                            string.format(
                                '%s %s',
                                i18n.drop_down_table.mod_group,
                                rows['mods.mod_group']
                            )
                        ) or ''
                    )

                else
                    tbl_caption = string.format(
                        '%s (%s)',
                        m_util.html.poe_color(
                            'mod',
                            h.header(rows['mods.stat_text_raw'])
                        ) or '',
                        rows['mods.scope']
                    )
                end

                -- Create a table index for handling the collapsible:
                table_index_base = table_index_base+1
                if table_index_mod_group ~= nil then
                    table_index = table_index_mod_group
                else
                    table_index = table_index_base
                end

                -- Add class and style to the <table>:
                tbl = container:tag('table')
                tbl
                    :attr('class', 'mw-collapsible mw-collapsed')
                    :attr('style',
                        'text-align:left; line-height:1.60em; width:810px;'
                    )
                    :tag('th')
                        :attr('class',
                            string.format('mw-customtoggle-%s', table_index)
                        )
                        :attr('style',
                            'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                        )
                        :attr('colspan', '3' .. #extra_fields)
                        :wikitext(tbl_caption)
                        :done()
                    :done()
            end

            -- If the mod has no name then use the mod type:
            local mod_name = rows['mods.name'] or ''
            if  mod_name == '' then
                mod_name = rows['mods.mod_type']
            end

            -- Check if there are any extra properties to show in
            -- the drop down list and then add a cell for that,
            -- add this node at the end of the table row:
            local td = mw.html.create('td')
            if extra_fields ~= nil then
                for _, extra_field in ipairs(extra_fields) do
                    td
                        :attr('width', '*')
                        :wikitext(string.format(
                            '%s:&nbsp;%s ',
                            extra_field,
                            rows[extra_field] or ''
                            )
                        )
                        :done()
                end
            end

            -- Style mods.tags:
            local mods_tags = table.concat(
                m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}),
                ', '
            )
            if mods_tags ~= '' then
                mods_tags = m_util.html.tooltip('*', mods_tags, class)
            end

            -- Add a table row with the interesting properties that
            -- modifier has:
            tbl
                :tag('tr')
                    :attr('class', 'mw-collapsible mw-collapsed')
                    :attr(
                        'id',
                        string.format('mw-customcollapsible-%s', table_index)
                    )
                    :tag('td')
                        :attr('width', '160')
                        :wikitext(
                            string.format(
                                '&nbsp;&nbsp;&nbsp;[[%s|%s]]',
                                rows['mods._pageName'],
                                mod_name:gsub('%s', '&nbsp;')
                            )
                        )
                        :done()
                    :tag('td')
                        :attr('width', '1')
                        :wikitext(
                            string.format(
                                '%s&nbsp;%s',
                                m_game.level_requirement['short_upper']
                                    :gsub('%s', '&nbsp;'),
                                rows['mods.required_level']
                            )
                        )
                        :done()
                    :tag('td')
                        :attr('width', '*')
                        :wikitext(
                            string.format(
                                '%s%s',
                                m_util.html.poe_color(
                                    'mod',
                                    rows['mods.stat_text']
                                        :gsub('<br>', ', ')
                                        :gsub('<br />', ' ')
                                ) or '',
                                mods_tags
                            )
                        )
                        :done()
                    :node(td)
                    :done()
                :done()

            -- Save the last mod group for later comparison:
            last_group = rows['mods.mod_group']
            last_type = rows['mods.mod_type']
        end
        out[#out+1] = tostring(container)
    end

    -- Outro text:
    out[#out+1] = '<br>'
    out[#out+1] = m_util.html.poe_color(
        'normal',
        string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
    )

    return table.concat(out,'')
end

-- ----------------------------------------------------------------------------
-- Debug functions
-- ----------------------------------------------------------------------------

p.debug = {}
function p.debug.tbl_data(tbl)
    keys = {}
    for _, data in ipairs(mod_table.data) do
        if type(data.arg) == 'string' then
            keys[data.arg] = 1
        elseif type(data.arg) == 'table' then
            for _, arg in ipairs(data.arg) do
                keys[arg] = 1
            end
        end
    end

    for _, key in ipairs(mod_table.weights) do
        keys[key] = 1
    end

    local out = {}
    for key, _ in pairs(keys) do
        out[#out+1] = string.format("['%s'] = '1'", key)
    end

    return table.concat(out, ', ')
end

-- ----------------------------------------------------------------------------
--
-- ----------------------------------------------------------------------------

return p