Path of Exile Wiki

Wiki поддерживается сообществом, поэтому подумайте над тем, чтобы внести свой вклад.

ПОДРОБНЕЕ

Path of Exile Wiki
Нет описания правки
Нет описания правки
(не показано 25 промежуточных версий 2 участников)
Строка 1: Строка 1:
  +
-------------------------------------------------------------------------------
--[[
 
  +
--
Module responsible for displaying modifiers in various ways.
 
  +
-- Module:Modifier table
 
  +
--
]]
 
  +
-- Module responsible for displaying modifiers in various ways. Implements
  +
-- Template:Modifier table and Template:Item modifiers
  +
-------------------------------------------------------------------------------
   
  +
require('Module:No globals')
 
local m_util = require('Module:Util')
 
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 m_cargo = require('Module:Cargo')
   
  +
-- Should we use the sandbox version of our submodules?
local cargo = mw.ext.cargo
 
  +
local use_sandbox = m_util.misc.maybe_sandbox('Modifier table')
   
local p = {}
+
local m_game = mw.loadData('Module:Game')
   
  +
-- Lazy loading
-- ----------------------------------------------------------------------------
 
  +
local f_item_link -- require('Module:Item link').item_link
-- 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.
 
   
  +
-- The cfg table contains all localisable strings and configuration, to make it
local i18n = {
 
  +
-- easier to port this module to another wiki.
mod_table = {
 
  +
local cfg = use_sandbox and mw.loadData('Module:Modifier table/config/sandbox') or mw.loadData('Module:Modifier table/config')
name = m_util.html.abbr('Наименование', 'Название модификатора, если оно доступно, или его внутренний идентификатор'),
 
mod_group = m_util.html.abbr('Группа', 'При обычных обстоятельствах одновременно может отображаться только один модификатор из указанной группы'),
 
mod_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 = '[[Лабиринт правителя|Лабиринт]]',
 
stat_text = m_util.html.abbr('Свойства', 'Свойства модификатора и диапазон, в котором они могут действовать (если применимо)'),
 
buff = m_util.html.abbr('Эффект', 'Идентификатор предоставленного эффекта и связанные с ним значения'),
 
granted_skill = m_util.html.abbr('Умение', 'Идентификатор предоставленного умения'),
 
tags = '[[Tags]]',
 
 
iiq = m_util.html.abbr('IIQ', 'увеличение количества найденных предметов'),
 
iir = m_util.html.abbr('IIR', 'повышение редкости найденных предметов'),
 
pack_size = m_util.html.abbr('Размер<br>группы', 'Размер групп монстров'),
 
 
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 = 'Первый Лабиринт',
 
cruel_labyrinth = 'Жестокий Лабиринт',
 
merciless_labyrinth = 'Безжалостный Лабиринт',
 
eternal_labyrinth = 'Вечный Лабиринт',
 
},
 
 
drop_down_table = {
 
collapse_all = 'Свернуть все',
 
expand_all = 'Развернуть все',
 
table_intro = 'В приведенных ниже таблицах представлены доступные [[свойства]] для таких [[предмет]]ов, как',
 
prefix = 'Префикс',
 
suffix = 'Суффикс',
 
corrupted = 'Оскверненный',
 
enchant = 'Зачарование',
 
elder_prefix = 'Префикс Древнего',
 
elder_suffix = 'Суффикс Древнего',
 
shaper_prefix = 'Префикс Создателя',
 
shaper_suffix = 'Суффикс Создателя',
 
delve_prefix = 'Префикс Спуска',
 
delve_suffix = 'Суффикс Спуска',
 
mod_group = 'Группа свойств:',
 
},
 
 
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 = 'Измените название с "%s" на любой из этих идентификаторов модификатора:<br>%s',
 
multiple_results = 'Выберите только один из этих идентификаторов модификатора:<br>%s',
 
no_results = 'Результатов не найдено.',
 
},
 
}
 
   
  +
local i18n = cfg.i18n
   
  +
-- ----------------------------------------------------------------------------
--
 
-- Helper/Utility functions
+
-- Helper functions
  +
-- ----------------------------------------------------------------------------
--
 
   
 
local h = {}
 
local h = {}
  +
  +
-- Lazy loading for Module:Item link
  +
function h.item_link(args)
  +
if not f_item_link then
  +
f_item_link = require('Module:Item link').item_link
  +
end
  +
return f_item_link(args)
  +
end
   
 
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
   
 
function h.query_weights(table_name, page_ids)
 
function h.query_weights(table_name, page_ids)
  +
return m_cargo.map_results_to_id{
results = cargo.query(
 
  +
results=m_cargo.query(
string.format('mods,%s', table_name),
 
string.format('mods._pageID,%s.tag,%s.weight', table_name, table_name),
+
{'mods', table_name},
{
+
{
where=page_ids,
+
'mods._pageID',
join=string.format('mods._pageID=%s._pageID', table_name),
+
table_name .. '.tag',
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
+
table_name .. '.weight'
limit=5000,
+
},
}
+
{
  +
where=page_ids,
)
 
  +
join=string.format('mods._pageID=%s._pageID', table_name),
if #results == 5000 then
 
  +
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
error('Hit maximum cargo results')
 
end
+
}
+
),
return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
+
field='mods._pageID',
  +
}
 
end
 
end
   
Строка 128: Строка 78:
 
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
   
  +
h.tbl = {}
function h.cargo_query(tpl_args)
 
  +
h.tbl.display = {}
  +
h.tbl.display.factory = {}
  +
function h.tbl.display.factory.value(args)
  +
-- Format options for each field:
  +
args.options = args.options or {}
  +
  +
-- Separator between fields:
  +
args.delimiter = args.delimiter or ', '
  +
  +
return function(tpl_args, tr, data, fields)
  +
local values = {}
  +
local fmt_values = {}
  +
  +
for index, field in ipairs(fields) do
  +
local value = {
  +
min=data[field],
  +
max=data[field],
  +
base=data[field],
  +
}
  +
if value.min then
  +
