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 (Use the in-game name instead of the internal tag.)
(p.drop_down_table - Use m_cargo to map results to pagename.)
Line 35: Line 35:
 
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 = '[[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',
 
normal_labyrinth = 'Normal Labyrinth',
 
cruel_labyrinth = 'Cruel Labyrinth',
 
cruel_labyrinth = 'Cruel Labyrinth',
Line 48: Line 48:
 
eternal_labyrinth = 'Eternal Labyrinth',
 
eternal_labyrinth = 'Eternal Labyrinth',
 
},
 
},
  +
 
 
drop_down_table = {
 
drop_down_table = {
 
collapse_all = 'Collapse all',
 
collapse_all = 'Collapse all',
Line 55: Line 55:
 
prefix = 'Prefix',
 
prefix = 'Prefix',
 
suffix = 'Suffix',
 
suffix = 'Suffix',
corrupted = 'Corrupted',
+
corrupted = 'Corrupted',
 
enchant = 'Enchantment',
 
enchant = 'Enchantment',
 
elder_prefix = 'Elder prefix',
 
elder_prefix = 'Elder prefix',
Line 74: Line 74:
 
back_to_top = 'Back to top',
 
back_to_top = 'Back to top',
 
},
 
},
  +
 
 
errors = {
 
errors = {
 
--
 
--
Line 81: Line 81:
 
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 101: Line 101:
 
function h.header(str)
 
function h.header(str)
 
--[[
 
--[[
This function replaces specific numbers with a generic #.
+
This function replaces specific numbers with a generic #.
 
]]
 
]]
  +
 
 
local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
 
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, '%d+%.*%d*'), '#')
 
s = table.concat(m_util.string.split(s, '<br>'), ', ')
 
s = table.concat(m_util.string.split(s, '<br>'), ', ')
 
s = table.concat(m_util.string.split(s, '<br />'), ' ')
 
s = table.concat(m_util.string.split(s, '<br />'), ' ')
  +
 
 
return s
 
return s
 
end
 
end
Line 126: Line 126:
 
error('Hit maximum cargo results')
 
error('Hit maximum cargo results')
 
end
 
end
  +
 
 
return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
 
return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
 
end
 
end
Line 137: Line 137:
 
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'] or '',
 
v['mods.id'] or v['mods._pageName'] or '',
 
string.gsub(
 
string.gsub(
v['mods.stat_text_raw'] or 'N/A',
+
v['mods.stat_text_raw'] or 'N/A',
'<br>',
+
'<br>',
 
', '
 
', '
 
) or '',
 
) or '',
 
v['mods._pageName'] or ''
 
v['mods._pageName'] or ''
 
)
 
)
end
+
end
 
return table.concat(str, '<br>')
 
return table.concat(str, '<br>')
 
end
 
end
Line 153: Line 153:
 
--[[
 
--[[
 
Returns a Cargo query of all the results.
 
Returns a Cargo query of all the results.
  +
 
 
tpl_args to be added to the cargo table needs to prefixed with q_.
 
tpl_args to be added to the cargo table needs to prefixed with q_.
 
* tpl_args.q_*
 
* tpl_args.q_*
  +
 
 
]]
 
]]
  +
 
 
-- Parse query arguments
 
-- Parse query arguments
 
local query = {
 
local query = {
Line 164: Line 164:
 
offset = 0,
 
offset = 0,
 
}
 
}
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
  +
 
-- Query cargo table. If there are too many results then repeat,
+
-- Query cargo table. If there are too many results then repeat,
 
-- offset, re-query and add the remaining results:
 
-- offset, re-query and add the remaining results:
local results = {}
+
local results = {}
 
repeat
 
repeat
 