values[#values+1] = value.max
  +
local opts = args.options[index] or {}
  +
-- Global colour is set, no overrides.
  +
if args.color ~= nil or opts.color == nil then
  +
opts.no_color = true
  +
end
  +
fmt_values[#fmt_values+1] = m_util.html.format_value(nil, value, opts)
  +
end
  +
end
  +
  +
if #values == 0 then
  +
tr
  +
:wikitext(m_util.html.td.na())
  +
else
  +
local td = tr:tag('td')
  +
td
  +
:attr('data-sort-value', table.concat(values, args.delimiter))
  +
:wikitext(table.concat(fmt_values, args.delimiter))
  +
if args.color then
  +
td:attr('class', 'tc -' .. args.color)
  +
end
  +
end
  +
end
  +
end
  +
  +
function h.get_spawn_chance(args)
 
--[[
 
--[[
Returns a Cargo query of all the results.
+
Calculates the spawn chance of a set of mods that all have a
+
spawn weight.
  +
tpl_args to be added to the cargo table needs to prefixed with q_.
 
* tpl_args.q_*
 
 
 
]]
 
]]
  +
 
-- Parse query arguments
+
-- Get the table:
local query = {
+
local tbl = args['tbl']
  +
limit = 5000,
 
  +
-- Probabilities affecting the result besides the spawn weight:
offset = 0,
 
  +
local chance_multiplier = tonumber(args['chance_multiplier']) or 1
}
 
  +
for key, value in pairs(tpl_args) do
 
  +
-- Total number of outcomes.
if string.sub(key, 0, 2) == 'q_' then
 
  +
local N = 0
query[string.sub(key, 3)] = value
 
end
+
for i,_ in ipairs(tbl) do
  +
N = N + tbl[i]['spawn_weights.weight']
 
end
 
end
  +
 
  +
for i,_ in ipairs(tbl) do
-- Query cargo table. If there are too many results then repeat,
 
-- offset, re-query and add the remaining results:
+
-- Number of ways it can happen:
  +
local n = tbl[i]['spawn_weights.weight']
local results = {}
 
  +
repeat
 
local result = cargo.query(
+
-- Truncated value:
  +
tbl[i]['spawn_weights.chance'] = string.format(
query.tables,
 
query.fields,
+
"%0.2f%%",
query
+
n/N * chance_multiplier*100
 
)
 
)
  +
end
query.offset = query.offset + #result
 
   
  +
return tbl
for _,v in ipairs(result) do
 
results[#results + 1] = v
 
end
 
until #result < query.limit
 
 
return results
 
 
end
 
end
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
  +
-- Additional configuration
-- Template: Mod table
 
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
   
Строка 195: Строка 185:
 
},
 
},
 
},
 
},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, tr, data, fields)
 
local name
 
local name
 
if data['mods.name'] then
 
if data['mods.name'] then
Строка 202: Строка 192:
 
name = data['mods.id']
 
name = data['mods.id']
 
end
 
end
  +
 
tr
 
tr
 
:tag('td')
 
:tag('td')
Строка 213: Строка 204:
 
header = i18n.mod_table.domain,
 
header = i18n.mod_table.domain,
 
fields = {'mods.domain'},
 
fields = {'mods.domain'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, tr, data, fields)
local value = data['mods.domain']
+
local k = 'mods.domain'
tr
+
local i = tonumber(data[k])
:tag('td')
+
if m_game.constants.mod.domains[i] == nil then
:attr('data-sort-value', value)
+
error('Undefined Modifier Domain ['..i..'] needs to be added to Module:Game')
  +
end
:wikitext(m_game.constants.mod.domains[tonumber(value)]['short_upper'])
 
  +
data[k] = m_game.constants.mod.domains[i]['short_upper']
  +
h.tbl.display.factory.value({})(tpl_args, tr, data, fields)
 
end,
 
end,
 
order = 2000,
 
order = 2000,
Строка 227: Строка 220:
 
header = i18n.mod_table.generation_type,
 
header = i18n.mod_table.generation_type,
 
fields = {'mods.generation_type'},
 
fields = {'mods.generation_type'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, tr, data, fields)
local value = data['mods.generation_type']
+
local k = 'mods.generation_type'
tr
+
local i = tonumber(data[k])
:tag('td')
+
if m_game.constants.mod.generation_types[i] == nil then
:attr('data-sort-value', value)
+
error('Undefined Modifier Generation Type ['..i..'] needs to be added to Module:Game')
  +
end
:wikitext(m_game.constants.mod.generation_types[tonumber(value)]['short_upper'])
 
  +
data[k] = m_game.constants.mod.generation_types[i]['short_upper']
  +
h.tbl.display.factory.value{}(tpl_args, tr, data, fields)
 
end,
 
end,
 
order = 2001,
 
order = 2001,
Строка 241: Строка 236:
 
header = i18n.mod_table.mod_group,
 
header = i18n.mod_table.mod_group,
 
fields = {'mods.mod_group'},
 
fields = {'mods.mod_group'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.mod_group'])
 
end,
 
 
order = 2002,
 
order = 2002,
 
sort_type = 'text',
 
sort_type = 'text',
Строка 253: Строка 244:
 
header = i18n.mod_table.mod_type,
 
header = i18n.mod_table.mod_type,
 
fields = {'mods.mod_type'},
 
fields = {'mods.mod_type'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.mod_type'])
 
end,
 
 
order = 2003,
 
order = 2003,
 
sort_type = 'text',
 
sort_type = 'text',
Строка 265: Строка 252:
 
header = i18n.mod_table.required_level,
 
header = i18n.mod_table.required_level,
 
fields = {'mods.required_level'},
 
fields = {'mods.required_level'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.required_level'])
 
end,
 
 
order = 2004,
 
order = 2004,
 
},
 
},
Строка 276: Строка 259:
 
header = i18n.mod_table.labyrinth,
 
header = i18n.mod_table.labyrinth,
 
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]],
]], i18n.mod_table.normal_labyrinth, i18n.mod_table.cruel_labyrinth, i18n.mod_table.merciless_labyrinth, i18n.mod_table.eternal_labyrinth)},
+
i18n.mod_table.normal_labyrinth,
  +
i18n.mod_table.cruel_labyrinth,
display = function(tpl_args, frame, tr, data)
 
tr
+
i18n.mod_table.merciless_labyrinth,
:tag('td')
+
i18n.mod_table.eternal_labyrinth)
  +
},
:wikitext(data['labyrinth_text'])
 
  +
display = h.tbl.display.factory.value{},
end,
 
 
order = 2005,
 
order = 2005,
 
sort_type = 'text',
 
sort_type = 'text',
Строка 297: Строка 280:
 
header = i18n.mod_table.stat_text,
 
header = i18n.mod_table.stat_text,
 
fields = {'mods.stat_text'},
 
fields = {'mods.stat_text'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, tr, data, fields)
local text
+
local value
 
-- map display type shows this in another column, remove this text to avoid clogging up the list
 
-- map display type shows this in another column, remove this text to avoid clogging up the list
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
 
local texts = m_util.string.split(data['mods.stat_text'], '<br>')
 
local texts = m_util.string.split(data['mods.stat_text'], '<br>')
 
local out = {}
 
local out = {}
  +
 
 
local valid
 
local valid
 
for _, v in ipairs(texts) do
 
for _, v in ipairs(texts) do
Строка 313: Строка 296:
 
end
 
end
 
end
 
end
  +
 
 
if valid then
 
if valid then
 
table.insert(out, v)
 
table.insert(out, v)
 
end
 
end
 
end
 
end
  +
 
text = table.concat(out, '<br>')
+
value = table.concat(out, '<br>')
 
else
 
else
text = data['mods.stat_text']
+
value = data['mods.stat_text']
 
end
 
end
+
data['mods.stat_text'] = value
  +
h.tbl.display.factory.value{color='mod'}(tpl_args, tr, data, fields)
tr
 
:tag('td')
 
:wikitext(text)
 
 
end,
 
end,
 
order = 3000,
 
order = 3000,
 
sort_type = 'text',
 
sort_type = 'text',
 
},
 
},
 
 
{
 
{
 
arg = 'buff',
 
arg = 'buff',
 
header = i18n.mod_table.buff,
 
header = i18n.mod_table.buff,
 
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
 
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{delimiter=' '},
tr
 
:tag('td')
 
:wikitext(string.format('%s %s', data['mods.granted_buff_id'], data['mods.granted_buff_value']))
 
end,
 
 
order = 4000,
 
order = 4000,
 
sort_type = 'text',
 
sort_type = 'text',
Строка 348: Строка 324:
 
header = i18n.mod_table.granted_skill,
 
header = i18n.mod_table.granted_skill,
 
fields = {'mods.granted_skill'},
 
fields = {'mods.granted_skill'},
display = function(tpl_args, frame, tr, data)
+
display = h.tbl.display.factory.value{},
tr
 
:tag('td')
 
:wikitext(data['mods.granted_skill'])
 
end,
 
 
order = 4001,
 
order = 4001,
 
sort_type = 'text',
 
sort_type = 'text',
Строка 360: Строка 332:
 
header = i18n.mod_table.tags,
 
header = i18n.mod_table.tags,
 
fields = {'mods.tags'},
 
fields = {'mods.tags'},
display = function(tpl_args, frame, tr, data)
+
display = function(tpl_args, tr, data, fields)
tr
+
local k = 'mods.tags'
:tag('td')
+
data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ')
:wikitext(table.concat(m_util.string.split(data['mods.tags'], ','), ', '))
+
h.tbl.display.factory.value{}(tpl_args, tr, data, fields)
 
end,
 
end,
 
order = 5000,
 
order = 5000,
Строка 385: Строка 357:
 
mod_table.weights = {'spawn_weights', 'generation_weights'}
 
mod_table.weights = {'spawn_weights', 'generation_weights'}
   
  +
-- ----------------------------------------------------------------------------
function p.mod_table(frame)
 
  +
-- Main functions
  +
-- ----------------------------------------------------------------------------
  +
  +
local function _mod_table(tpl_args)
 
--[[
 
--[[
 
Creates a generic table for modifiers.
 
Creates a generic table for modifiers.
  +
 
 
Examples
 
Examples
 
--------
 
--------
Строка 400: Строка 376:
 
enchantment=1,
 
enchantment=1,
 
}
 
}
 
 
]]
 
]]
  +
 
 
tpl_args = getArgs(frame, {
 
parentFirst = true
 
})
 
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
Строка 427: Строка 396:
 
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
Строка 435: Строка 404:
 
end
 
end
 
end
 
end
  +
 
 
if enabled then
 
if enabled then
 
row_info.options = row_info.options or {}
 
row_info.options = row_info.options or {}
Строка 441: Строка 410:
 
end
 
end
 
end
 
end
  +
 
 
-- sort the rows
 
-- sort the rows
 
table.sort(row_infos, function (a, b)
 
table.sort(row_infos, function (a, b)
 
return (a.order or 0) < (b.order or 0)
 
return (a.order or 0) < (b.order or 0)
 
end)
 
end)
  +
 