local result = cargo.query(
 
local result = cargo.query(
Line 184: Line 184:
 
results[#results + 1] = v
 
results[#results + 1] = v
 
end
 
end
until #result < query.limit
+
until #result < query.limit
  +
 
 
return results
 
return results
 
end
 
end
Line 286: Line 286:
 
fields = {string.format([[
 
fields = {string.format([[
 
CONCAT(
 
CONCAT(
CASE mods.required_level
+
CASE mods.required_level
WHEN 32 THEN "%s"
+
WHEN 32 THEN "%s"
WHEN 53 THEN "%s"
+
WHEN 53 THEN "%s"
WHEN 66 THEN "%s"
+
WHEN 66 THEN "%s"
WHEN 75 THEN "%s"
+
WHEN 75 THEN "%s"
 
END
 
END
 
)=labyrinth_text
 
)=labyrinth_text
Line 312: Line 312:
 
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 322: Line 322:
 
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>')
 
text = table.concat(out, '<br>')
 
else
 
else
 
text = data['mods.stat_text']
 
text = data['mods.stat_text']
 
end
 
end
  +
 
 
tr
 
tr
 
:tag('td')
 
:tag('td')
Line 397: Line 397:
 
--[[
 
--[[
 
Creates a generic table for modifiers.
 
Creates a generic table for modifiers.
  +
 
 
Examples
 
Examples
 
--------
 
--------
Line 409: Line 409:
 
enchantment=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
 
-- default to enabled
 
tpl_args.name = tpl_args.name or true
 
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 436: Line 436:
 
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 444: Line 444:
 
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 450: Line 450:
 
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 tables
local tables = 'mods'
+
local tables = 'mods'
 
if tpl_args.q_tables then
 
if tpl_args.q_tables then
 
tables = tables .. ',' .. tpl_args.q_tables
 
tables = tables .. ',' .. tpl_args.q_tables
 
end
 
end
  +
 
  +
 
 
-- Set required fields
 
-- Set required fields
 
local fields = {
 
local fields = {
Line 476: Line 476:
 
end
 
end
 
end
 
end
  +
 
 
-- Parse query arguments
 
-- Parse query arguments
 
local query = {
 
local query = {
Line 482: Line 482:
 
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
  +
 
  +
 
 
fields = table.concat(fields, ',')
 
fields = table.concat(fields, ',')
 
if tpl_args.q_fields then
 
if tpl_args.q_fields then
 
fields = fields .. ' ,' .. tpl_args.q_fields
 
fields = fields .. ' ,' .. tpl_args.q_fields
 
end
 
end
  +
 
 
local results = cargo.query(
 
local results = cargo.query(
 
tables,
 
tables,
Line 499: Line 499:
 
query
 
query
 
)
 
)
  +
 
 
if #results == 0 then
 
if #results == 0 then
 
if tpl_args.default ~= nil then
 
if tpl_args.default ~= nil then
Line 507: Line 507:
 
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 it's actually needed
 
-- because performance impact should be neglible
 
-- because performance impact should be neglible
Line 515: Line 515:
 
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 522: Line 522:
 
end
 
end
 
end
 
end
  +
 
 
local stats
 
local stats
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
Line 529: Line 529:
 
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(
 
local stat_results = cargo.query(
 
'mods,mod_stats',
 
'mods,mod_stats',
Line 543: Line 543:
 
error('Hit maximum cargo results')
 
error('Hit maximum cargo results')
 
end
 
end
  +
 
 
stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
 
stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
 
-- In addition map stats to stat <-> min/max pairs
 
-- In addition map stats to stat <-> min/max pairs
Line 554: Line 554:
 
end
 
end
 
end
 
end
  +
 
 
--
 
--
 
-- Display
 
-- Display
 
--
 
--
  +
 
 
-- Preformance optimization
 
-- Preformance optimization
 
if tpl_args.q_fields then
 
if tpl_args.q_fields then
Line 570: Line 570:
 
tpl_args._extra_fields = {}
 
tpl_args._extra_fields = {}
 
end
 
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')
 
for _, row_info in ipairs(row_infos) do
 
for _, row_info in ipairs(row_infos) do
Line 583: Line 583:
 
: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 593: Line 593:
 
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 601: Line 601:
 
end
 
end
 
end
 
end
  +
 
 
for _, field in ipairs(tpl_args._extra_fields) do
 
for _, field in ipairs(tpl_args._extra_fields) do
 
tr
 
tr
Line 607: Line 607:
 
:wikitext(field)
 
:wikitext(field)
 
end
 
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 _, rowinfo 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
Line 633: Line 633:
 
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 655: Line 655:
 
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 667: Line 667:
 
: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
 
end
 
end
  +
 
 
for _, field in ipairs(tpl_args._extra_fields) do
 
for _, field in ipairs(tpl_args._extra_fields) do
 
if row[field] then
 
if row[field] then
Line 683: Line 683:
 
end
 
end
 
end
 
end
  +
 
 
return tostring(tbl)
 
return tostring(tbl)
 
end
 
end
Line 698: Line 698:
 
--[[
 
--[[
 
Finds and links to a modifier in formatted form.
 
Finds and links to a modifier in formatted form.
  +
 
To do list
+
To do list
 
----------
 
----------
* Standardize hoverbox so it can be used in multiple places easily
+
* Standardize hoverbox so it can be used in multiple places easily
and make it easy to add rows of data.
+
and make it easy to add rows of data.
  +
 
 
Examples
 
Examples
 
--------
 
--------
Line 711: Line 711:
 
= p.modifier_link{"StrDexMaster%"}
 
= 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 717: Line 717:
 
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:
 
-- Define query arguments:
 
local tables = {'mods', 'mod_stats', 'spawn_weights'}
 
local tables = {'mods', 'mod_stats', 'spawn_weights'}
Line 727: Line 727:
 
join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
 
join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
 
where = string.format(
 
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%%"',
+
'(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.modid,
 
tpl_args.modid,
 
tpl_args.modid,
tpl_args.statid or '%'
+
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',
 
}
 
}
  +
 
 
-- Query cargo rows:
 
-- Query cargo rows:
 
local results = m_util.cargo.query(tables, fields, query, args)
 
local results = m_util.cargo.query(tables, fields, query, args)
  +
 
 
-- Create own list for each cargo table and group by page name:
 
-- Create own list for each cargo table and group by page name:
 
tpl_args.tbl = {}
 
tpl_args.tbl = {}
for _,v in ipairs(tables) do
+
for _,v in ipairs(tables) do
 
tpl_args.tbl[v] = {}
 
tpl_args.tbl[v] = {}
 
end
 
end
  +
 
 
tpl_args.results_unique = {}
 
tpl_args.results_unique = {}
 
local hash = {}
 
local hash = {}
 
for _,v in ipairs(results) do
 
for _,v in ipairs(results) do
for ii, vv in pairs(tpl_args.tbl) do
+
for ii, vv in pairs(tpl_args.tbl) do
if tpl_args.tbl[ii][v['mods._pageName']] == nil then
+
if tpl_args.tbl[ii][v['mods._pageName']] == nil then
 
tpl_args.tbl[ii][v['mods._pageName']] = {}
 
tpl_args.tbl[ii][v['mods._pageName']] = {}
 
end
 
end
 
local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
 
local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
 
tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
 
tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
  +
 
 
-- Get a sorted list that only has unique page names:
 
-- Get a sorted list that only has unique page names:
 
if hash[v['mods._pageName']] ~= true then
 
if hash[v['mods._pageName']] ~= true then
Line 763: Line 763:
 
end
 
end
 
end
 
end
end
+
end
   
-- If no results are found then just create a empty list to avoid
+
-- If no results are found then just create a empty list to avoid
 
-- index errors.
 
-- index errors.
 
if tpl_args.results_unique[1] == nil then
 
if tpl_args.results_unique[1] == nil then
 
tpl_args.results_unique[1] = {}
 
tpl_args.results_unique[1] = {}
 
end
 
end
  +
 
 
-- Helpful error handling:
 
-- Helpful error handling:
 
local err_tbl = {
 
local err_tbl = {
Line 790: Line 790:
 
disp = {
 
disp = {
 
string.gsub(
 
string.gsub(
i18n.errors.incorrect_modid,
+
i18n.errors.incorrect_modid,
 
'%%s',
 
'%%s',
 
tpl_args.modid,
 
tpl_args.modid,
Line 799: Line 799:
 
},
 
},
 
}
 
}
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 807: Line 807:
 
end
 
end
 
end
 
end
  +
 
 
-- Display formats:
 
-- Display formats:
 
local display = {
 
local display = {
Line 813: Line 813:
 
display = function(tpl_args, frame)
 
display = function(tpl_args, frame)
 
local name = m_util.html.poe_color(
 
local name = 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']
 
)
 
)
 
)
 
)
  +
 
 
local tooltip_table = {
 
local tooltip_table = {
 
m_util.html.poe_color(
 
m_util.html.poe_color(
Line 837: Line 837:
 
-- 'help',
 
-- 'help',
 
-- table.concat(
 
-- table.concat(
-- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
+
-- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
 
-- ', '
 
-- ', '
 
-- )
 
-- )
Line 846: Line 846:
 
),
 
),
 
}
 
}
  +
 
 
local tt_tbl_fltrd = {}
 
local tt_tbl_fltrd = {}
 
for _,v in ipairs(tooltip_table) do
 
for _,v in ipairs(tooltip_table) do
if v ~= nil and v ~= '' then
+
if v ~= nil and v ~= '' then
 
tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
 
tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
 
end
 
end
 
end
 
end
 
local tooltip = table.concat(tt_tbl_fltrd, '<br>')
 
local tooltip = table.concat(tt_tbl_fltrd, '<br>')
  +
 
 
return m_util.html.tooltip(name, tooltip, class)
 
return m_util.html.tooltip(name, tooltip, class)
 
end,
 
end,
Line 863: Line 863:
 
'%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 889: Line 889:
 
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 897: Line 897:
 
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.tbl.mod_stats[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 903: Line 903:
 
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 919: Line 919:
 
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.tbl.mod_stats[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 925: Line 925:
 
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 939: Line 939:
 
},
 
},
 
}
 
}
  +
 
 
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 952: Line 952:
 
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 972: Line 972:
 
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 979: Line 979:
 
-- 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:
 
* Add support to:
 
* Forsaken masters
 
* Forsaken masters
 
* Bestiary
 
* Bestiary
* Add a proper expand/collapse toggle for the entire header row so
+
* Add a proper expand/collapse toggle for the entire header row so
it reacts together with mw-collapsible.
+
it reacts together with mw-collapsible.
* Show Mod group in a better way perhaps:
+
* Show Mod group in a better way perhaps:
 
Mod group [Collapsible, default=Expanded]
 
Mod group [Collapsible, default=Expanded]
 
# to Damage [Collapsible, default=Collapsed]
 
# to Damage [Collapsible, default=Collapsed]
 
3 to Damage
 
3 to Damage
 
5 to Damage
 
5 to Damage
* Add a where condition that somehow filters out mods that obviously
+
* Add a where condition that somehow filters out mods that obviously
wont match with the item. spawn_weights.weight>0 isn't enough due
+
wont match with the item. spawn_weights.weight>0 isn't enough due
 
to possible edge cases.
 
to possible edge cases.
  +
 
  +
 
Examples:
+
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'}
Line 1,017: Line 1,017:
 
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
 
Boots
 
= p.drop_down_table{item = 'Iron Greaves', header = 'Boots'}
 
= p.drop_down_table{item = 'Iron Greaves', header = 'Boots'}
  +
 
 
= p.drop_down_table{
 
= p.drop_down_table{
item = 'Fishing Rod',
+
item = 'Fishing Rod',
header = 'FISH PLEASE',
+
header = 'FISH PLEASE',
item_tags = 'fishing_rod',
+
item_tags = 'fishing_rod',
 
extra_fields = 'mods.tags'
 
extra_fields = 'mods.tags'
 
}
 
}
  +
 
 
= p.drop_down_table{
 
= p.drop_down_table{
 
item = 'Fishing Rod',
 
item = 'Fishing Rod',
Line 1,039: Line 1,039:
 
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 1,049: Line 1,049:
 
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)
  +
 
 
-- Format the cargo query:
 
-- Format the cargo query:
 
local where
 
local where
Line 1,055: Line 1,055:
 
{tpl_args.page, 'items._pageName = "%s"'},
 
{tpl_args.page, 'items._pageName = "%s"'},
 
{tpl_args.item, 'items.name = "%s"'},
 
{tpl_args.item, 'items.name = "%s"'},
}) do
+
}) do
 
if v[1] ~= nil then
 
if v[1] ~= nil then
 
where = string.format(v[2], v[1])
 
where = string.format(v[2], v[1])
Line 1,061: Line 1,061:
 
end
 
end
 
end
 
end
  +
 
 
local item_info = m_cargo.query(
 
local item_info = m_cargo.query(
 
{'items'},
 
{'items'},
Line 1,071: Line 1,071:
 
}
 
}
 
)[1]
 
)[1]
  +
 
-- Set the item class as key and the corresponding mod domain as
+
-- Set the item class as key and the corresponding mod domain as
 
-- value:
 
-- value:
 
local class_to_domain = {
 
local class_to_domain = {
Line 1,085: Line 1,085:
 
['AbyssJewel']=13,
 
['AbyssJewel']=13,
 
}
 
}
-- Get the domain, if it's not defined in the table assume it's
+
-- Get the domain, if it's not defined in the table assume it's
 
-- in the item domain.
 
-- in the item domain.
 
item_info['items.domain'] = class_to_domain[item_info['items.class_id']] or 1
 
item_info['items.domain'] = class_to_domain[item_info['items.class_id']] or 1
  +
 
 
-- Convert the mod domain number to understandable text:
 
-- Convert the mod domain number to understandable text:
 
item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
 
item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
  +
 
-- For some reason cargo queried item tags, are not comma-space
+
-- For some reason cargo queried item tags, are not comma-space
 
-- separated any more.
 
-- separated any more.
 
if tpl_args.item_tags ~= nil then
 
if tpl_args.item_tags ~= nil then
Line 1,101: Line 1,101:
 
if tpl_args.extra_item_tags then
 
if tpl_args.extra_item_tags then
 
local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
 
local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
for _,v in ipairs(extra_item_tags) do
+
for _,v in ipairs(extra_item_tags) do
 
tpl_args.item_tags[#tpl_args.item_tags+1] = v
 
tpl_args.item_tags[#tpl_args.item_tags+1] = v
 
end
 
end
 
end
 
end
  +
 
 
-- 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 elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
 
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 shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
Line 1,115: Line 1,115:
 
local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_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']
 
local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']
  +
 
 
local section = {}
 
local section = {}
 
section = {
 
section = {
Line 1,122: Line 1,122:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,135: Line 1,135:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
Line 1,143: Line 1,143:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,156: Line 1,156:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
Line 1,166: Line 1,166:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,183: Line 1,183:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,200: Line 1,200:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,217: Line 1,217:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,232: Line 1,232:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
]],
+
]],
 
item_tag,
 
item_tag,
 
1,
 
1,
Line 1,245: Line 1,245:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
Line 1,253: Line 1,253:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,266: Line 1,266:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
Line 1,276: Line 1,276:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,293: Line 1,293:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,310: Line 1,310:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,327: Line 1,327:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,344: Line 1,344:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,361: Line 1,361:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,378: Line 1,378:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,395: Line 1,395:
 
return string.format(
 
return string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,410: Line 1,410:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,423: Line 1,423:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
+
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
 
is_implicit = true,
 
is_implicit = true,
 
},
 
},
Line 1,433: Line 1,433:
 
where = function(tpl_args, frame, value)
 
where = function(tpl_args, frame, value)
 
local where = {}
 
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
+
for _, item_tag in ipairs(tpl_args.item_tags) do
 
where[#where+1] = string.format(
 
where[#where+1] = string.format(
 
[[
 
[[
(spawn_weights.tag="%s"
+
(spawn_weights.tag="%s"
AND mods.generation_type=%s
+
AND mods.generation_type=%s
AND mods.domain=%s
+
AND mods.domain=%s
 
AND mods.stat_text IS NOT NULL)
 
AND mods.stat_text IS NOT NULL)
 
]],
 
]],
Line 1,446: Line 1,446:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
is_implicit = true,
 
is_implicit = true,
 
},
 
},
 
}
 
}
  +
 
 
-- Save the original tag format:
 
-- Save the original tag format:
 
local item_tags_orig = {}
 
local item_tags_orig = {}
Line 1,458: Line 1,458:
 
item_tags_orig[i] = v
 
item_tags_orig[i] = v
 
end
 
end
  +
 
 
local item_mods = {}
 
local item_mods = {}
 
local mod_group_counter = {}
 
local mod_group_counter = {}
 
mod_group_counter['all'] = {}
 
mod_group_counter['all'] = {}
 
local extra_fieldss = {}
 
local extra_fieldss = {}
local table_index_base = -1
+
local table_index_base = -1
 
for _, sctn in ipairs(section) do
 
for _, sctn in ipairs(section) do
 
item_mods[sctn['header']] = {}
 
item_mods[sctn['header']] = {}
  +
 
 
-- Preallocate the mod group counter, implicit and explicit mods
 
-- Preallocate the mod group counter, implicit and explicit mods
 
-- are counted separetely because they can spawn together:
 
-- are counted separetely because they can spawn together:
 
mod_group_counter[sctn['header']] = {}
 
mod_group_counter[sctn['header']] = {}
 
local adj = 'explicit'
 
local adj = 'explicit'
if sctn['is_implicit'] then
+
if sctn['is_implicit'] then
 
adj = 'implicit'
 
adj = 'implicit'
 
end
 
end
Line 1,479: Line 1,479:
 
end
 
end
 
end
 
end
  +
 
 
local continue = true
 
local continue = true
 
local current_tags
 
local current_tags
Line 1,496: Line 1,496:
 
end
 
end
 
end
 
end
  +
 
if continue then
+
if continue then
 
-- Cargo preparation:
 
-- Cargo preparation:
 
tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
 
tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
Line 1,505: Line 1,505:
 
tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
 
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.required_level, mods._pageName, spawn_weights.ordinal'
 
tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods.required_level, mods._pageName, spawn_weights.ordinal'
  +
 
 
local extra_fields = {}
 
local extra_fields = {}
 
if tpl_args.extra_fields ~= nil then
 
if tpl_args.extra_fields ~= nil then
 
extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
 
extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
 
tpl_args.q_fields = string.format(
 
tpl_args.q_fields = string.format(
'%s, %s',
+
'%s, %s',
tpl_args.q_fields,
+
tpl_args.q_fields,
 
table.concat(extra_fields, ', ')
 
table.concat(extra_fields, ', ')
 
)
 
)
 
end
 
end
  +
 
-- Query mods:
+
-- Query mods and map the results to the pagename:
results = h.cargo_query(tpl_args)
+
local results = m_cargo.map_results_to_id{
+
results=h.cargo_query(tpl_args),
-- Create own list for spawn weights and group by page name:
+
field='mods._pageName',
local spawn_weights = {}
+
keep_id_field=true,
local results_unique = {}
+
append_id_field=true,
local hash = {}
+
}
  +
for _,v in ipairs(results) do
 
if spawn_weights[v['mods._pageName']] == nil then
+
if #results > 0 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
 
 
-- Loop through all found modifiers:
 
-- Loop through all found modifiers:
 
local last
 
local last
for _, v in ipairs(results_unique) do
+
for _, id in ipairs(results) do
local pagename = v['spawn_weights._pageName']
 
 
 
if sctn['header'] == i18n.drop_down_table.elder_prefix then
 
if sctn['header'] == i18n.drop_down_table.elder_prefix then
-- error(mw.dumpObject(spawn_weights))
+
-- error(mw.dumpObject(results))
 
end
 
end
-- Loop through all the modifier tags until they match
+
-- Loop through all the modifier tags until they match
 
-- the item tags:
 
-- the item tags:
 
local j = 0
 
local j = 0
 
local tag_match_stop
 
local tag_match_stop
repeat
+
repeat
 
j = j+1
 
j = j+1
local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
+
local mod_tag = results[id][j]['spawn_weights.tag']
 
local mod_tag_weight = tonumber(
 
local mod_tag_weight = tonumber(
spawn_weights[pagename][j]['spawn_weights.weight']
+
results[id][j]['spawn_weights.weight']
 
)
 
)
   
-- Loop through the item tags until it matches the
+
-- Loop through the item tags until it matches the
-- spawn weight tag and the mod tag has a value larger than
+
-- spawn weight tag and the mod tag has a value larger than
 
-- zero:
 
-- zero:
 
local y = 0
 
local y = 0
 
local tag_match_add = false
 
local tag_match_add = false
repeat
+
repeat
 
y = y+1
 
y = y+1
tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
+
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)
 
tag_match_add = (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
 
until tag_match_stop or y == #current_tags
 
until tag_match_stop or y == #current_tags
  +
 
-- If there's a match then save that mod and other
+
-- If there's a match then save that mod and other
 
-- interesting information:
 
-- interesting information:
if tag_match_add then
+
if tag_match_add then
  +
 
-- Assume that the mod is global then go through
+
-- Assume that the mod is global then go through
-- all the stat ids and check if any of the
+
-- all the stat ids and check if any of the
 
-- stats are local:
 
-- stats are local:
local mod_scope = 'Global'
+
local mod_scope = 'Global'
for _, vv in ipairs(spawn_weights[pagename]) do
+
for _, vv in ipairs(results[id]) do
if vv['mod_stats.id']:find('.*local.*') ~= nil then
+
if vv['mod_stats.id']:find('.*local.*') ~= nil then
 
mod_scope = 'Local'
 
mod_scope = 'Local'
 
end
 
end
end
+
end
  +
 
 
-- Save the matching modifier tag:
 
-- Save the matching modifier tag:
 
local a = #item_mods[sctn['header']]
 
local a = #item_mods[sctn['header']]
item_mods[sctn['header']][a+1] = spawn_weights[pagename][j]
+
item_mods[sctn['header']][a+1] = results[id][j]
  +
 
 
-- Save other interesting fields:
 
-- Save other interesting fields:
item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
+
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]['spawn_weight.idx_match'] = j
item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
+
item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
 
item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
 
item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
  +
 
 
-- Count the mod groups:
 
-- Count the mod groups:
 
local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
 
local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
Line 1,598: Line 1,583:
 
mod_group_counter[header][adj][group] = {}
 
mod_group_counter[header][adj][group] = {}
 
end
 
end
local tp = spawn_weights[pagename][j]['mods.mod_type']
+
local tp = results[id][j]['mods.mod_type']
 
local bef = mod_group_counter[header][adj][group][tp] or 0
 
local bef = mod_group_counter[header][adj][group][tp] or 0
 
mod_group_counter[header][adj][group][tp] = 1 + bef
 
mod_group_counter[header][adj][group][tp] = 1 + bef
 
end
 
end
 
end
 
end
until tag_match_stop
+
until tag_match_stop
 
end
 
end
  +
 
-- If the user wants to see the spawn chance then do the
+
-- If the user wants to see the spawn chance then do the
 
-- calculations and save that result as well:
 
-- calculations and save that result as well:
if tpl_args.spawn_chance ~= nil then
+
if tpl_args.spawn_chance ~= nil then
 
extra_fields[#extra_fields+1] = 'spawn_weights.chance'
 
extra_fields[#extra_fields+1] = 'spawn_weights.chance'
 
item_mods[sctn['header']] = h.get_spawn_chance{
 
item_mods[sctn['header']] = h.get_spawn_chance{
tbl = item_mods[sctn['header']],
+
tbl = item_mods[sctn['header']],
 
chance_multiplier = sctn['chance_multiplier']
 
chance_multiplier = sctn['chance_multiplier']
 
}
 
}
 
end
 
end
  +
 
 
extra_fieldss[sctn['header']] = extra_fields
 
extra_fieldss[sctn['header']] = extra_fields
 
end
 
end
 
end
 
end
 
end
 
end
  +
 
  +
 
--
+
--
 
-- Display the item mods
 
-- 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(tpl_args.item_tags, ', ')
 
tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
 
)
 
)
Line 1,638: Line 1,623:
 
)
 
)
 
out[#out+1] = expand_button
 
out[#out+1] = expand_button
out[#out+1] = string.format('%s %s.<br><br><br>',
+
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=item_info['items._pageName'],
+
page=item_info['items._pageName'],
 
name=item_info['items.name'],
 
name=item_info['items.name'],
inventory_icon=item_info['items.inventory_icon'] or '',
+
inventory_icon=item_info['items.inventory_icon'] or '',
html=item_info['items.html'] or '',
+
html=item_info['items.html'] or '',
 
skip_query=true
 
skip_query=true
 
}
 
}
 
)
 
)
  +
 
 
-- Loop through the sections:
 
-- Loop through the sections:
 
for _, sctn in ipairs(section) do
 
for _, sctn in ipairs(section) do
Line 1,655: Line 1,640:
 
if sctn['is_implicit'] then
 
if sctn['is_implicit'] then
 
adj = 'implicit'
 
adj = 'implicit'
end
+
end
  +
 
 
-- Create html container:
 
-- 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;')
  +
 
 
-- Create the drop down table with <table></table>:
 
-- Create the drop down table with <table></table>:
local headers = container
+
local headers = container
if #item_mods[sctn['header']] > 0 then
+
if #item_mods[sctn['header']] > 0 then
 
headers
 
headers
 
:tag('h3')
 
:tag('h3')
Line 1,670: Line 1,655:
 
:done()
 
:done()
 
end
 
end
  +
 
 
local total_mod_groups = 0
 
local total_mod_groups = 0
 
for _ in pairs(mod_group_counter[sctn['header']][adj]) do
 
for _ in pairs(mod_group_counter[sctn['header']][adj]) do
 
total_mod_groups = 1+total_mod_groups
 
total_mod_groups = 1+total_mod_groups
 
end
 
end
  +
 
-- Loop through and add all matching mods to the <table>.
+
-- Loop through and add all matching mods to the <table>.
 
local tbl, last_group, last_type
 
local tbl, last_group, last_type
for _, rows in ipairs(item_mods[sctn['header']]) do
+
for _, rows in ipairs(item_mods[sctn['header']]) do
  +
 
-- If the last mod group is different to the current
+
-- If the last mod group is different to the current
-- mod group then assume the mod isn't related and start
+
-- mod group then assume the mod isn't related and start
-- a new drop down list, if there's only one mod group
+
-- a new drop down list, if there's only one mod group
 
-- then use mod type instead:
 
-- 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 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
+
-- Check through all the mods and see if there are
 
-- multiple mod types within the same mod group:
 
-- multiple mod types within the same mod group:
 
local count = {}
 
local count = {}
for _, n in ipairs(item_mods[sctn['header']]) do
+
for _, n in ipairs(item_mods[sctn['header']]) do
  +
 
-- If the mod has the same mod group, then add
+
-- If the mod has the same mod group, then add
-- the mod type to the counter. Only unique mod
+
-- the mod type to the counter. Only unique mod
-- types matter so the number is just a dummy
+
-- types matter so the number is just a dummy
 
-- value:
 
-- value:
 
if n['mods.mod_group'] == rows['mods.mod_group'] then
 
if n['mods.mod_group'] == rows['mods.mod_group'] then
count[n['mods.mod_type']] = 1
+
count[n['mods.mod_type']] = 1
 
end
 
end
 
end
 
end
  +
 
-- Calculate how many unique mod types with the
+
-- Calculate how many unique mod types with the
-- same mod group there are for all explicit or implicit
+
-- same mod group there are for all explicit or implicit
-- sections since a mod with the same mod group can't
+
-- sections since a mod with the same mod group can't
-- spawn. Doesn't matter if it's prefix or suffix.
+
-- spawn. Doesn't matter if it's prefix or suffix.
 
local number_of_mod_types = 0
 
local number_of_mod_types = 0
for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
+
for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
 
number_of_mod_types = 1 + number_of_mod_types
 
number_of_mod_types = 1 + number_of_mod_types
 
end
 
end
  +
 
-- If there are multiple unique mod types with the
+
-- If there are multiple unique mod types with the
-- same mod group then change the style of the drop
+
-- same mod group then change the style of the drop
-- down list to indicate it, if there's only one
+
-- down list to indicate it, if there's only one
 
-- mod group in the generation type then ignore it:
 
-- mod group in the generation type then ignore it:
 
local table_index_mod_group
 
local table_index_mod_group
 
if number_of_mod_types > 1 and total_mod_groups > 1 then
 
if number_of_mod_types > 1 and total_mod_groups > 1 then
 
table_index_mod_group = table.concat(
 
table_index_mod_group = table.concat(
{string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
+
{string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
 
''
 
''
 
)
 
)
  +
 
 
tbl_caption = string.format(
 
tbl_caption = string.format(
'%s',
+
'%s',
 
m_util.html.poe_color(
 
m_util.html.poe_color(
'stat',
+
'stat',
 
string.format(
 
string.format(
'%s %s',
+
'%s %s',
i18n.drop_down_table.mod_group,
+
i18n.drop_down_table.mod_group,
 
rows['mods.mod_group']
 
rows['mods.mod_group']
 
)
 
)
Line 1,731: Line 1,716:
 
)
 
)
   
else
+
else
 
tbl_caption = string.format(
 
tbl_caption = string.format(
'%s (%s)',
+
'%s (%s)',
 
m_util.html.poe_color(
 
m_util.html.poe_color(
'mod',
+
'mod',
 
h.header(rows['mods.stat_text_raw'])
 
h.header(rows['mods.stat_text_raw'])
) or '',
+
) or '',
 
rows['mods.scope']
 
rows['mods.scope']
 
)
 
)
 
end
 
end
  +
 
 
-- Create a table index for handling the collapsible:
 
-- Create a table index for handling the collapsible:
 
table_index_base = table_index_base+1
 
table_index_base = table_index_base+1
if table_index_mod_group ~= nil then
+
if table_index_mod_group ~= nil then
 
table_index = table_index_mod_group
 
table_index = table_index_mod_group
 
else
 
else
 
table_index = table_index_base
 
table_index = table_index_base
 
end
 
end
  +
 
 
-- Add class and style to the <table>:
 
-- Add class and style to the <table>:
 
tbl = container:tag('table')
 
tbl = container:tag('table')
 
tbl
 
tbl
 
:attr('class', 'mw-collapsible mw-collapsed')
 
:attr('class', 'mw-collapsible mw-collapsed')
:attr('style',
+
:attr('style',
 
'text-align:left; line-height:1.60em; width:810px;'
 
'text-align:left; line-height:1.60em; width:810px;'
 
)
 
)
 
:tag('th')
 
:tag('th')
:attr('class',
+
:attr('class',
 
string.format('mw-customtoggle-%s', table_index)
 
string.format('mw-customtoggle-%s', table_index)
 
)
 
)
:attr('style',
+
:attr('style',
 
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
 
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
 
)
 
)
Line 1,767: Line 1,752:
 
:wikitext(tbl_caption)
 
:wikitext(tbl_caption)
 
:done()
 
:done()
:done()
+
:done()
 
end
 
end
  +
 
 
-- If the mod has no name then use the mod type:
 
-- If the mod has no name then use the mod type:
 
local mod_name = rows['mods.name']
 
local mod_name = rows['mods.name']
if mod_name == '' or mod_name == nil then
+
if mod_name == '' or mod_name == nil then
 
mod_name = rows['mods.mod_type']
 
mod_name = rows['mods.mod_type']
 
end
 
end
  +
 
-- Check if there are any extra properties to show in
+
-- Check if there are any extra properties to show in
-- the drop down list and then add a cell for that,
+
-- the drop down list and then add a cell for that,
 
-- add this node at the end of the table row:
 
-- add this node at the end of the table row:
 
local td = mw.html.create('td')
 
local td = mw.html.create('td')
if extra_fields ~= nil then
+
if extra_fields ~= nil then
for _, extra_field in ipairs(extra_fields) do
+
for _, extra_field in ipairs(extra_fields) do
 
td
 
td
 
:attr('width', '*')
 
:attr('width', '*')
 
:wikitext(string.format(
 
:wikitext(string.format(
'%s:&nbsp;%s ',
+
'%s:&nbsp;%s ',
extra_field,
+
extra_field,
 
rows[extra_field]
 
rows[extra_field]
 
)
 
)
Line 1,793: Line 1,778:
 
end
 
end
 
end
 
end
  +
 
 
-- Style mods.tags:
 
-- Style mods.tags:
 
local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
 
local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
 
if mods_tags ~= '' then
 
if mods_tags ~= '' then
 
mods_tags = m_util.html.tooltip('*', mods_tags, class)
 
mods_tags = m_util.html.tooltip('*', mods_tags, class)
end
+
end
  +
 
-- Add a table row with the interesting properties that
+
-- Add a table row with the interesting properties that
 
-- modifier has:
 
-- modifier has:
 
tbl
 
tbl
Line 1,806: Line 1,791:
 
:attr('class', 'mw-collapsible mw-collapsed')
 
:attr('class', 'mw-collapsible mw-collapsed')
 
:attr(
 
:attr(
'id',
+
'id',
 
string.format('mw-customcollapsible-%s', table_index)
 
string.format('mw-customcollapsible-%s', table_index)
 
)
 
)
Line 1,813: Line 1,798:
 
:wikitext(
 
:wikitext(
 
string.format(
 
string.format(
'&nbsp;&nbsp;&nbsp;[[%s|%s]]',
+
'&nbsp;&nbsp;&nbsp;[[%s|%s]]',
rows['mods._pageName'],
+
rows['mods._pageName'],
 
mod_name:gsub('%s', '&nbsp;')
 
mod_name:gsub('%s', '&nbsp;')
 
)
 
)
Line 1,825: Line 1,810:
 
'%s&nbsp;%s',
 
'%s&nbsp;%s',
 
m_game.level_requirement['short_upper']
 
m_game.level_requirement['short_upper']
:gsub('%s', '&nbsp;'),
+
:gsub('%s', '&nbsp;'),
 
rows['mods.required_level']
 
rows['mods.required_level']
 
)
 
)
 
)
 
)
:done()
+
:done()
 
:tag('td')
 
:tag('td')
 
:attr('width', '*')
 
:attr('width', '*')
Line 1,836: Line 1,821:
 
'%s%s',
 
'%s%s',
 
m_util.html.poe_color(
 
m_util.html.poe_color(
'mod',
+
'mod',
 
rows['mods.stat_text']
 
rows['mods.stat_text']
 
:gsub('<br>', ', ')
 
:gsub('<br>', ', ')
 
:gsub('<br />', ' ')
 
:gsub('<br />', ' ')
) or '',
+
) or '',
 
mods_tags
 
mods_tags
 
)
 
)
Line 1,848: Line 1,833:
 
:done()
 
:done()
 
:done()
 
:done()
  +
 
 
-- Save the last mod group for later comparison:
 
-- Save the last mod group for later comparison:
 
last_group = rows['mods.mod_group']
 
last_group = rows['mods.mod_group']
Line 1,855: Line 1,840:
 
out[#out+1] = tostring(container)
 
out[#out+1] = tostring(container)
 
end
 
end
  +
 
 
-- Outro text:
 
-- Outro text:
 
out[#out+1] = '<br>'
 
out[#out+1] = '<br>'
Line 1,862: Line 1,847:
 
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
 
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
 
)
 
)
  +
 
 
return table.concat(out,'')
 
return table.concat(out,'')
 
end
 
end
Line 1,874: Line 1,859:
 
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,882: Line 1,867:
 
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 22:58, 8 January 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 = '[[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',
        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)
    results = cargo.query(
        string.format('mods,%s', table_name),
        string.format('mods._pageID,%s.tag,%s.weight', table_name, table_name),
        {
            where=page_ids,
            join=string.format('mods._pageID=%s._pageID', table_name),
            orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
            limit=5000,
        }
    )
    if #results == 5000 then
        error('Hit maximum cargo results')
    end

    return m_util.cargo.map_results_to_id{results=results,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

function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results.

    tpl_args to be added to the cargo table needs to prefixed with q_.
    * tpl_args.q_*

    ]]

    -- Parse query arguments
    local query = {
        limit = 5000,
        offset = 0,
    }
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        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(
            query.tables,
            query.fields,
            query
        )
        query.offset = query.offset + #result

        for _,v in ipairs(result) do
            results[#results + 1] = v
        end
    until #result < query.limit

    return results
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)
            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)
            local value = data['mods.domain']
            tr
                :tag('td')
                    :attr('data-sort-value', value)
                    :wikitext(m_game.constants.mod.domains[tonumber(value)]['short_upper'])
        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)
            local value = data['mods.generation_type']
            tr
                :tag('td')
                    :attr('data-sort-value', value)
                    :wikitext(m_game.constants.mod.generation_types[tonumber(value)]['short_upper'])
        end,
        order = 2001,
        sort_type = 'text',
    },
    {
        arg = {'group', 'mod_group'},
        header = i18n.mod_table.mod_group,
        fields = {'mods.mod_group'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.mod_group'])
        end,
        order = 2002,
        sort_type = 'text',
    },
    {
        arg = {'mod_type'},
        header = i18n.mod_table.mod_type,
        fields = {'mods.mod_type'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.mod_type'])
        end,
        order = 2003,
        sort_type = 'text',
    },
    {
        arg = {'level', 'required_level'},
        header = i18n.mod_table.required_level,
        fields = {'mods.required_level'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.required_level'])
        end,
        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 = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['labyrinth_text'])
        end,
        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)
            local text
            -- 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

                text = table.concat(out, '<br>')
            else
                text = data['mods.stat_text']
            end

            tr
                :tag('td')
                    :wikitext(text)
        end,
        order = 3000,
        sort_type = 'text',
    },

    {
        arg = 'buff',
        header = i18n.mod_table.buff,
        fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(string.format('%s %s', data['mods.granted_buff_id'], data['mods.granted_buff_value']))
        end,
        order = 4000,
        sort_type = 'text',
    },
    {
        arg = {'skill', 'granted_skill'},
        header = i18n.mod_table.granted_skill,
        fields = {'mods.granted_skill'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.granted_skill'])
        end,
        order = 4001,
        sort_type = 'text',
    },
    {
        arg = {'tags'},
        header = i18n.mod_table.tags,
        fields = {'mods.tags'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(table.concat(m_util.string.split(data['mods.tags'], ','), ', '))
        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 tables
    local tables = 'mods'
    if tpl_args.q_tables then
        tables = tables .. ',' .. tpl_args.q_tables
    end


    -- Set required fields
    local fields = {
        'mods._pageID',
    }
    for _, rowinfo in ipairs(row_infos) do
        if type(rowinfo.fields) == 'function' then
            rowinfo.fields = rowinfo.fields()
        end
        for index, field in ipairs(rowinfo.fields) do
            rowinfo.options[index] = rowinfo.options[index] or {}
            fields[#fields+1] = field
        end
    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


    fields = table.concat(fields, ',')
    if tpl_args.q_fields then
        fields = fields .. ' ,' .. tpl_args.q_fields
    end

    local results = 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

        local stat_results = 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',
                limit=5000,
            }
        )
        if #stat_results == 5000 then
            error('Hit maximum cargo results')
        end

        stats = m_util.cargo.map_results_to_id{results=stat_results, 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
    if tpl_args.q_fields then
        tpl_args._extra_fields = m_util.string.split(tpl_args.q_fields, ',')
        for index, field in ipairs(tpl_args._extra_fields) do
            field = m_util.string.split(field, '=')
            -- field[2] will be nil if there is no alias
            tpl_args._extra_fields[index] = field[2] or field[1]
        end
    else
        tpl_args._extra_fields = {}
    end

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

    local tr = tbl:tag('tr')
    for _, row_info in ipairs(row_infos) do
        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 _, rowinfo in ipairs(row_infos) do
            -- this has been cast from a function in an earlier step
            local display = true
            for index, field in ipairs(rowinfo.fields) do
                -- this will bet set to an empty value not nil confusingly
                if row[field] == '' then
                    if rowinfo.options[index].optional ~= true then
                        display = false
                        break
                    else
                        row[field] = nil
                    end
                end
            end
            if display then
                rowinfo.display(tpl_args, frame, tr, row, rowinfo.fields)
            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
                    weight_out[#weight_out+1] = string.format('%s %s', wrow[key .. '.tag'], wrow[key .. '.weight'])
                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 ''

    -- Define query arguments:
    local tables = {'mods', 'mod_stats', 'spawn_weights'}
    local fields = {'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'}
    local query = {
        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',
    }

    -- Query cargo rows:
    local results = m_util.cargo.query(tables, fields, query, args)

    -- 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

    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

    -- If no results are found then just create a empty list to avoid
    -- index errors.
    if tpl_args.results_unique[1] == nil then
        tpl_args.results_unique[1] = {}
    end

    -- Helpful error handling:
    local err_tbl = {
        {
            bool = #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.tbl.mod_stats[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.tbl.mod_stats[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'] = 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']

    -- For some reason cargo queried item tags, are not comma-space
    -- separated any more.
    if tpl_args.item_tags ~= nil then
        tpl_args.item_tags = m_util.string.split(tpl_args.item_tags, ', ')
    else
        tpl_args.item_tags = m_util.string.split(item_info['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
            tpl_args.item_tags[#tpl_args.item_tags+1] = v
        end
    end

    -- Create drop down lists in these sections and query in these
    -- generation types.

    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']

    local section = {}
    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.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:
            tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
            tpl_args.q_fields = '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, 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 = sctn['where'](tpl_args, frame, value)
            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.required_level, mods._pageName, 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.q_fields = string.format(
                    '%s, %s',
                    tpl_args.q_fields,
                    table.concat(extra_fields, ', ')
                )
            end

            -- Query mods and map the results to the pagename:
            local results = m_cargo.map_results_to_id{
                results=h.cargo_query(tpl_args),
                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
                    if sctn['header'] == i18n.drop_down_table.elder_prefix then
                     --   error(mw.dumpObject(results))
                    end
                    -- 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']
            if  mod_name == '' or mod_name == nil 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]
                            )
                        )
                        :done()
                end
            end

            -- Style mods.tags:
            local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
            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