-- Set tables
+
-- Set required and extra tables:
local tables = 'mods'
+
local tables = {'mods'}
if tpl_args.q_tables then
+
for _, v in ipairs(m_util.string.split_args(tpl_args.q_tables, {',%s*'})) do
tables = tables .. ',' .. tpl_args.q_tables
+
tables[#tables+1] = v
 
end
 
end
  +
 
  +
-- Set required and extra fields:
 
-- Set required fields
 
 
local fields = {
 
local fields = {
 
'mods._pageID',
 
'mods._pageID',
 
}
 
}
for _, rowinfo in ipairs(row_infos) do
+
for _, row_info in ipairs(row_infos) do
if type(rowinfo.fields) == 'function' then
+
if type(row_info.fields) == 'function' then
rowinfo.fields = rowinfo.fields()
+
row_info.fields = row_info.fields()
 
end
 
end
for index, field in ipairs(rowinfo.fields) do
+
for index, field in ipairs(row_info.fields) do
rowinfo.options[index] = rowinfo.options[index] or {}
+
row_info.options[index] = row_info.options[index] or {}
 
fields[#fields+1] = field
 
fields[#fields+1] = field
 
end
 
end
 
end
 
end
  +
tpl_args._extra_fields = m_util.string.split_args(tpl_args.q_fields, {',%s*'})
 
  +
for _, v in ipairs(tpl_args._extra_fields) do
-- Parse query arguments
 
  +
fields[#fields+1] = v
  +
end
  +
  +
-- Parse query arguments:
 
local query = {
 
local query = {
 
-- Workaround: fix duplicates
 
-- Workaround: fix duplicates
 
groupBy='mods._pageID',
 
groupBy='mods._pageID',
 
}
 
}
for key, value in pairs(tpl_args) do
+
for key, value in pairs(tpl_args) do
 
if string.sub(key, 0, 2) == 'q_' then
 
if string.sub(key, 0, 2) == 'q_' then
 
query[string.sub(key, 3)] = value
 
query[string.sub(key, 3)] = value
 
end
 
end
 
end
 
end
  +
 
  +
local results = m_cargo.query(tables, fields, query)
 
  +
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 #results == 0 then
 
if tpl_args.default ~= nil then
 
if tpl_args.default ~= nil then
Строка 498: Строка 460:
 
end
 
end
 
end
 
end
  +
 
-- this might be needed in other queries, currently not checking if it's actually needed
+
-- this might be needed in other queries, currently not checking if
-- because performance impact should be neglible
+
-- it's actually needed because performance impact should be neglible
 
local page_ids = {}
 
local page_ids = {}
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
page_ids[#page_ids+1] = string.format('mods._pageID="%s"', row['mods._pageID'])
+
page_ids[#page_ids+1] = string.format(
  +
'mods._pageID="%s"',
  +
row['mods._pageID']
  +
)
 
end
 
end
 
page_ids = table.concat(page_ids, ' OR ')
 
page_ids = table.concat(page_ids, ' OR ')
  +
 
 
local weights = {}
 
local weights = {}
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
Строка 513: Строка 478:
 
end
 
end
 
end
 
end
  +
 
 
local stats
 
local stats
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
Строка 520: Строка 485:
 
query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
 
query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
 
end
 
end
  +
 
local stat_results = cargo.query(
+
stats = m_cargo.map_results_to_id{
'mods,mod_stats',
+
results=m_cargo.query(
'mods._pageID,mod_stats.id,mod_stats.min,mod_stats.max',
+
{'mods', 'mod_stats'},
{
+
{
where=string.format('(%s) AND (%s)', page_ids, table.concat(query_stat_ids, ' OR ')),
+
'mods._pageID',
join='mods._pageID=mod_stats._pageID',
+
'mod_stats.id',
orderBy='mods.id ASC',
+
'mod_stats.min',
limit=5000,
+
'mod_stats.max',
}
+
},
)
+
{
  +
where=string.format(
if #stat_results == 5000 then
 
error('Hit maximum cargo results')
+
'(%s) AND (%s)',
end
+
page_ids,
  +
table.concat(query_stat_ids, ' OR ')
 
  +
),
stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
 
  +
join='mods._pageID=mod_stats._pageID',
  +
orderBy='mods.id ASC',
  +
}
  +
),
  +
field='mods._pageID'
  +
}
  +
 
-- In addition map stats to stat <-> min/max pairs
 
-- In addition map stats to stat <-> min/max pairs
 
for page_id, rows in pairs(stats) do
 
for page_id, rows in pairs(stats) do
 
local stat_id_map = {}
 
local stat_id_map = {}
 
for _, row in ipairs(rows) do
 
for _, row in ipairs(rows) do
stat_id_map[row['mod_stats.id']] = {min=tonumber(row['mod_stats.min']), max=tonumber(row['mod_stats.max'])}
+
stat_id_map[row['mod_stats.id']] = {
  +
min=tonumber(row['mod_stats.min']),
  +
max=tonumber(row['mod_stats.max'])
  +
}
 
end
 
end
 
stats[page_id] = stat_id_map
 
stats[page_id] = stat_id_map
 
end
 
end
 
end
 
end
  +
 
 
--
 
--
 
-- Display
 
-- Display
 
--
 
--
  +
 
 
-- Preformance optimization
 
-- Preformance optimization
if tpl_args.q_fields then
+
for index, field in ipairs(tpl_args._extra_fields) do
tpl_args._extra_fields = m_util.string.split(tpl_args.q_fields, ',')
+
field = m_util.string.split(field, '%s*=%s*')
  +
-- field[2] will be nil if there is no alias
for index, field in ipairs(tpl_args._extra_fields) do
 
field = m_util.string.split(field, '=')
+
tpl_args._extra_fields[index] = field[2] or field[1]
-- 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
 
end
  +
 
 
local tbl = mw.html.create('table')
 
local tbl = mw.html.create('table')
 
tbl:attr('class', 'wikitable sortable modifier-table')
 
tbl:attr('class', 'wikitable sortable modifier-table')
 
-- Header
 
-- Header
  +
 
 
local tr = tbl:tag('tr')
 
local tr = tbl:tag('tr')
  +
local display_fields = {}
for _, row_info in ipairs(row_infos) do
 
  +
for i, row_info in ipairs(row_infos) do
  +
for j, field in ipairs(row_info.fields) do
  +
-- Aliased name is used as keys in the results:
  +
field = m_util.string.split(field, '%s*=%s*')
  +
field = field[2] or field[1]
  +
  +
-- Make a new field table since mod_table.data will remain
  +
-- modified the 2nd time a function is run and then crash.
  +
if j == 1 then
  +
display_fields[i] = {}
  +
end
  +
display_fields[i][j] = field
  +
end
  +
 
tr
 
tr
 
:tag('th')
 
:tag('th')
Строка 574: Строка 558:
 
: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
Строка 584: Строка 568:
 
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
Строка 592: Строка 576:
 
end
 
end
 
end
 
end
  +
 
 
for _, field in ipairs(tpl_args._extra_fields) do
 
for _, field in ipairs(tpl_args._extra_fields) do
 
tr
 
tr
Строка 598: Строка 582:
 
: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 i, row_info in ipairs(row_infos) do
 
-- this has been cast from a function in an earlier step
 
-- this has been cast from a function in an earlier step
 
local display = true
 
local display = true
for index, field in ipairs(rowinfo.fields) do
+
for j, field in ipairs(display_fields[i]) do
 
-- this will bet set to an empty value not nil confusingly
 
-- this will bet set to an empty value not nil confusingly
if row[field] == '' then
+
if row[field] == nil or row[field] == '' then
if rowinfo.options[index].optional ~= true then
+
if row_info.options[j].optional ~= true then
 
display = false
 
display = false
 
break
 
break
Строка 619: Строка 603:
 
end
 
end
 
if display then
 
if display then
rowinfo.display(tpl_args, frame, tr, row, rowinfo.fields)
+
row_info.display(tpl_args, tr, row, display_fields[i])
 
else
 
else
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.type == 'map' then
 
if tpl_args.type == 'map' then
 
for stat_id, data in pairs(mod_table.stat_ids) do
 
for stat_id, data in pairs(mod_table.stat_ids) do
Строка 646: Строка 630:
 
end
 
end
 
end
 
end
  +
 
 
for _, key in ipairs(mod_table.weights) do
 
for _, key in ipairs(mod_table.weights) do
 
if tpl_args[key] then
 
if tpl_args[key] then
 
local weight_out = {}
 
local weight_out = {}
 
for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
 
for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
weight_out[#weight_out+1] = string.format('%s %s', wrow[key .. '.tag'], wrow[key .. '.weight'])
+
if wrow[key .. '.tag'] and wrow[key .. '.weight'] then
  +
weight_out[#weight_out+1] = string.format(
  +
'%s %s',
  +
wrow[key .. '.tag'],
  +
wrow[key .. '.weight']
  +
)
  +
end
 
end
 
end
  +
 
if #weight_out > 0 then
 
if #weight_out > 0 then
 
tr
 
tr
:tag('td')
+
:tag('td')
:wikitext(table.concat(weight_out, '<br>'))
+
:wikitext(table.concat(weight_out, '<br>'))
:done()
+
:done()
else
+
else
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
 
end
 
end
 
end
 
end
 
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
Строка 674: Строка 665:
 
end
 
end
 
end
 
end
  +
 
 
return tostring(tbl)
 
return tostring(tbl)
 
end
 
end
   
  +
local function _drop_down_table(tpl_args)
 
 
 
-- ---------------------------------------------------------------------
 
-- Modifier link
 
-- ---------------------------------------------------------------------
 
 
 
function p.modifier_link(frame)
 
 
--[[
 
--[[
  +
This function queries mods that can spawn on an item. It compares
Finds and links to a modifier in formatted form.
 
  +
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
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.
 
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'}
= p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
+
= p.drop_down_table{item='Stone Axe', header='Two Handed Axes'}
   
 
Accessories
 
Accessories
= p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
+
= p.drop_down_table{item='Amber Amulet', header='Amulets'}
  +
 
 
Jewels
 
Jewels
= p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
+
= p.drop_down_table{item='Cobalt Jewel', header='Jewels'}
  +
 
 
Armour
 
Armour
= p.drop_down_table{item = 'Plate Vest', header = 'Body armours'}
+
= p.drop_down_table{item='Plate Vest', header='Body armours'}
  +
 
 
Boots
 
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',
item_tags = 'axe, one_hand_weapon, onehand, weapon, default',
+
item_tags='axe, one_hand_weapon, onehand, weapon, default',
extra_item_tags = 'fishing_rod'
+
extra_item_tags='fishing_rod'
 
}
 
}
  +
 
 
= p.drop_down_table{
 
= p.drop_down_table{
item = 'Vaal Blade',
+
item='Vaal Blade',
 
}
 
}
  +
 
 
]]
 
]]
   
-- Get template arguments:
 
local tpl_args = getArgs(frame, {parentFirst=true})
 
local frame = m_util.misc.get_frame(frame)
 
 
 
-- Format the cargo query:
 
-- Format the cargo query:
 
local where
 
local where
Строка 1040: Строка 733:
 
{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])
Строка 1046: Строка 739:
 
end
 
end
 
end
 
end
  +
 
 
local item_info = m_cargo.query(
 
local item_info = m_cargo.query(
 
{'items'},
 
{'items'},
  +
{
{'items.name', 'items.tags', 'items.class', 'items.class_id', 'items.inventory_icon', 'items.html', 'items.release_version', 'items._pageName'},
 
  +
'items.name',
  +
'items.tags',
  +
'items.class_id',
  +
'items.inventory_icon',
  +
'items.html',
  +
'items.release_version',
  +
'items._pageName'
  +
},
 
{
 
{
 
where=where,
 
where=where,
 
groupBy='items._pageName',
 
groupBy='items._pageName',
orderBy = 'items.name, items.release_version DESC',
+
orderBy='items.name, items.release_version DESC',
 
}
 
}
 
)[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 value
 
local class_to_domain = {
 
local class_to_domain = {
['Флаконы жизни']=2,
+
['LifeFlask']=2,
['Флаконы маны']=2,
+
['ManaFlask']=2,
['Флаконы равновесия']=2,
+
['HybridFlask']=2,
['Особые флаконы']=2,
+
['UtilityFlask']=2,
['Важные особые флаконы']=2,
+
['UtilityFlaskCritical']=2,
['Карты']=5,
+
['Map']=5,
['Самоцветы']=10,
+
['Jewel']=10,
['Камни лиги']=12,
+
['Leaguestone']=12,
['Самоцвет Бездны']=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']] or 1
+
item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1
  +
 
 
-- Convert the mod domain number to understandable text:
 
-- 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
+
-- Format item tags:
  +
tpl_args.item_tags = m_util.string.split_args(
-- separated any more.
 
if tpl_args.item_tags ~= nil then
+
tpl_args.item_tags or item_info['items.tags'],
  +
{sep=',%s*'}
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'], ',')
+
for _,v in ipairs(m_util.string.split_args(tpl_args.extra_item_tags, {sep=',%s*'})) do
  +
tpl_args.item_tags[#tpl_args.item_tags+1] = v
end
 
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
 
end
  +
 
  +
-- Format extra fields:
  +
local extra_fields = m_util.string.split_args(tpl_args.extra_fields, {sep=',%s*'})
  +
  +
  +
-- Get tags that are appended to special items:
  +
local tags_cfg = m_game.constants.item.classes[item_info['items.class_id']].tags or {}
  +
 
-- Create drop down lists in these sections and query in these
 
-- Create drop down lists in these sections and query in these
 
-- generation types.
 
-- generation types.
  +
local section = {
 
local 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 section = {}
 
section = {
 
 
{
 
{
 
header = i18n.drop_down_table.prefix,
 
header = i18n.drop_down_table.prefix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
 
item_tag,
 
item_tag,
 
1,
 
1,
Строка 1111: Строка 815:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
 
{
 
{
 
header = i18n.drop_down_table.suffix,
 
header = i18n.drop_down_table.suffix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
 
item_tag,
 
item_tag,
 
2,
 
2,
Строка 1127: Строка 836:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
 
{
 
{
tags = {elder_tag},
+
tags = {tags_cfg.elder},
 
header = i18n.drop_down_table.elder_prefix,
 
header = i18n.drop_down_table.elder_prefix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, value)
 
return string.format(
 
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,
+
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.elder,
 
1,
 
1,
 
item_info['items.domain']
 
item_info['items.domain']
Строка 1144: Строка 858:
 
},
 
},
 
{
 
{
tags = {elder_tag},
+
tags = {tags_cfg.elder},
 
header = i18n.drop_down_table.elder_suffix,
 
header = i18n.drop_down_table.elder_suffix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, value)
 
return string.format(
 
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,
+
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.elder,
 
2,
 
2,
 
item_info['items.domain']
 
item_info['items.domain']
Строка 1156: Строка 875:
 
},
 
},
 
{
 
{
tags = {shaper_tag},
+
tags = {tags_cfg.shaper},
 
header = i18n.drop_down_table.shaper_prefix,
 
header = i18n.drop_down_table.shaper_prefix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, value)
 
return string.format(
 
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,
+
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.shaper,
 
1,
 
1,
 
item_info['items.domain']
 
item_info['items.domain']
Строка 1168: Строка 892:
 
},
 
},
 
{
 
{
tags = {shaper_tag},
+
tags = {tags_cfg.shaper},
 
header = i18n.drop_down_table.shaper_suffix,
 
header = i18n.drop_down_table.shaper_suffix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, value)
 
return string.format(
 
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,
+
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.shaper,
 
2,
 
2,
 
item_info['items.domain']
 
item_info['items.domain']
Строка 1181: Строка 910:
 
{
 
{
 
header = i18n.drop_down_table.delve_prefix,
 
header = i18n.drop_down_table.delve_prefix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
 
item_tag,
 
item_tag,
 
1,
 
1,
Строка 1191: Строка 925:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
 
{
 
{
 
header = i18n.drop_down_table.delve_suffix,
 
header = i18n.drop_down_table.delve_suffix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
 
item_tag,
 
item_tag,
 
2,
 
2,
Строка 1207: Строка 946:
 
)
 
)
 
end
 
end
  +
 
return table.concat(where, ' OR ')
+
return table.concat(where, ' OR ')
 
end,
 
end,
 
},
 
},
 
{
 
{
header = i18n.drop_down_table.corrupted,
+
tags = {tags_cfg.crusader},
where = function(tpl_args, frame, value)
+
header = i18n.drop_down_table.crusader_prefix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.crusader,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.crusader},
  +
header = i18n.drop_down_table.crusader_suffix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.crusader,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.eyrie},
  +
header = i18n.drop_down_table.eyrie_prefix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.eyrie,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.eyrie},
  +
header = i18n.drop_down_table.eyrie_suffix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.eyrie,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.basilisk},
  +
header = i18n.drop_down_table.basilisk_prefix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.basilisk,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.basilisk},
  +
header = i18n.drop_down_table.basilisk_suffix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.basilisk,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.adjudicator},
  +
header = i18n.drop_down_table.adjudicator_prefix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.adjudicator,
  +
1,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
tags = {tags_cfg.adjudicator},
  +
header = i18n.drop_down_table.adjudicator_suffix,
  +
where = function(tpl_args, value)
  +
return string.format(
  +
[[
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > '')
  +
]],
  +
tags_cfg.adjudicator,
  +
2,
  +
item_info['items.domain']
  +
)
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.veiled_prefix,
  +
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > ''
  +
AND mods.id LIKE "%%Master%%")
  +
]],
 
item_tag,
 
item_tag,
5,
+
1,
item_info['items.domain']
+
9
 
)
 
)
 
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.
 
 
},
 
},
 
{
 
{
header = i18n.drop_down_table.enchant,
+
header = i18n.drop_down_table.veiled_suffix,
where = function(tpl_args, frame, value)
+
where = function(tpl_args, 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" AND mods.generation_type=%s AND mods.domain=%s AND mods.stat_text IS NOT NULL)',
 
  +
(spawn_weights.tag="%s"
  +
AND mods.generation_type=%s
  +
AND mods.domain=%s
  +
AND mods.stat_text > ''
  +
AND mods.id LIKE "%%Master%%")
  +
]],
 
item_tag,
 
item_tag,
10,
+
2,
  +
9
  +
)
  +
end
  +
  +
return table.concat(where, ' OR ')
  +
end,
  +
},
  +
{
  +
header = i18n.drop_down_table.corrupted,
  +
where = function(tpl_args, 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 > '')
  +
]],
  +
item_tag,
  +
5,
 
item_info['items.domain']
 
item_info['items.domain']
 
)
 
)
 
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.
  +
is_implicit = true,
 
},
 
},
  +
-- Reduce template expansion size by disabling enchantments, there are
  +
-- other good pages with enchantment lists:
  +
-- {
  +
-- header = i18n.drop_down_table.enchant,
  +
-- where = function(tpl_args, 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 > '')
  +
-- ]],
  +
-- item_tag,
  +
-- 10,
  +
-- item_info['items.domain']
  +
-- )
  +
-- end
  +
  +
-- return table.concat(where, ' OR ')
  +
-- end,
  +
-- is_implicit = true,
  +
-- },
 
}
 
}
  +
 
 
-- Introductory text:
 
local out = {}
 
out[#out+1] = string.format(
 
'==%s== \n',
 
tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
 
)
 
out[#out+1] = 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] = 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
 
}
 
)
 
 
 
-- Save the original tag format:
 
-- Save the original tag format:
 
local item_tags_orig = {}
 
local item_tags_orig = {}
Строка 1274: Строка 1184:
 
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 tableIndex = -1
+
local extra_fieldss = {}
  +
local table_index_base = -1
 
for _, sctn in ipairs(section) do
 
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 continue = true
 
local current_tags
 
local current_tags
 
if sctn['tags'] then
 
if sctn['tags'] then
-- some item classes do not have shaper/elder items, so the table will not contain any tags:
+
-- some item classes do not have shaper/elder items, so the table
  +
-- will not contain any tags:
 
if #sctn['tags'] == 0 then
 
if #sctn['tags'] == 0 then
 
continue = false
 
continue = false
Строка 1296: Строка 1223:
 
end
 
end
 
end
 
end
  +
 
 
if continue then
 
if continue then
-- Create html container:
 
local container = mw.html.create('div')
 
:attr('style', 'vertical-align:top; display:inline-block;')
 
 
 
-- Cargo preparation:
 
-- Cargo preparation:
tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
+
local q_fields = {
  +
'mods._pageName',
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.tags, mods._pageName, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal, spawn_weights._pageName'
 
  +
'mods.name',
tpl_args.q_join = 'mods._pageName=spawn_weights._pageName, mods._pageName=mod_stats._pageName'
 
tpl_args.q_where = sctn['where'](tpl_args, frame, value)
+
'mods.id',
tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
+
'mods.required_level',
tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods.required_level, mods._pageName, spawn_weights.ordinal'
+
'mods.generation_type',
+
'mods.domain',
local extra_fields = {}
+
'mods.mod_group',
if tpl_args.extra_fields ~= nil then
+
'mods.mod_type',
extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
+
'mods.stat_text',
tpl_args.q_fields = string.format(
+
'mods.stat_text_raw',
'%s, %s',
+
'mods.tags',
tpl_args.q_fields,
+
'mod_stats.id',
table.concat(extra_fields, ', ')
+
'spawn_weights.tag',
)
+
'spawn_weights.weight',
  +
'spawn_weights.ordinal',
  +
'spawn_weights._pageName'
  +
}
  +
for i, v in ipairs(extra_fields) do
  +
q_fields[#q_fields+1] = v
 
end
 
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=m_cargo.query(
-- Create own list for spawn weights and group by page name:
+
{'mods', 'spawn_weights', 'mod_stats'},
local spawn_weights = {}
+
q_fields,
local results_unique = {}
+
{
local hash = {}
+
join = [[
  +
mods._pageName=spawn_weights._pageName,
for _,v in ipairs(results) do
 
  +
mods._pageName=mod_stats._pageName
if spawn_weights[v['mods._pageName']] == nil then
 
spawn_weights[v['mods._pageName']] = {}
+
]],
end
+
where = sctn['where'](tpl_args, value),
local n = #spawn_weights[v['mods._pageName']] or 0
+
groupBy = [[
spawn_weights[v['mods._pageName']][n+1] = v
+
mods._pageName,
+
spawn_weights.tag,
-- Get a sorted list that only has unique page names:
+
spawn_weights.weight
if hash[v['mods._pageName']] ~= true then
+
]],
results_unique[#results_unique+1] = v
+
orderBy = [[
hash[v['mods._pageName']] = true
+
mods.generation_type,
end
+
mods.mod_group,
end
+
mods.mod_type,
+
mods.required_level,
if #results_unique > 0 then
+
mods._pageName,
item_mods[sctn['header']] = {}
+
spawn_weights.ordinal
mod_group_counter[sctn['header']] = {}
+
]],
+
}
  +
),
  +
field='mods._pageName',
  +
keep_id_field=true,
  +
append_id_field=true,
  +
}
  +
  +
if #results > 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']
+
-- Loop through all the modifier tags until they match
 
if sctn['header'] == i18n.drop_down_table.elder_prefix then
 
-- error(mw.dumpObject(spawn_weights))
 
end
 
-- 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 = 'Глобальный'
+
local mod_scope = 'Глобальный'
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 = 'Локальный'
 
mod_scope = 'Локальный'
 
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
  +
-- Mod group counter:
 
  +
-- 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'
mod_group_counter[sctn['header']][group] = 1 + (mod_group_counter[sctn['header']][group] or 0)
+
for _, header in ipairs({sctn['header'], 'all'}) do
mod_group_counter['all'][group] = 1 + (mod_group_counter['all'][group] or 0)
+
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
 
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
-- Create the drop down table with <table></table>:
 
local headers = container
+
end
  +
end
if #item_mods[sctn['header']] > 0 then
 
  +
end
headers
 
  +
:tag('h3')
 
  +
:wikitext(string.format(
 
  +
--
'%s',
 
  +
-- Display the item mods
sctn['header']
 
  +
--
)
 
  +
  +
-- 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,
  +
h.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, table_index
  +
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
  +
local tbl_caption
  +
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']
 
)
 
)
:done()
+
) or ''
:done()
+
)
  +
  +
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
 
end
  +
 
local total_mod_groups = 0
+
-- Create a table index for handling the collapsible:
for k,v in pairs(mod_group_counter[sctn['header']]) do
+
table_index_base = table_index_base+1
total_mod_groups = 1+total_mod_groups
+
if table_index_mod_group ~= nil then
  +
table_index = table_index_mod_group
  +
else
  +
table_index = table_index_base
 
end
 
end
  +
 
-- Loop through and add all matching mods to the <table>.
+
-- Add class and style to the <table>:
local tbl, last_group, last_type
+
tbl = container:tag('table')
for _, rows in ipairs(item_mods[sctn['header']]) do
+
tbl
+
:attr('class', 'mw-collapsible mw-collapsed')
-- If the last mod group is different to the current
+
:attr('style',
-- mod group then assume the mod isn't related and start
+
'text-align:left; line-height:1.60em; width:810px;'
-- a new drop down list, if there's only one mod group
+
)
-- then use mod type instead:
+
:tag('th')
  +
:attr('class',
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
+
string.format('mw-customtoggle-%s', table_index)
-- multiple mod types within the same mod group:
+
)
local count = {}
+
:attr('style',
for _, n in ipairs(item_mods[sctn['header']]) do
+
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
+
)
-- If the mod has the same mod group, then add
+
:attr('colspan', '3' .. #extra_fields)
-- the mod type to the counter. Only unique mod
+
:wikitext(tbl_caption)
-- types matter so the number is just a dummy
+
:done()
-- value:
+
:done()
  +
end
if n['mods.mod_group'] == rows['mods.mod_group'] then
 
  +
count[n['mods.mod_type']] = 1
 
end
+
-- If the mod has no name then use the mod type:
end
+
local mod_name = rows['mods.name'] or ''
+
if mod_name == '' then
-- Calculate how many unique mod types with the
+
mod_name = rows['mods.mod_type']
-- same mod group there are:
+
end
  +
local number_of_mod_types = 0
 
for _ in pairs(count) do
+
-- Check if there are any extra properties to show in
number_of_mod_types = number_of_mod_types + 1
+
-- the drop down list and then add a cell for that,
end
+
-- add this node at the end of the table row:
+
local td = mw.html.create('td')
  +
if extra_fields ~= nil then
-- If there are multiple unique mod types with the
 
  +
for _, extra_field in ipairs(extra_fields) do
-- same mod group then change the style of the drop
 
-- down list to indicate it, if there's only one
+
td
-- mod group in the generation type then ignore it:
+
:attr('width', '*')
if number_of_mod_types > 1 and total_mod_groups > 1 then
+
:wikitext(string.format(
tbl_caption = string.format(
+
'%s:&nbsp;%s ',
'%s',
+
extra_field,
m_util.html.poe_color(
+
rows[extra_field] or ''
'mod',
 
string.format(
 
'%s %s',
 
i18n.drop_down_table.mod_group,
 
rows['mods.mod_group']
 
)
 
) or ''
 
 
)
 
)
  +
)
  +
:done()
  +
end
  +
end
   
else
+
-- Style mods.tags:
tbl_caption = string.format(
+
local mods_tags = table.concat(
'%s (%s)',
+
m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}),
m_util.html.poe_color(
+
', '
'mod',
+
)
h.header(rows['mods.stat_text'])
+
if mods_tags ~= '' then
) or '',
+
mods_tags = m_util.html.tooltip('*', mods_tags)
rows['mods.scope']
+
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;')
 
)
 
)
end
+
)
+
:done()
-- Add class and style to the <table>:
+
:tag('td')
tableIndex = tableIndex+1
+
:attr('width', '1')
tbl = container:tag('table')
+
:wikitext(
tbl
+
string.format(
:attr('class', 'mw-collapsible mw-collapsed')
+
'%s&nbsp;%s',
:attr('style',
+
m_game.level_requirement['short_upper']
'text-align:left; line-height:1.60em; width:810px;'
+
:gsub('%s', '&nbsp;'),
  +
rows['mods.required_level']
 
)
 
)
:tag('th')
+
)
:attr('class',
+
:done()
string.format(
+
:tag('td')
'mw-customtoggle-%s',
+
:attr('width', '*')
tableIndex
+
:wikitext(
)
+
string.format(
)
+
'%s%s',
:attr('style',
+
m_util.html.poe_color(
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
+
'mod',
)
+
rows['mods.stat_text']
:attr('colspan', '3' .. #extra_fields)
+
:gsub('<br>', ', ')
:wikitext(tbl_caption)
+
:gsub('<br />', ' ')
:done()
+
) or '',
:done()
+
mods_tags
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',
 
tableIndex
 
)
 
 
)
 
)
: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()
 
:done()
+
:node(td)
-- Save the last mod group for later comparison:
+
:done()
last_group = rows['mods.mod_group']
+
:done()
  +
last_type = rows['mods.mod_type']
 
end
+
-- Save the last mod group for later comparison:
end
+
last_group = rows['mods.mod_group']
+
last_type = rows['mods.mod_type']
out[#out+1] = tostring(container)
 
 
end
 
end
  +
out[#out+1] = tostring(container)
 
end
 
end
  +
 
  +
-- Outro text:
  +
out[#out+1] = '<br>'
  +
out[#out+1] = m_util.html.poe_color(
  +
'normal',
  +
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
  +
)
  +
 
return table.concat(out,'')
 
return table.concat(out,'')
 
end
 
end
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
-- Debug functions
+
-- Exported functions
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
   
  +
local p = {}
  +
  +
--
  +
-- Template:Modifier table
  +
--
  +
p.mod_table = m_util.misc.invoker_factory(_mod_table, {
  +
parentFirst = true,
  +
})
  +
  +
--
  +
-- Template:Item modifiers
  +
--
  +
p.drop_down_table = m_util.misc.invoker_factory(_drop_down_table, {
  +
parentFirst = true,
  +
})
  +
  +
--
  +
-- Debug
  +
--
 
p.debug = {}
 
p.debug = {}
 
function p.debug.tbl_data(tbl)
 
function p.debug.tbl_data(tbl)
 
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
Строка 1628: Строка 1642:
 
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
 
-- ----------------------------------------------------------------------------
 
--
 
-- ----------------------------------------------------------------------------
 
   
 
return p
 
return p

Версия от 16:09, 24 января 2022

Template info icon Документация модуля[просмотр] [править] [история] [очистить]

Реализует {{Modifier table}} и {{Item modifiers}}.

-------------------------------------------------------------------------------
-- 
--                           Module:Modifier table
-- 
-- Module responsible for displaying modifiers in various ways. Implements 
-- Template:Modifier table and Template:Item modifiers
-------------------------------------------------------------------------------

require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Modifier table')

local m_game = mw.loadData('Module:Game')

-- Lazy loading
local f_item_link -- require('Module:Item link').item_link

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Modifier table/config/sandbox') or mw.loadData('Module:Modifier table/config')

local i18n = cfg.i18n

-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------

local h = {}

-- Lazy loading for Module:Item link
function h.item_link(args)
    if not f_item_link then
        f_item_link = require('Module:Item link').item_link
    end
    return f_item_link(args)
end

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

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

   return s
end

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

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

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

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

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

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

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

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

    ]]

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

    -- Probabilities affecting the result besides the spawn weight:
    local chance_multiplier = tonumber(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

-- ----------------------------------------------------------------------------
-- Additional configuration
-- ----------------------------------------------------------------------------

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, tr, data, fields)
            local name
            if data['mods.name'] then
                name = data['mods.name']
            else
                name = data['mods.id']
            end

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

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

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

                value = table.concat(out, '<br>')
            else
                value = data['mods.stat_text']
            end
            data['mods.stat_text'] = value
            h.tbl.display.factory.value{color='mod'}(tpl_args, tr, data, fields)
        end,
        order = 3000,
        sort_type = 'text',
    },
    {
        arg = 'buff',
        header = i18n.mod_table.buff,
        fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
        display = h.tbl.display.factory.value{delimiter=' '},
        order = 4000,
        sort_type = 'text',
    },
    {
        arg = {'skill', 'granted_skill'},
        header = i18n.mod_table.granted_skill,
        fields = {'mods.granted_skill'},
        display = h.tbl.display.factory.value{},
        order = 4001,
        sort_type = 'text',
    },
    {
        arg = {'tags'},
        header = i18n.mod_table.tags,
        fields = {'mods.tags'},
        display = function(tpl_args, tr, data, fields)
            local k = 'mods.tags'
            data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ')
            h.tbl.display.factory.value{}(tpl_args, tr, data, fields)
        end,
        order = 5000,
        sort_type = 'text',
    },
}
mod_table.stat_ids = {
    ['map_item_drop_quantity_+%'] = {
        header = i18n.mod_table.iiq,
        pattern = '%d+%% увеличение количества найденных предметов в этой области',
    },
    ['map_item_drop_rarity_+%'] = {
        header = i18n.mod_table.iir,
        pattern = '%d+%% повышение редкости найденных предметов в этой области',
    },
    ['map_pack_size_+%'] = {
        header = i18n.mod_table.pack_size,
        pattern = '%+%d+%% увеличение размера групп монстров',
    }
}
mod_table.weights = {'spawn_weights', 'generation_weights'}

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _mod_table(tpl_args)
    --[[
    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,
    }
    ]]

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

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

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

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

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

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

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

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

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

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

    if #results == 0 then
        if tpl_args.default ~= nil then
            return tpl_args.default
        else
            return 'Результатов не найдено'
        end
    end

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

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

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

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

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

    --
    -- Display
    --

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

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

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

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

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

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

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

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

    -- Body

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

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

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

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

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

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

    return tostring(tbl)
end

local function _drop_down_table(tpl_args)
    --[[
    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',
    }

    ]]

    -- 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 value
    local class_to_domain = {
        ['LifeFlask']=2,
        ['ManaFlask']=2,
        ['HybridFlask']=2,
        ['UtilityFlask']=2,
        ['UtilityFlaskCritical']=2,
        ['Map']=5,
        ['Jewel']=10,
        ['Leaguestone']=12,
        ['AbyssJewel']=13,
    }
    -- Get the domain, if it's not defined in the table assume it's
    -- in the item domain.
    item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1

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

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

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


    -- Get tags that are appended to special items:
    local tags_cfg = m_game.constants.item.classes[item_info['items.class_id']].tags or {}

    -- Create drop down lists in these sections and query in these
    -- generation types.
    local section = {
        {
            header = i18n.drop_down_table.prefix,
            where = function(tpl_args, 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 > '')
                        ]],
                        item_tag,
                        1,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.suffix,
            where = function(tpl_args, 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 > '')
                        ]],
                        item_tag,
                        2,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {tags_cfg.elder},
            header = i18n.drop_down_table.elder_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.elder,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.elder},
            header = i18n.drop_down_table.elder_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.elder,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.shaper},
            header = i18n.drop_down_table.shaper_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.shaper,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.shaper},
            header = i18n.drop_down_table.shaper_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.shaper,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.delve_prefix,
            where = function(tpl_args, 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 > '')
                        ]],
                        item_tag,
                        1,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.delve_suffix,
            where = function(tpl_args, 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 > '')
                        ]],
                        item_tag,
                        2,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {tags_cfg.crusader},
            header = i18n.drop_down_table.crusader_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.crusader,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.crusader},
            header = i18n.drop_down_table.crusader_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.crusader,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.eyrie},
            header = i18n.drop_down_table.eyrie_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.eyrie,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.eyrie},
            header = i18n.drop_down_table.eyrie_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.eyrie,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.basilisk},
            header = i18n.drop_down_table.basilisk_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.basilisk,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.basilisk},
            header = i18n.drop_down_table.basilisk_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.basilisk,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.adjudicator},
            header = i18n.drop_down_table.adjudicator_prefix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.adjudicator,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {tags_cfg.adjudicator},
            header = i18n.drop_down_table.adjudicator_suffix,
            where = function(tpl_args, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text > '')
                    ]],
                    tags_cfg.adjudicator,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.veiled_prefix,
            where = function(tpl_args, 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 > ''
                            AND mods.id LIKE "%%Master%%")
                        ]],
                        item_tag,
                        1,
                        9
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.veiled_suffix,
            where = function(tpl_args, 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 > ''
                            AND mods.id LIKE "%%Master%%")
                        ]],
                        item_tag,
                        2,
                        9
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.corrupted,
            where = function(tpl_args, 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 > '')
                        ]],
                        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,
        },
        -- Reduce template expansion size by disabling enchantments, there are
        -- other good pages with enchantment lists:
        -- {
            -- header = i18n.drop_down_table.enchant,
            -- where = function(tpl_args, 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 > '')
                        -- ]],
                        -- item_tag,
                        -- 10,
                        -- item_info['items.domain']
                    -- )
                -- end

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

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

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

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

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

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

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

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

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

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

                            -- Assume that the mod is global then go through
                            -- all the stat ids and check if any of the
                            -- stats are local:
                            local mod_scope = 'Глобальный'
                            for _, vv in ipairs(results[id]) do
                                if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                    mod_scope = 'Локальный'
                                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,
        h.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, table_index
        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
                local tbl_caption
                if number_of_mod_types > 1 and total_mod_groups > 1 then
                    table_index_mod_group = table.concat(
                        {string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
                        ''
                    )

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

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

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

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

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

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

            -- Style mods.tags:
            local mods_tags = table.concat(
                m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}),
                ', '
            )
            if mods_tags ~= '' then
                mods_tags = m_util.html.tooltip('*', mods_tags)
            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

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

-- 
-- Template:Modifier table
-- 
p.mod_table = m_util.misc.invoker_factory(_mod_table, {
    parentFirst = true,
})

-- 
-- Template:Item modifiers
-- 
p.drop_down_table = m_util.misc.invoker_factory(_drop_down_table, {
    parentFirst = true,
})

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