Path of Exile Wiki

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

ПОДРОБНЕЕ

Path of Exile Wiki
Регистрация
(Отмена правки 397288, сделанной Ruba159753 (обсуждение))
Метка: отмена
Magiczocker (обсуждение | вклад)
м (sync)
 
(не показана 41 промежуточная версия 4 участников)
Строка 1: Строка 1:
  +
-------------------------------------------------------------------------------
-- Item table
 
--
 
-- Creates various item tables from cargo queries.
 
 
--
 
--
  +
-- Module:Item table
 
--
 
--
  +
-- This module implements Template:Item table and other templates that query
  +
-- and display lists of items.
  +
-------------------------------------------------------------------------------
  +
 
-- Todo list
 
-- Todo list
 
-- ---------
 
-- ---------
-- * Handle table columns that can have multiple cargo rows, preferably
+
-- * Handle table columns that can have multiple cargo rows, preferably
 
-- in one or two queries. Not per column AND row.
 
-- in one or two queries. Not per column AND row.
-- * Handle template include size? Remove item link when getting close
+
-- * Handle template include size? Remove item link when getting close
-- to the limit?
+
-- to the limit?
-- * Add a wrapper around f_item_link to be able to disable all
+
-- * Add a wrapper around f_item_link to be able to disable all
 
-- hoverboxes, to avoid running into template expansion size issues.
 
-- hoverboxes, to avoid running into template expansion size issues.
   
  +
require('Module:No globals')
 
  +
local getArgs = require('Module:Arguments').getArgs
-- ----------------------------------------------------------------------------
 
-- Imports
 
-- ----------------------------------------------------------------------------
 
 
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 f_skill_link = require('Module:Skill link').skill_link
 
 
local m_cargo = require('Module:Cargo')
 
local m_cargo = require('Module:Cargo')
   
  +
local m_game = mw.loadData('Module:Game')
-- ----------------------------------------------------------------------------
 
-- Globals
 
-- ----------------------------------------------------------------------------
 
   
  +
-- Should we use the sandbox version of our submodules?
local c = {}
 
  +
local use_sandbox = m_util.misc.maybe_sandbox('Item table')
c.query_default = 50
 
  +
c.query_max = 300
 
  +
-- Lazy loading
  +
local f_item_link -- require('Module:Item link').item_link
  +
local f_skill_link -- require('Module:Skill link').skill_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:Item table/config/sandbox') or mw.loadData('Module:Item table/config')
  +
  +
local i18n = cfg.i18n
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
  +
-- Helper functions
-- Strings
 
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
-- This section contains strings used by this module.
 
-- Add new strings here instead of in-code directly, this will help other
 
-- people to correct spelling mistakes easier and help with translation to
 
-- other PoE wikis.
 
   
local i18n = {
+
local h = {}
categories = {
+
h.string = {}
-- maintenance cats
 
query_limit = 'Item tables hitting query limit',
 
query_hard_limit = 'Item tables hitting hard query limit',
 
no_results = 'Таблицы предметов без результатов',
 
},
 
 
-- Used by the item table
 
item_table = {
 
item = 'Предмет',
 
skill_gem = 'Камень умений',
 
   
  +
function h.string.format(str, vars)
physical_dps = m_util.html.abbr('фУВС', 'физический урон в секунду'),
 
  +
--[[
fire_dps = m_util.html.abbr('Fire УВС', 'урона от огня в секунду'),
 
  +
Allow string replacement using named arguments.
cold_dps = m_util.html.abbr('Cold УВС', 'урона от холода в секунду'),
 
lightning_dps = m_util.html.abbr('Light. УВС', 'урона от молнии в секунду'),
 
chaos_dps = m_util.html.abbr('Chaos УВС', 'урона хаосом в секунду'),
 
elemental_dps = m_util.html.abbr('сУВС', 'урон от стихий (т.е огонь/холод/молния) в секунду'),
 
poison_dps = m_util.html.abbr('Poison УВС', 'урон от яда (т.е. физический/хаос) в секунду'),
 
dps = m_util.html.abbr('УВС', 'общий урон (т.е. физический/огонь/холод/молния/хаос) в секунду'),
 
base_item = 'Базовый<br>предмет',
 
metadata_id = 'Metadata ID',
 
item_class = 'Тип<br>предмета',
 
essence_level = 'Уровень<br>сущности',
 
drop_level = 'Drop<br>Level',
 
release_version = 'Release<br>Version',
 
removal_version = 'Removal<br>Version',
 
version_link = '[[Версия %s|%s]]',
 
drop_enabled = m_util.html.abbr('Drop<br>Enabled', 'If an item is drop disabled, it can not be normally obtained, but still may be available under specific conditions (like trading via standard league or limited time events'),
 
drop_leagues = 'Drop Leagues',
 
drop_areas = 'Области выпадения',
 
drop_text = 'Дополнительные<br>ограничения выпадения',
 
stack_size = 'Размер<br>стопки',
 
stack_size_currency_tab = m_util.html.abbr('Размер<br>стопки<br>во вкладке', 'Размер стопки в валютной вкладке'),
 
armour = m_util.html.abbr('Бр', 'Броня'),
 
evasion = m_util.html.abbr('Укл', 'Уклонение'),
 
energy_shield = m_util.html.abbr('Эщ', 'Энерг. щит'),
 
block = m_util.html.abbr('Блок', 'Шанс заблокировать'),
 
damage = m_util.html.abbr('Урон', 'Урон с цветовой кодировкой'),
 
attacks_per_second = m_util.html.abbr('АВС', 'Атак в секунду'),
 
local_critical_strike_chance = m_util.html.abbr('Крит', 'Локальный шанс критического удара оружием'),
 
flask_life = m_util.html.abbr('Здоровье', 'Количество здоровья, регенерируемое за время действия флакона'),
 
flask_mana = m_util.html.abbr('Мана', 'Количество маны, регенерируемое за время действия флакона'),
 
flask_duration = 'Длительность',
 
flask_charges_per_use = m_util.html.abbr('Расход', 'Количество зарядов, расходуемых при использовании'),
 
flask_maximum_charges = m_util.html.abbr('Вместимость', 'Максимальное количество зарядов флакона'),
 
item_limit = 'Максимум',
 
jewel_radius = 'Радиус',
 
map_tier = 'Уровень<br>карты',
 
map_level = 'Уровень<br>области',
 
map_guild_character = m_util.html.abbr('Символ', 'Символ для тега гильдии'),
 
master_level_requirement = '[[Image:Level up icon small.png‎|link=|Уровень мастера]]',
 
master = 'Мастер',
 
master_favour_cost = 'Цена<br>расположения',
 
variation_count = 'Изменений',
 
buff_effects = 'Buff Effects',
 
stats = 'Свойства',
 
quality_stats = 'Свойство за 1% качества',
 
effects = 'Эффект(-ы)',
 
incubator_effect = 'Эффект инкубатора',
 
flavour_text = 'Сопроводительный текст',
 
prediction_text = 'Prediction',
 
help_text = 'Вспомогательный текст',
 
seal_cost = m_util.html.abbr('Стоимость<br>печати', 'Количество Серебряных монет, необходимых для запечатывания пророчества'),
 
objective = 'Задача',
 
reward = 'Награда',
 
buff_icon = 'Изображение<br>эффекта',
 
quest_name = 'Задание',
 
quest_act = 'Quest<br>Act',
 
purchase_costs = m_util.html.abbr('Purchase Cost', 'Cost of purchasing an item of this type at NPC vendors. This does not indicate whether NPCs actually sell the item.'),
 
sell_price = m_util.html.abbr('Sell Price', 'Items or currency received when selling this item at NPC vendors. Certain vendor recipes may override this value.'),
 
sextant = m_util.html.abbr('Секстант', 'Карты в радиусе действия.'),
 
boss_name = 'Босс',
 
boss_number = 'Количество боссов',
 
legacy = m_util.html.abbr('Устаревшие свойства', 'Сравните устаревшие варианты с текущими.&#10;• Яркий текст указывает свойства, которые отличаются от последнего варианта.&#10;• Зачеркнутый текст указывает свойства, которые не существуют в устаревших вариантах.'),
 
granted_skills = 'Даруемые умения',
 
granted_skills_level_label = 'уровня',
 
granted_skills_level_pattern = '(%d+)%s*{granted_skills_level_label}',
 
granted_skills_level_format = ' {level_number} {granted_skills_level_label}',
 
granted_skills_skill_output_format = '{sl}{level}',
 
granted_skills_gem_output_format = '{il}{level}',
 
alternate_art = 'Alternate<br>Arts',
 
 
-- Skills
 
support_gem_letter = m_util.html.abbr('L', 'Support gem letter.'),
 
skill_icon = 'Изображение',
 
description = 'Описание',
 
skill_critical_strike_chance = m_util.html.abbr('Крит', 'Шанс критического удара'),
 
cast_time = m_util.html.abbr('Время<br>применения', 'Время применения умения в секундах'),
 
attack_speed_multiplier = m_util.html.abbr('ASPD', 'Множитель скорости атаки'),
 
damage_effectiveness = m_util.html.abbr('Эффект.<br>добавл.<br>урона', 'Эффективность добавленного урона'),
 
mana_cost_multiplier = m_util.html.abbr('МРМ', 'Множитель расхода маны - missing values indicate it changes on gem level'),
 
mana_cost = m_util.html.abbr('Мана', 'Расход маны'),
 
reserves_mana_suffix = m_util.html.abbr('У', 'маны удерживается'),
 
vaal_souls_requirement = m_util.html.abbr('Души', 'Требуемое количество душ (1.5x in part 2, 2x in maps)'),
 
stored_uses = m_util.html.abbr('Заряды', 'Максимальное количество встроенных применений'),
 
primary_radius = m_util.html.abbr('Р1', 'Основной радиус'),
 
secondary_radius = m_util.html.abbr('Р2', 'Вторичный радиус'),
 
tertiary_radius = m_util.html.abbr('Р3', 'Третичный радиус'),
 
vendor_rewards = m_util.html.abbr('Награда торговца', 'Можно приобрести у торговцев после выполнения заданий'),
 
vendor_rewards_row_format = '[[Акт %s]] после выполнения [[%s]] который выдаёт [[%s]] покупка доступна: %s.',
 
vendor_rewards_any_classes = 'любым персонажем',
 
quest_rewards = m_util.html.abbr('Награды за задание', 'Награды после завершения задания'),
 
quest_rewards_row_format = '[[Акт %s]] - выполните задание [[%s]], используется %s.',
 
quest_rewards_any_classes = 'любым персонажем',
 
},
 
 
 
  +
TODO:
prophecy_description = {
 
  +
* Support %d ?
objective = 'Задача',
 
  +
* Support 0.2f ?
reward = 'Награда',
 
},
 
 
item_disambiguation = {
 
original='первоначальный вариант',
 
drop_enabled='текущий вариант,',
 
drop_disabled='устаревший вариант,',
 
known_release = ' который был введён в дополнении [[%s|%s]]',
 
list_pattern='%s — %s%s.'
 
},
 
   
errors = {
+
Parameters
  +
----------
generic_argument_parameter = 'Unrecognized %s parameter "%s"',
 
  +
str : String to replace.
invalid_item_table_mode = 'Invalid mode for item table',
 
  +
vars : Table of arguments.
},
 
}
 
   
  +
Examples
  +
--------
  +
= h.string.format('{foo} is {bar}.', {foo='Dinner', bar='nice'})
   
  +
References
-- ----------------------------------------------------------------------------
 
  +
----------
-- Helper & utility functions
 
  +
http://lua-users.org/wiki/StringInterpolation
-- ----------------------------------------------------------------------------
 
  +
]]
   
  +
if not vars then
local h = {}
 
  +
vars = str
  +
str = vars[1]
  +
end
  +
  +
return (string.gsub(str, "({([^}]+)})",
  +
function(whole, i)
  +
return vars[i] or whole
  +
end))
  +
end
  +
  +
-- 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
  +
  +
-- Lazy loading for Module:Skill link
  +
function h.skill_link(args)
  +
if not f_skill_link then
  +
f_skill_link = require('Module:Skill link').skill_link
  +
end
  +
return f_skill_link(args)
  +
end
   
 
function h.na_or_val(tr, value, func)
 
function h.na_or_val(tr, value, func)
Строка 192: Строка 110:
 
h.tbl = {}
 
h.tbl = {}
   
function h.tbl.range_fields(field)
+
function h.tbl.range_fields(args)
  +
local suffixes = {'maximum', 'text', 'colour'}
  +
if args.full then
  +
suffixes[#suffixes+1] = 'minimum'
  +
end
  +
 
return function()
 
return function()
 
local fields = {}
 
local fields = {}
  +
local inner = function (field)
for _, partial_field in ipairs({'maximum', 'text', 'colour'}) do
 
fields[#fields+1] = string.format('%s_range_%s', field, partial_field)
+
for _, partial_field in ipairs(suffixes) do
  +
fields[#fields+1] = string.format('%s_range_%s', field, partial_field)
  +
end
  +
end
  +
  +
if type(args.field) == 'table' then
  +
for _, field in ipairs(args.field) do
  +
inner(field)
  +
end
  +
else
  +
inner(args.field)
 
end
 
end
 
return fields
 
return fields
Строка 233: Строка 166:
 
local fmt_values = {}
 
local fmt_values = {}
 
local sdata = data2.skill_levels[data['items._pageName']]
 
local sdata = data2.skill_levels[data['items._pageName']]
  +
 
 
for index, field in ipairs(fields) do
 
for index, field in ipairs(fields) do
 
local value = {
 
local value = {
Строка 254: Строка 187:
 
end
 
end
 
end
 
end
  +
 
 
if #values == 0 then
 
if #values == 0 then
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
Строка 277: Строка 210:
 
:attr('class', 'tc -' .. (data[string.format('%s_range_colour', args.field)] or 'default'))
 
:attr('class', 'tc -' .. (data[string.format('%s_range_colour', args.field)] or 'default'))
 
:wikitext(data[string.format('%s_range_text', args.field)])
 
:wikitext(data[string.format('%s_range_text', args.field)])
  +
:done()
  +
end
  +
end
  +
  +
function h.tbl.display.factory.range_composite(args)
  +
-- division by default
  +
if args.func == nil then
  +
args.func = function (a, b)
  +
if b == 0 then
  +
return 'fail'
  +
end
  +
return a / b
  +
end
  +
end
  +
  +
return function(tr, data, fields)
  +
local field = {}
  +
for i=1, 2 do
  +
local fieldn = args['field' .. i]
  +
field[i] = {
  +
min = tonumber(data[string.format('%s_range_minimum', fieldn)]) or 0,
  +
max = tonumber(data[string.format('%s_range_maximum', fieldn)]) or 0,
  +
color = data[string.format('%s_range_colour', fieldn)] or 'default',
  +
}
  +
end
  +
  +
field.min = args.func(field[1].min, field[2].min)
  +
field.max = args.func(field[1].max, field[2].max)
  +
  +
if field.min == 'fail' or field.max == 'fail' then
  +
field.text = ''
  +
field.color = 'default'
  +
else
  +
for i=1, 2 do
  +
if field[i].color ~= 'default' then
  +
field.color = field[i].color
  +
break
  +
end
  +
end
  +
if field.min == field.max then
  +
field.text = string.format('%.2f', field.min)
  +
else
  +
field.text = string.format('(%.2f-%.2f)', field.min, field.max)
  +
end
  +
end
  +
  +
tr
  +
:tag('td')
  +
:attr('data-sort-value', field.max)
  +
:attr('class', 'tc -' .. field.color)
  +
:wikitext(field.text)
 
:done()
 
:done()
 
end
 
end
Строка 292: Строка 276:
 
end
 
end
 
return value
 
return value
  +
end
  +
end
  +
  +
function h.tbl.display.factory.atlas_tier(args)
  +
args = args or {}
  +
return function (tr, data)
  +
for i=0,4 do
  +
local t = tonumber(data['atlas_maps.map_tier' .. i])
  +
  +
if t == 0 then
  +
tr
  +
:tag('td')
  +
:attr('table-sort-value', 0)
  +
:attr('class', 'table-cell-xmark')
  +
:wikitext('✗')
  +
else
  +
if args.is_level then
  +
t = t + 67
  +
end
  +
tr
  +
:tag('td')
  +
:attr('class', 'tc -value')
  +
:wikitext(t)
  +
:done()
  +
end
  +
end
 
end
 
end
 
end
 
end
Строка 328: Строка 338:
 
order = 1001,
 
order = 1001,
 
sort_type = 'text',
 
sort_type = 'text',
  +
},
  +
{
  +
arg = 'rarity',
  +
header = i18n.item_table.rarity,
  +
fields = {'items.rarity'},
  +
display = h.tbl.display.factory.value{},
  +
order = 1002,
  +
},
  +
{
  +
arg = 'rarity_id',
  +
header = i18n.item_table.rarity_id,
  +
fields = {'items.rarity_id'},
  +
display = h.tbl.display.factory.value{},
  +
order = 1003,
 
},
 
},
 
{
 
{
Строка 334: Строка 358:
 
fields = {'items.metadata_id'},
 
fields = {'items.metadata_id'},
 
display = h.tbl.display.factory.value{},
 
display = h.tbl.display.factory.value{},
order = 1002,
+
order = 1004,
 
},
 
},
 
{
 
{
Строка 367: Строка 391:
 
arg = 'level',
 
arg = 'level',
 
header = m_game.level_requirement.icon,
 
header = m_game.level_requirement.icon,
fields = h.tbl.range_fields('items.required_level'),
+
fields = h.tbl.range_fields{field='items.required_level'},
 
display = h.tbl.display.factory.range{field='items.required_level'},
 
display = h.tbl.display.factory.range{field='items.required_level'},
 
order = 5000,
 
order = 5000,
Строка 374: Строка 398:
 
arg = 'ar',
 
arg = 'ar',
 
header = i18n.item_table.armour,
 
header = i18n.item_table.armour,
fields = h.tbl.range_fields('armours.armour'),
+
fields = h.tbl.range_fields{field='armours.armour'},
 
display = h.tbl.display.factory.range{field='armours.armour'},
 
display = h.tbl.display.factory.range{field='armours.armour'},
 
order = 6000,
 
order = 6000,
Строка 381: Строка 405:
 
arg = 'ev',
 
arg = 'ev',
 
header =i18n.item_table.evasion,
 
header =i18n.item_table.evasion,
fields = h.tbl.range_fields('armours.evasion'),
+
fields = h.tbl.range_fields{field='armours.evasion'},
 
display = h.tbl.display.factory.range{field='armours.evasion'},
 
display = h.tbl.display.factory.range{field='armours.evasion'},
 
order = 6001,
 
order = 6001,
Строка 388: Строка 412:
 
arg = 'es',
 
arg = 'es',
 
header = i18n.item_table.energy_shield,
 
header = i18n.item_table.energy_shield,
fields = h.tbl.range_fields('armours.energy_shield'),
+
fields = h.tbl.range_fields{field='armours.energy_shield'},
 
display = h.tbl.display.factory.range{field='armours.energy_shield'},
 
display = h.tbl.display.factory.range{field='armours.energy_shield'},
 
order = 6002,
 
order = 6002,
  +
},
  +
{
  +
arg = 'wd',
  +
header = i18n.item_table.ward,
  +
fields = h.tbl.range_fields{field='armours.ward'},
  +
display = h.tbl.display.factory.range{field='armours.ward'},
  +
order = 6003,
 
},
 
},
 
{
 
{
 
arg = 'block',
 
arg = 'block',
 
header = i18n.item_table.block,
 
header = i18n.item_table.block,
fields = h.tbl.range_fields('shields.block'),
+
fields = h.tbl.range_fields{field='shields.block'},
 
display = h.tbl.display.factory.range{field='shields.block'},
 
display = h.tbl.display.factory.range{field='shields.block'},
order = 6003,
+
order = 6004,
 
},
 
},
 
--[[{
 
--[[{
Строка 412: Строка 443:
 
display = h.tbl.display.factory.range{field='maximum physical damage'},
 
display = h.tbl.display.factory.range{field='maximum physical damage'},
 
order = 7001,
 
order = 7001,
  +
 
 
},]]--
 
},]]--
 
{
 
{
Строка 429: Строка 460:
 
arg = {'weapon', 'aps'},
 
arg = {'weapon', 'aps'},
 
header = i18n.item_table.attacks_per_second,
 
header = i18n.item_table.attacks_per_second,
fields = h.tbl.range_fields('weapons.attack_speed'),
+
fields = h.tbl.range_fields{field='weapons.attack_speed'},
 
display = h.tbl.display.factory.range{field='weapons.attack_speed'},
 
display = h.tbl.display.factory.range{field='weapons.attack_speed'},
 
order = 8001,
 
order = 8001,
Строка 436: Строка 467:
 
arg = {'weapon', 'crit'},
 
arg = {'weapon', 'crit'},
 
header = i18n.item_table.local_critical_strike_chance,
 
header = i18n.item_table.local_critical_strike_chance,
fields = h.tbl.range_fields('weapons.critical_strike_chance'),
+
fields = h.tbl.range_fields{field='weapons.critical_strike_chance'},
 
display = h.tbl.display.factory.range{field='weapons.critical_strike_chance'},
 
display = h.tbl.display.factory.range{field='weapons.critical_strike_chance'},
 
order = 8002,
 
order = 8002,
Строка 443: Строка 474:
 
arg = {'physical_dps'},
 
arg = {'physical_dps'},
 
header = i18n.item_table.physical_dps,
 
header = i18n.item_table.physical_dps,
fields = h.tbl.range_fields('weapons.physical_dps'),
+
fields = h.tbl.range_fields{field='weapons.physical_dps'},
 
display = h.tbl.display.factory.range{field='weapons.physical_dps'},
 
display = h.tbl.display.factory.range{field='weapons.physical_dps'},
 
order = 8100,
 
order = 8100,
Строка 450: Строка 481:
 
arg = {'lightning_dps'},
 
arg = {'lightning_dps'},
 
header = i18n.item_table.lightning_dps,
 
header = i18n.item_table.lightning_dps,
fields = h.tbl.range_fields('weapons.lightning_dps'),
+
fields = h.tbl.range_fields{field='weapons.lightning_dps'},
 
display = h.tbl.display.factory.range{field='weapons.lightning_dps'},
 
display = h.tbl.display.factory.range{field='weapons.lightning_dps'},
 
order = 8101,
 
order = 8101,
Строка 457: Строка 488:
 
arg = {'cold_dps'},
 
arg = {'cold_dps'},
 
header = i18n.item_table.cold_dps,
 
header = i18n.item_table.cold_dps,
fields = h.tbl.range_fields('weapons.cold_dps'),
+
fields = h.tbl.range_fields{field='weapons.cold_dps'},
 
display = h.tbl.display.factory.range{field='weapons.cold_dps'},
 
display = h.tbl.display.factory.range{field='weapons.cold_dps'},
 
order = 8102,
 
order = 8102,
Строка 464: Строка 495:
 
arg = {'fire_dps'},
 
arg = {'fire_dps'},
 
header = i18n.item_table.fire_dps,
 
header = i18n.item_table.fire_dps,
fields = h.tbl.range_fields('weapons.fire_dps'),
+
fields = h.tbl.range_fields{field='weapons.fire_dps'},
 
display = h.tbl.display.factory.range{field='weapons.fire_dps'},
 
display = h.tbl.display.factory.range{field='weapons.fire_dps'},
 
order = 8103,
 
order = 8103,
Строка 471: Строка 502:
 
arg = {'chaos_dps'},
 
arg = {'chaos_dps'},
 
header = i18n.item_table.chaos_dps,
 
header = i18n.item_table.chaos_dps,
fields = h.tbl.range_fields('weapons.chaos_dps'),
+
fields = h.tbl.range_fields{field='weapons.chaos_dps'},
 
display = h.tbl.display.factory.range{field='weapons.chaos_dps'},
 
display = h.tbl.display.factory.range{field='weapons.chaos_dps'},
 
order = 8104,
 
order = 8104,
Строка 478: Строка 509:
 
arg = {'elemental_dps'},
 
arg = {'elemental_dps'},
 
header = i18n.item_table.elemental_dps,
 
header = i18n.item_table.elemental_dps,
fields = h.tbl.range_fields('weapons.elemental_dps'),
+
fields = h.tbl.range_fields{field='weapons.elemental_dps'},
 
display = h.tbl.display.factory.range{field='weapons.elemental_dps'},
 
display = h.tbl.display.factory.range{field='weapons.elemental_dps'},
 
order = 8105,
 
order = 8105,
Строка 485: Строка 516:
 
arg = {'poison_dps'},
 
arg = {'poison_dps'},
 
header = i18n.item_table.poison_dps,
 
header = i18n.item_table.poison_dps,
fields = h.tbl.range_fields('weapons.poison_dps'),
+
fields = h.tbl.range_fields{field='weapons.poison_dps'},
 
display = h.tbl.display.factory.range{field='weapons.poison_dps'},
 
display = h.tbl.display.factory.range{field='weapons.poison_dps'},
 
order = 8106,
 
order = 8106,
Строка 492: Строка 523:
 
arg = {'dps'},
 
arg = {'dps'},
 
header = i18n.item_table.dps,
 
header = i18n.item_table.dps,
fields = h.tbl.range_fields('weapons.dps'),
+
fields = h.tbl.range_fields{field='weapons.dps'},
 
display = h.tbl.display.factory.range{field='weapons.dps'},
 
display = h.tbl.display.factory.range{field='weapons.dps'},
 
order = 8107,
 
order = 8107,
Строка 499: Строка 530:
 
arg = 'flask_life',
 
arg = 'flask_life',
 
header = i18n.item_table.flask_life,
 
header = i18n.item_table.flask_life,
fields = h.tbl.range_fields('flasks.life'),
+
fields = h.tbl.range_fields{field='flasks.life'},
 
display = h.tbl.display.factory.range{field='flasks.life'},
 
display = h.tbl.display.factory.range{field='flasks.life'},
 
order = 9000,
 
order = 9000,
  +
},
  +
{
  +
arg = 'flask_life_per_second',
  +
header = i18n.item_table.flask_life_per_second,
  +
fields = h.tbl.range_fields{field={'flasks.life', 'flasks.duration'}, full=true},
  +
display = h.tbl.display.factory.range_composite{field1='flasks.life', field2='flasks.duration'},
  +
order = 9001,
  +
},
  +
{
  +
arg = 'flask_life_per_charge',
  +
header = i18n.item_table.flask_life_per_charge,
  +
fields = h.tbl.range_fields{field={'flasks.life', 'flasks.charges_per_use'}, full=true},
  +
display = h.tbl.display.factory.range_composite{field1='flasks.life', field2='flasks.charges_per_use'},
  +
order = 9002,
 
},
 
},
 
{
 
{
 
arg = 'flask_mana',
 
arg = 'flask_mana',
 
header = i18n.item_table.flask_mana,
 
header = i18n.item_table.flask_mana,
fields = h.tbl.range_fields('flasks.mana'),
+
fields = h.tbl.range_fields{field='flasks.mana'},
 
display = h.tbl.display.factory.range{field='flasks.mana'},
 
display = h.tbl.display.factory.range{field='flasks.mana'},
order = 9001,
+
order = 9010,
  +
},
  +
{
  +
arg = 'flask_mana_per_second',
  +
header = i18n.item_table.flask_mana_per_second,
  +
fields = h.tbl.range_fields{field={'flasks.mana', 'flasks.duration'}, full=true},
  +
display = h.tbl.display.factory.range_composite{field1='flasks.mana', field2='flasks.duration'},
  +
order = 9011,
  +
},
  +
{
  +
arg = 'flask_mana_per_charge',
  +
header = i18n.item_table.flask_mana_per_charge,
  +
fields = h.tbl.range_fields{field={'flasks.mana', 'flasks.charges_per_use'}, full=true},
  +
display = h.tbl.display.factory.range_composite{field1='flasks.mana', field2='flasks.charges_per_use'},
  +
order = 9012,
 
},
 
},
 
{
 
{
 
arg = 'flask',
 
arg = 'flask',
 
header = i18n.item_table.flask_duration,
 
header = i18n.item_table.flask_duration,
fields = h.tbl.range_fields('flasks.duration'),
+
fields = h.tbl.range_fields{field='flasks.duration'},
 
display = h.tbl.display.factory.range{field='flasks.duration'},
 
display = h.tbl.display.factory.range{field='flasks.duration'},
order = 9002,
+
order = 9020,
 
},
 
},
 
{
 
{
 
arg = 'flask',
 
arg = 'flask',
 
header = i18n.item_table.flask_charges_per_use,
 
header = i18n.item_table.flask_charges_per_use,
fields = h.tbl.range_fields('flasks.charges_per_use'),
+
fields = h.tbl.range_fields{field='flasks.charges_per_use'},
 
display = h.tbl.display.factory.range{field='flasks.charges_per_use'},
 
display = h.tbl.display.factory.range{field='flasks.charges_per_use'},
order = 9003,
+
order = 9030,
 
},
 
},
 
{
 
{
 
arg = 'flask',
 
arg = 'flask',
 
header = i18n.item_table.flask_maximum_charges,
 
header = i18n.item_table.flask_maximum_charges,
fields = h.tbl.range_fields('flasks.charges_max'),
+
fields = h.tbl.range_fields{field='flasks.charges_max'},
 
display = h.tbl.display.factory.range{field='flasks.charges_max'},
 
display = h.tbl.display.factory.range{field='flasks.charges_max'},
order = 9004,
+
order = 9031,
  +
},
  +
-- Seed data 91xx,
  +
{
  +
arg = {'seed', 'seed_type'},
  +
header = i18n.item_table.seed_type,
  +
fields = {'harvest_seeds.type', 'harvest_seeds.type_id'},
  +
display = function (tr, data)
  +
tr
  +
:tag('td')
  +
:attr('table-sort-value', data['harvest_seeds.type'])
  +
:attr('class', 'tc -' .. data['harvest_seeds.type_id'])
  +
:wikitext(data['harvest_seeds.type'])
  +
end,
  +
order = 9100,
  +
},
  +
{
  +
arg = {'seed', 'seed_tier'},
  +
header = i18n.item_table.seed_tier,
  +
fields = {'harvest_seeds.tier'},
  +
display = h.tbl.display.factory.value{},
  +
order = 9101,
  +
},
  +
{
  +
arg = {'seed', 'seed_growth_cycles'},
  +
header = i18n.item_table.seed_growth_cycles,
  +
fields = {'harvest_seeds.growth_cycles'},
  +
display = h.tbl.display.factory.value{},
  +
order = 9102,
  +
},
  +
{
  +
arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_primal_lifeforce_percentage'},
  +
header = i18n.item_table.seed_consumed_primal_lifeforce_percentage,
  +
fields = {'harvest_seeds.consumed_primal_lifeforce_percentage'},
  +
display = h.tbl.display.factory.value{color='primal'},
  +
order = 9110,
  +
},
  +
{
  +
arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_vivid_lifeforce_percentage'},
  +
header = i18n.item_table.seed_consumed_vivid_lifeforce_percentage,
  +
fields = {'harvest_seeds.consumed_vivid_lifeforce_percentage'},
  +
display = h.tbl.display.factory.value{color='vivid'},
  +
order = 9110,
  +
},
  +
{
  +
arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_wild_lifeforce_percentage'},
  +
header = i18n.item_table.seed_consumed_wild_lifeforce_percentage,
  +
fields = {'harvest_seeds.consumed_wild_lifeforce_percentage'},
  +
display = h.tbl.display.factory.value{color='wild'},
  +
order = 9110,
  +
},
  +
{
  +
arg = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_amount'},
  +
header = i18n.item_table.seed_required_nearby_seed_amount,
  +
fields = {'harvest_seeds.required_nearby_seed_amount'},
  +
display = h.tbl.display.factory.value{},
  +
order = 9113,
  +
},
  +
{
  +
arg = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_tier'},
  +
header = i18n.item_table.seed_required_nearby_seed_tier,
  +
fields = {'harvest_seeds.required_nearby_seed_tier'},
  +
display = h.tbl.display.factory.value{},
  +
order = 9114,
 
},
 
},
  +
-- 9120 show crafting options?
 
{
 
{
 
arg = 'item_limit',
 
arg = 'item_limit',
Строка 570: Строка 693:
 
order = 11020,
 
order = 11020,
 
sort_type = 'text',
 
sort_type = 'text',
  +
},
  +
{
  +
arg = 'atlas_tier',
  +
header = i18n.item_table.atlas_tier,
  +
colspan = 5,
  +
fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
  +
display = h.tbl.display.factory.atlas_tier{is_level=false},
  +
order = 11050,
  +
},
  +
{
  +
arg = 'atlas_level',
  +
header = i18n.item_table.atlas_level,
  +
colspan = 5,
  +
fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
  +
display = h.tbl.display.factory.atlas_tier{is_level=true},
  +
order = 11060,
 
},
 
},
 
{
 
{
Строка 622: Строка 761:
 
display = h.tbl.display.factory.value{colour='mod'},
 
display = h.tbl.display.factory.value{colour='mod'},
 
order = 12002,
 
order = 12002,
  +
sort_type = 'text',
  +
},
  +
{
  +
arg = {'seed', 'seed_effect'},
  +
header = i18n.item_table.effects,
  +
fields = {'harvest_seeds.effect'},
  +
display = h.tbl.display.factory.value{colour='crafted'},
  +
order = 12003,
 
sort_type = 'text',
 
sort_type = 'text',
 
},
 
},
Строка 698: Строка 845:
 
:wikitext(
 
:wikitext(
 
string.format(
 
string.format(
i18n.item_table.version_link,
+
i18n.item_table.version_link,
data['items.release_version'],
+
data['items.release_version'],
 
data['items.release_version']
 
data['items.release_version']
 
)
 
)
Строка 715: Строка 862:
 
:wikitext(
 
:wikitext(
 
string.format(
 
string.format(
i18n.item_table.version_link,
+
i18n.item_table.version_link,
data['items.removal_version'],
+
data['items.removal_version'],
 
data['items.removal_version']
 
data['items.removal_version']
 
)
 
)
Строка 735: Строка 882:
 
fields = {'items.drop_leagues'},
 
fields = {'items.drop_leagues'},
 
display = function(tr, data)
 
display = function(tr, data)
  +
local s = m_util.string.split(data['items.drop_leagues'], ',')
  +
for i, v in ipairs(s) do
  +
s[i] = string.format(i18n.item_table.drop_leagues_link, v, v)
  +
end
 
tr
 
tr
 
:tag('td')
 
:tag('td')
:wikitext(table.concat(m_util.string.split(data['items.drop_leagues'], ','), '<br>'))
+
:wikitext(table.concat(s, '<br>'))
 
end,
 
end,
 
order = 15003,
 
order = 15003,
Строка 748: Строка 899:
 
display = h.tbl.display.factory.value{},
 
display = h.tbl.display.factory.value{},
 
order = 15004,
 
order = 15004,
  +
sort_type = 'text',
  +
},
  +
{
  +
arg = {'drop', 'drop_monsters'},
  +
header = i18n.item_table.drop_monsters,
  +
fields = {'items.drop_monsters'},
  +
display = function(tr, data, na, results2)
  +
if results2['drop_monsters_query'] == nil then
  +
results2['drop_monsters_query'] = m_cargo.query(
  +
{'items', 'monsters', 'main_pages'},
  +
{
  +
'items._pageName',
  +
'monsters._pageName',
  +
'monsters.name',
  +
'main_pages._pageName',
  +
},
  +
{
  +
join=[[
  +
items.drop_monsters HOLDS monsters.metadata_id,
  +
monsters.metadata_id=main_pages.id
  +
]],
  +
where=string.format([[
  +
items._pageID IN (%s)
  +
AND monsters.metadata_id IS NOT NULL
  +
]],
  +
table.concat(results2.pageIDs, ', ')
  +
),
  +
orderBy='items.drop_monsters',
  +
}
  +
)
  +
  +
results2['drop_monsters_query'] = m_cargo.map_results_to_id{
  +
results=results2['drop_monsters_query'],
  +
field='items._pageName',
  +
}
  +
end
  +
local results = results2['drop_monsters_query'][data['items._pageName']] or {}
  +
local tbl = {}
  +
for _,v in ipairs(results) do
  +
local page = v['main_pages._pageName'] or v['monsters._pageName'] or ''
  +
local name = v['monsters.name'] or v['items.drop_monsters'] or ''
  +
tbl[#tbl+1] = string.format('[[%s|%s]]', page, name)
  +
end
  +
h.na_or_val(tr, table.concat(tbl, '<br>'))
  +
end,
  +
order = 15005,
 
sort_type = 'text',
 
sort_type = 'text',
 
},
 
},
Строка 755: Строка 952:
 
fields = {'items.drop_text'},
 
fields = {'items.drop_text'},
 
display = h.tbl.display.factory.value{},
 
display = h.tbl.display.factory.value{},
order = 15005,
+
order = 15006,
 
sort_type = 'text',
 
sort_type = 'text',
 
},
 
},
Строка 767: Строка 964:
 
{'items', 'quest_rewards'},
 
{'items', 'quest_rewards'},
 
{
 
{
'quest_rewards.reward',
+
'quest_rewards._pageName',
'quest_rewards.classes',
+
'quest_rewards.classes',
'quest_rewards.act',
+
'quest_rewards.act',
 
'quest_rewards.quest'
 
'quest_rewards.quest'
 
},
 
},
 
{
 
{
join='items._pageName=quest_rewards.reward',
+
join='items._pageName=quest_rewards._pageName',
 
where=string.format(
 
where=string.format(
'items._pageID IN (%s) AND quest_rewards.reward IS NOT NULL',
+
'items._pageID IN (%s) AND quest_rewards._pageName IS NOT NULL',
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 782: Строка 979:
 
)
 
)
 
results2['quest_query'] = m_cargo.map_results_to_id{
 
results2['quest_query'] = m_cargo.map_results_to_id{
results=results2['quest_query'],
+
results=results2['quest_query'],
field='quest_rewards.reward',
+
field='quest_rewards._pageName',
 
}
 
}
 
end
 
end
  +
 
 
local results = results2['quest_query'][data['items._pageName']] or {}
 
local results = results2['quest_query'][data['items._pageName']] or {}
 
local tbl = {}
 
local tbl = {}
for _, v in ipairs(results) do
+
for _, v in ipairs(results) do
local classes = table.concat(m_util.string.split(v['quest_rewards.classes'] or '', ''), ', ')
+
local classes = table.concat(m_util.string.split(v['quest_rewards.classes'] or '', ',%s*'), ', ')
 
if classes == '' or classes == nil then
 
if classes == '' or classes == nil then
 
classes = i18n.item_table.quest_rewards_any_classes
 
classes = i18n.item_table.quest_rewards_any_classes
end
+
end
  +
 
 
tbl[#tbl+1] = string.format(
 
tbl[#tbl+1] = string.format(
 
i18n.item_table.quest_rewards_row_format,
 
i18n.item_table.quest_rewards_row_format,
v['quest_rewards.act'],
+
v['quest_rewards.act'],
v['quest_rewards.quest'],
+
v['quest_rewards.quest'],
 
classes
 
classes
 
)
 
)
 
end
 
end
  +
 
value = table.concat(tbl, '<br>')
+
local value = table.concat(tbl, '<br>')
 
if value == nil or value == '' then
 
if value == nil or value == '' then
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
Строка 825: Строка 1022:
 
{'items', 'vendor_rewards'},
 
{'items', 'vendor_rewards'},
 
{
 
{
'vendor_rewards.reward',
+
'vendor_rewards._pageName',
'vendor_rewards.classes',
+
'vendor_rewards.classes',
'vendor_rewards.act',
+
'vendor_rewards.act',
'vendor_rewards.npc',
+
'vendor_rewards.npc',
 
'vendor_rewards.quest',
 
'vendor_rewards.quest',
 
},
 
},
 
{
 
{
join='items._pageName=vendor_rewards.reward',
+
join='items._pageName=vendor_rewards._pageName',
 
where=string.format(
 
where=string.format(
'items._pageID IN (%s) AND vendor_rewards.reward IS NOT NULL',
+
'items._pageID IN (%s) AND vendor_rewards._pageName IS NOT NULL',
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 841: Строка 1038:
 
)
 
)
 
results2['vendor_query'] = m_cargo.map_results_to_id{
 
results2['vendor_query'] = m_cargo.map_results_to_id{
results=results2['vendor_query'],
+
results=results2['vendor_query'],
field='vendor_rewards.reward',
+
field='vendor_rewards._pageName',
 
}
 
}
 
end
 
end
 
local results = results2['vendor_query'][data['items._pageName']] or {}
 
local results = results2['vendor_query'][data['items._pageName']] or {}
  +
 
 
local tbl = {}
 
local tbl = {}
for _, v in ipairs(results) do
+
for _, v in ipairs(results) do
local classes = table.concat(m_util.string.split(v['vendor_rewards.classes'] or '', ''), ', ')
+
local classes = table.concat(m_util.string.split(v['vendor_rewards.classes'] or '', ',%s*'), ', ')
 
if classes == '' or classes == nil then
 
if classes == '' or classes == nil then
 
classes = i18n.item_table.vendor_rewards_any_classes
 
classes = i18n.item_table.vendor_rewards_any_classes
end
+
end
  +
 
 
tbl[#tbl+1] = string.format(
 
tbl[#tbl+1] = string.format(
 
i18n.item_table.vendor_rewards_row_format,
 
i18n.item_table.vendor_rewards_row_format,
v['vendor_rewards.act'],
+
v['vendor_rewards.act'],
v['vendor_rewards.quest'],
+
v['vendor_rewards.quest'],
v['vendor_rewards.npc'],
+
v['vendor_rewards.npc'],
 
classes
 
classes
 
)
 
)
 
end
 
end
  +
 
value = table.concat(tbl, '<br>')
+
local value = table.concat(tbl, '<br>')
 
if value == nil or value == '' then
 
if value == nil or value == '' then
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
Строка 881: Строка 1078:
 
fields = {'item_purchase_costs.name', 'item_purchase_costs.amount'},
 
fields = {'item_purchase_costs.name', 'item_purchase_costs.amount'},
 
display = function (tr, data)
 
display = function (tr, data)
-- Can purchase costs have multiple currencies and rows?
+
-- Can purchase costs have multiple currencies and rows?
 
-- Just switch to the same method as in sell_price then.
 
-- Just switch to the same method as in sell_price then.
 
local tbl = {}
 
local tbl = {}
if data['item_purchase_costs.name'] ~= nil then
+
if data['item_purchase_costs.name'] ~= nil then
 
tbl[#tbl+1] = string.format(
 
tbl[#tbl+1] = string.format(
'%sx %s',
+
'%sx %s',
 
data['item_purchase_costs.amount'],
 
data['item_purchase_costs.amount'],
f_item_link{data['item_purchase_costs.name']}
+
h.item_link{data['item_purchase_costs.name']}
 
)
 
)
 
end
 
end
Строка 905: Строка 1102:
 
{'items', 'item_sell_prices'},
 
{'items', 'item_sell_prices'},
 
{
 
{
'item_sell_prices.name',
+
'item_sell_prices.name',
'item_sell_prices.amount',
+
'item_sell_prices.amount',
 
'item_sell_prices._pageID'
 
'item_sell_prices._pageID'
 
},
 
},
Строка 912: Строка 1109:
 
join='items._pageID=item_sell_prices._pageID',
 
join='items._pageID=item_sell_prices._pageID',
 
where=string.format(
 
where=string.format(
'items._pageID IN (%s) AND item_sell_prices._pageID IS NOT NULL',
+
'items._pageID IN (%s) AND item_sell_prices._pageID IS NOT NULL',
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 919: Строка 1116:
 
)
 
)
 
results2['sell_price_query'] = m_cargo.map_results_to_id{
 
results2['sell_price_query'] = m_cargo.map_results_to_id{
results=results2['sell_price_query'],
+
results=results2['sell_price_query'],
 
field='item_sell_prices._pageID',
 
field='item_sell_prices._pageID',
 
}
 
}
end
+
end
 
local results = results2['sell_price_query'][data['items._pageID']] or {}
 
local results = results2['sell_price_query'][data['items._pageID']] or {}
  +
 
 
local tbl = {}
 
local tbl = {}
 
for _,v in ipairs(results) do
 
for _,v in ipairs(results) do
 
tbl[#tbl+1] = string.format(
 
tbl[#tbl+1] = string.format(
'%sx %s',
+
'%sx %s',
 
v['item_sell_prices.amount'],
 
v['item_sell_prices.amount'],
f_item_link{v['item_sell_prices.name']}
+
h.item_link{v['item_sell_prices.name']}
 
)
 
)
 
end
 
end
Строка 941: Строка 1138:
 
arg = {'boss', 'boss_name'},
 
arg = {'boss', 'boss_name'},
 
header = i18n.item_table.boss_name,
 
header = i18n.item_table.boss_name,
fields = {'maps.area_id'},
+
fields = {'maps.area_id'},
 
display = function(tr, data, na, results2)
 
display = function(tr, data, na, results2)
if results2['boss_query'] == nil then
+
if results2['boss_query'] == nil then
 
results2['boss_query'] = m_cargo.query(
 
results2['boss_query'] = m_cargo.query(
{'items', 'maps', 'areas'},
+
{'items', 'maps', 'areas', 'monsters', 'main_pages'},
 
{
 
{
'items._pageName',
+
'items._pageName',
'maps.area_id',
+
'maps.area_id',
'areas.id',
+
'areas.id',
'areas.boss_monster_ids'
+
'areas.boss_monster_ids',
  +
'monsters._pageName',
  +
'monsters.name',
  +
'main_pages._pageName',
 
},
 
},
 
{
 
{
join='items._pageID=maps._pageID, maps.area_id=areas.id',
+
join=[[
-- where='maps.area_id IS NOT NULL AND areas.boss_monster_ids HOLDS LIKE "%"',
+
items._pageID=maps._pageID,
where=string.format(
+
maps.area_id=areas.id,
'items._pageID IN (%s) AND maps.area_id IS NOT NULL AND areas.boss_monster_ids HOLDS LIKE "%%"',
+
areas.boss_monster_ids HOLDS monsters.metadata_id,
  +
monsters.metadata_id=main_pages.id
  +
]],
  +
where=string.format([[
  +
items._pageID IN (%s)
  +
AND maps.area_id IS NOT NULL
  +
AND areas.boss_monster_ids HOLDS LIKE "%%"
  +
]],
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 962: Строка 1169:
 
}
 
}
 
)
 
)
  +
 
 
results2['boss_query'] = m_cargo.map_results_to_id{
 
results2['boss_query'] = m_cargo.map_results_to_id{
results=results2['boss_query'],
+
results=results2['boss_query'],
 
field='items._pageName',
 
field='items._pageName',
 
}
 
}
end
+
end
 
local results = results2['boss_query'][data['items._pageName']] or {}
 
local results = results2['boss_query'][data['items._pageName']] or {}
 
local tbl = {}
 
local tbl = {}
 
for _,v in ipairs(results) do
 
for _,v in ipairs(results) do
tbl[#tbl+1] = v['areas.boss_monster_ids']
+
local page = v['main_pages._pageName'] or v['monsters._pageName'] or ''
  +
local name = v['monsters.name'] or v['areas.boss_monster_ids'] or ''
  +
tbl[#tbl+1] = string.format('[[%s|%s]]', page, name)
 
end
 
end
 
h.na_or_val(tr, table.concat(tbl, '<br>'))
 
h.na_or_val(tr, table.concat(tbl, '<br>'))
Строка 981: Строка 1190:
 
arg = {'boss', 'boss_number'},
 
arg = {'boss', 'boss_number'},
 
header = i18n.item_table.boss_number,
 
header = i18n.item_table.boss_number,
fields = {'maps.area_id'},
+
fields = {'maps.area_id'},
 
display = function(tr, data, na, results2)
 
display = function(tr, data, na, results2)
if results2['boss_query'] == nil then
+
if results2['boss_query'] == nil then
 
results2['boss_query'] = m_cargo.query(
 
results2['boss_query'] = m_cargo.query(
{'items', 'maps', 'areas'},
+
{'items', 'maps', 'areas', 'monsters', 'main_pages'},
 
{
 
{
'items._pageName',
+
'items._pageName',
'maps.area_id',
+
'maps.area_id',
'areas.id',
+
'areas.id',
'areas.boss_monster_ids'
+
'areas.boss_monster_ids',
  +
'monsters._pageName',
  +
'monsters.name',
  +
'main_pages._pageName',
 
},
 
},
 
{
 
{
join='items._pageID=maps._pageID, maps.area_id=areas.id',
+
join=[[
where=string.format(
+
items._pageID=maps._pageID,
'items._pageID IN (%s) AND maps.area_id IS NOT NULL AND areas.boss_monster_ids HOLDS LIKE "%"',
+
maps.area_id=areas.id,
  +
areas.boss_monster_ids HOLDS monsters.metadata_id,
  +
monsters.metadata_id=main_pages.id
  +
]],
  +
where=string.format([[
  +
items._pageID IN (%s)
  +
AND maps.area_id IS NOT NULL
  +
AND areas.boss_monster_ids HOLDS LIKE "%%"
  +
]],
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 1001: Строка 1221:
 
}
 
}
 
)
 
)
  +
 
 
results2['boss_query'] = m_cargo.map_results_to_id{
 
results2['boss_query'] = m_cargo.map_results_to_id{
results=results2['boss_query'],
+
results=results2['boss_query'],
 
field='items._pageName',
 
field='items._pageName',
 
}
 
}
end
+
end
local results = results2['boss_query'][data['items._pageName']]
+
local results = results2['boss_query'][data['items._pageName']] or {}
 
local tbl = {}
 
local tbl = {}
 
for _,v in ipairs(results) do
 
for _,v in ipairs(results) do
Строка 1015: Строка 1235:
 
end,
 
end,
 
order = 19002,
 
order = 19002,
},
 
{
 
arg = {'sextant'},
 
header = i18n.item_table.sextant,
 
fields = {'items.name', 'atlas_maps.x', 'atlas_maps.y', 'maps.series'},
 
display = function(tr, data, na, results2)
 
if results2['sextant_query'] == nil then
 
results2['sextant_query'] = m_cargo.query(
 
{'items', 'maps', 'atlas_maps'},
 
{
 
'items._pageName',
 
'items.name',
 
'maps.series',
 
'atlas_maps.x',
 
'atlas_maps.y',
 
'atlas_maps.connections'
 
},
 
{
 
join='items._pageID=atlas_maps._pageID, items._pageID=maps._pageID, maps._pageID=atlas_maps._pageID',
 
where='atlas_maps.x IS NOT NULL',
 
orderBy='maps.tier, items._pageName',
 
}
 
)
 
end
 
local results = results2['sextant_query'] or {}
 
 
local sextant_radius = 55 -- Should be in Module:Game?
 
local x_center = data['atlas_maps.x']
 
local y_center = data['atlas_maps.y']
 
 
if not (x_center and y_center) then
 
return
 
end
 
 
local tbl = {}
 
for _, v in ipairs(results) do
 
local x = v['atlas_maps.x']
 
local y = v['atlas_maps.y']
 
local r = ((x-x_center)^2 + (y-y_center)^2)^0.5
 
if (sextant_radius >= r) and (data['items._pageName'] ~= v['items._pageName']) then --
 
tbl[#tbl+1] = f_item_link({page=v['items._pageName']})
 
end
 
end
 
h.na_or_val(tr, table.concat(tbl, '<br>'))
 
end,
 
order = 20001,
 
sort_type = 'text',
 
 
},
 
},
 
{
 
{
 
arg = {'legacy'},
 
arg = {'legacy'},
 
header = i18n.item_table.legacy,
 
header = i18n.item_table.legacy,
fields = {'items.name'},
+
fields = {'items.name'},
 
display = function(tr, data, na, results2)
 
display = function(tr, data, na, results2)
if results2['legacy_query'] == nil then
+
if results2['legacy_query'] == nil then
 
results2['legacy_query'] = m_cargo.query(
 
results2['legacy_query'] = m_cargo.query(
 
{'items', 'legacy_variants'},
 
{'items', 'legacy_variants'},
 
{
 
{
'items._pageID',
+
'items._pageID',
'items._pageName',
+
'items._pageName',
'items.frame_type',
+
'items.frame_type',
'legacy_variants.removal_version',
+
'legacy_variants.removal_version',
'legacy_variants.implicit_stat_text',
+
'legacy_variants.implicit_stat_text',
'legacy_variants.explicit_stat_text',
+
'legacy_variants.explicit_stat_text',
'legacy_variants.stat_text',
+
'legacy_variants.stat_text',
'legacy_variants.base_item',
+
'legacy_variants.base_item',
 
'legacy_variants.required_level'
 
'legacy_variants.required_level'
 
},
 
},
Строка 1086: Строка 1259:
 
where='legacy_variants.removal_version IS NOT NULL',
 
where='legacy_variants.removal_version IS NOT NULL',
 
where=string.format(
 
where=string.format(
'items._pageID IN (%s) AND legacy_variants.removal_version IS NOT NULL',
+
'items._pageID IN (%s) AND legacy_variants.removal_version IS NOT NULL',
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
Строка 1092: Строка 1265:
 
}
 
}
 
)
 
)
  +
 
 
results2['legacy_query'] = m_cargo.map_results_to_id{
 
results2['legacy_query'] = m_cargo.map_results_to_id{
results=results2['legacy_query'],
+
results=results2['legacy_query'],
 
field='items._pageName',
 
field='items._pageName',
 
}
 
}
end
+
end
 
local results = results2['legacy_query'][data['items._pageName']] or {}
 
local results = results2['legacy_query'][data['items._pageName']] or {}
  +
 
 
local tbl = mw.html.create('table')
 
local tbl = mw.html.create('table')
 
:attr('width', '100%')
 
:attr('width', '100%')
Строка 1105: Строка 1278:
 
local cell = {}
 
local cell = {}
 
local l = {
 
local l = {
'legacy_variants.base_item',
+
'legacy_variants.base_item',
 
'legacy_variants.stat_text'
 
'legacy_variants.stat_text'
 
}
 
}
  +
 
-- Clean up data:
+
-- Clean up data:
 
for _, k in ipairs(l) do
 
for _, k in ipairs(l) do
if v[k] ~= nil then
+
if v[k] ~= nil then
 
local s = m_util.string.split(v[k], '*')
 
local s = m_util.string.split(v[k], '*')
 
local s_flt = {}
 
local s_flt = {}
for _, sss in ipairs(s) do
+
for _, sss in ipairs(s) do
if sss ~= nil and sss ~= '' then
+
if sss ~= nil and sss ~= '' then
 
s_flt[#s_flt+1] = string.gsub(sss, '\n', '')
 
s_flt[#s_flt+1] = string.gsub(sss, '\n', '')
 
end
 
end
end
+
end
  +
 
 
cell[#cell+1] = table.concat(s_flt, '<br>')
 
cell[#cell+1] = table.concat(s_flt, '<br>')
end
+
end
 
end
 
end
  +
 
 
local sep = string.format(
 
local sep = string.format(
'<span class="item-stat-separator -%s"></span>',
+
'<span class="item-stat-separator -%s"></span>',
 
v['items.frame_type']
 
v['items.frame_type']
 
)
 
)
Строка 1152: Строка 1325:
 
arg = {'granted_skills'},
 
arg = {'granted_skills'},
 
header = i18n.item_table.granted_skills,
 
header = i18n.item_table.granted_skills,
fields = {'items.name'},
+
fields = {'items.name'},
 
display = function(tr, data, na, results2)
 
display = function(tr, data, na, results2)
if results2['granted_skills_query'] == nil then
+
if results2['granted_skills_query'] == nil then
 
results2['granted_skills_query'] = m_cargo.query(
 
results2['granted_skills_query'] = m_cargo.query(
 
{'items', 'item_mods', 'mods', 'skill', 'items=items2'},
 
{'items', 'item_mods', 'mods', 'skill', 'items=items2'},
 
{
 
{
'items._pageName',
+
'items._pageName',
'items.name',
+
'items.name',
'item_mods.id',
+
'item_mods.id',
'mods._pageName',
+
'mods._pageName',
'mods.id',
+
'mods.id',
 
'mods.granted_skill',
 
'mods.granted_skill',
 
'mods.stat_text_raw',
 
'mods.stat_text_raw',
Строка 1180: Строка 1353:
 
join='items._pageID=item_mods._pageID, item_mods.id=mods.id, mods.granted_skill=skill.skill_id, skill._pageID=items2._pageID',
 
join='items._pageID=item_mods._pageID, item_mods.id=mods.id, mods.granted_skill=skill.skill_id, skill._pageID=items2._pageID',
 
where=string.format(
 
where=string.format(
'items._pageID IN (%s) AND mods.granted_skill IS NOT NULL',
+
'items._pageID IN (%s) AND mods.granted_skill IS NOT NULL',
 
table.concat(results2.pageIDs, ', ')
 
table.concat(results2.pageIDs, ', ')
 
),
 
),
 
}
 
}
 
)
 
)
  +
 
 
results2['granted_skills_query'] = m_cargo.map_results_to_id{
 
results2['granted_skills_query'] = m_cargo.map_results_to_id{
results=results2['granted_skills_query'],
+
results=results2['granted_skills_query'],
 
field='items._pageName',
 
field='items._pageName',
 
}
 
}
end
+
end
 
local results = results2['granted_skills_query'][data['items._pageName']] or {}
 
local results = results2['granted_skills_query'][data['items._pageName']] or {}
  +
 
 
local tbl = {}
 
local tbl = {}
 
for _, v in ipairs(results) do
 
for _, v in ipairs(results) do
  +
 
-- Check if a level for the skill is specified in the
+
-- Check if a level for the skill is specified in the
 
-- mod stat text.
 
-- mod stat text.
 
-- Stat ids have unreliable naming convention so using
 
-- Stat ids have unreliable naming convention so using
Строка 1203: Строка 1376:
 
local stat_text = v['mods.stat_text_raw'] or ''
 
local stat_text = v['mods.stat_text_raw'] or ''
 
local level_number = string.match(
 
local level_number = string.match(
stat_text:lower(),
+
stat_text:lower(),
m_util.string.format(
+
h.string.format(
 
i18n.item_table.granted_skills_level_pattern,
 
i18n.item_table.granted_skills_level_pattern,
 
{
 
{
Строка 1211: Строка 1384:
 
)
 
)
 
)
 
)
  +
 
-- If a level number was specified in the stat text
+
-- If a level number was specified in the stat text
-- then add it to the cell:
+
-- then add it to the cell:
 
if level_number then
 
if level_number then
level = m_util.string.format(
+
level = h.string.format(
 
i18n.item_table.granted_skills_level_format,
 
i18n.item_table.granted_skills_level_format,
 
{
 
{
Строка 1223: Строка 1396:
 
)
 
)
 
end
 
end
  +
 
-- Use different formats depending on if it's a gem or
+
-- Use different formats depending on if it's a gem or
 
-- not:
 
-- not:
 
if v['items2.class'] == nil then
 
if v['items2.class'] == nil then
tbl[#tbl+1] = m_util.string.format(
+
tbl[#tbl+1] = h.string.format(
 
i18n.item_table.granted_skills_skill_output_format,
 
i18n.item_table.granted_skills_skill_output_format,
 
{
 
{
sl = f_skill_link{
+
level = level,
  +
sl = h.skill_link{
 
skip_query=true,
 
skip_query=true,
page = v['skill.active_skill_name']
+
page = v['skill.active_skill_name']
or v['skill._pageName']
+
or v['skill._pageName']
or v['mods._pageName']
+
or v['mods._pageName']
 
or '',
 
or '',
name = v['skill.active_skill_name']
+
name = v['skill.active_skill_name']
or v['skill.stat_text']
+
or v['skill.stat_text']
 
or v['mods.granted_skill'],
 
or v['mods.granted_skill'],
 
icon = v['skill.skill_icon'],
 
icon = v['skill.skill_icon'],
 
},
 
},
level = level,
 
 
}
 
}
 
)
 
)
Строка 1247: Строка 1420:
 
local il_args = {
 
local il_args = {
 
skip_query=true,
 
skip_query=true,
page=v['items2._pageName'],
+
page=v['items2._pageName'],
name=v['items2.name'],
+
name=v['items2.name'],
inventory_icon=v['items2.inventory_icon'],
+
inventory_icon=v['items2.inventory_icon'],
 
width=v['items2.size_x'],
 
width=v['items2.size_x'],
 
height=v['items2.size_y'],
 
height=v['items2.size_y'],
 
}
 
}
  +
 
 
-- TODO: add in tpl_args.
 
-- TODO: add in tpl_args.
 
if no_html == nil then
 
if no_html == nil then
 
il_args.html = v['items2.html']
 
il_args.html = v['items2.html']
 
end
 
end
tbl[#tbl+1] = m_util.string.format(
+
tbl[#tbl+1] = h.string.format(
 
i18n.item_table.granted_skills_gem_output_format,
 
i18n.item_table.granted_skills_gem_output_format,
 
{
 
{
il = f_item_link(il_args),
 
 
level = level,
 
level = level,
  +
il = h.item_link(il_args),
 
}
 
}
 
)
 
)
Строка 1278: Строка 1451:
 
display = function (tr, data)
 
display = function (tr, data)
 
local alt_art = m_util.string.split(
 
local alt_art = m_util.string.split(
data['items.alternate_art_inventory_icons'],
+
data['items.alternate_art_inventory_icons'],
','
+
','
 
)
 
)
  +
 
 
-- TODO: Use il instead to handle size?
 
-- TODO: Use il instead to handle size?
 
-- local size = 39
 
-- local size = 39
Строка 1287: Строка 1460:
 
for i,v in ipairs(alt_art) do
 
for i,v in ipairs(alt_art) do
 
out[#out+1] = string.format(
 
out[#out+1] = string.format(
'[[%s|link=|%s]]',
+
'[[%s|link=|%s]]',
 
v,
 
v,
 
v
 
v
 
)
 
)
end
+
end
  +
 
 
tr
 
tr
 
:tag('td')
 
:tag('td')
Строка 1350: Строка 1523:
 
arg = 'level',
 
arg = 'level',
 
header = m_game.level_requirement.icon,
 
header = m_game.level_requirement.icon,
fields = h.tbl.range_fields('items.required_level'),
+
fields = h.tbl.range_fields{field='items.required_level'},
 
display = h.tbl.display.factory.range{field='items.required_level'},
 
display = h.tbl.display.factory.range{field='items.required_level'},
 
order = 3004,
 
order = 3004,
Строка 1444: Строка 1617:
 
appendix = appendix .. ' ' .. i18n.item_table.reserves_mana_suffix
 
appendix = appendix .. ' ' .. i18n.item_table.reserves_mana_suffix
 
end
 
end
  +
 
 
h.tbl.display.factory.value{options = {
 
h.tbl.display.factory.value{options = {
 
[1] = {
 
[1] = {
Строка 1537: Строка 1710:
 
arg = attr_data.arg,
 
arg = attr_data.arg,
 
header = attr_data.icon,
 
header = attr_data.icon,
fields = h.tbl.range_fields(string.format('items.required_%s', attr)),
+
fields = h.tbl.range_fields{field=string.format('items.required_%s', attr)},
 
display = h.tbl.display.factory.range{field=string.format('items.required_%s', attr)},
 
display = h.tbl.display.factory.range{field=string.format('items.required_%s', attr)},
 
order = 5000+i,
 
order = 5000+i,
Строка 1562: Строка 1735:
 
local p = {}
 
local p = {}
   
--
+
--
 
-- Template:Item table
 
-- Template:Item table
--
+
--
   
 
function p.item_table(frame)
 
function p.item_table(frame)
 
--[[
 
--[[
 
Creates a generic table for items.
 
Creates a generic table for items.
  +
 
 
Examples
 
Examples
 
--------
 
--------
 
= p.item_table{
 
= p.item_table{
q_tables = 'items, vendor_rewards',
+
q_tables='vendor_rewards, quest_rewards, skill_gems',
q_join = 'items.name = vendor_rewards.reward',
+
q_join='items._pageID=vendor_rewards._pageID, items._pageID=quest_rewards._pageID, items._pageID=skill_gems._pageID',
q_where= 'vendor_rewards.reward IS NOT NULL AND (items.class = "Active Skill Gems" OR items.class = "Support Skill Gems")',
+
q_where='(items.class="Active Skill Gems" OR items.class = "Support Skill Gems") AND (vendor_rewards.quest_id IS NOT NULL OR quest_rewards.quest_id IS NOT NULL)',
 
vendor=1,
 
vendor=1,
 
}
 
}
 
 
]]
 
]]
  +
 
 
local t = os.clock()
 
local t = os.clock()
 
-- args
 
-- args
Строка 1587: Строка 1759:
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
 
tpl_args.q_where = m_cargo.replace_holds{string=tpl_args.q_where}
 
tpl_args.q_where = m_cargo.replace_holds{string=tpl_args.q_where}
   
Строка 1600: Строка 1772:
 
},
 
},
 
}
 
}
  +
 
 
if tpl_args.mode == nil then
 
if tpl_args.mode == nil then
 
tpl_args.mode = 'item'
 
tpl_args.mode = 'item'
 
end
 
end
  +
 
 
if modes[tpl_args.mode] == nil then
 
if modes[tpl_args.mode] == nil then
 
error(i18n.errors.invalid_item_table_mode)
 
error(i18n.errors.invalid_item_table_mode)
 
end
 
end
  +
 
 
local results2 = {
 
local results2 = {
 
stats = {},
 
stats = {},
Строка 1614: Строка 1786:
 
pageIDs = {},
 
pageIDs = {},
 
}
 
}
  +
 
 
local row_infos = {}
 
local row_infos = {}
 
for _, row_info in ipairs(modes[tpl_args.mode].data) do
 
for _, row_info in ipairs(modes[tpl_args.mode].data) do
Строка 1622: Строка 1794:
 
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
Строка 1630: Строка 1802:
 
end
 
end
 
end
 
end
  +
 
 
if enabled then
 
if enabled then
 
row_info.options = row_info.options or {}
 
row_info.options = row_info.options or {}
Строка 1636: Строка 1808:
 
end
 
end
 
end
 
end
  +
 
 
-- Parse stat arguments
 
-- Parse stat arguments
 
local stat_columns = {}
 
local stat_columns = {}
Строка 1643: Строка 1815:
 
repeat
 
repeat
 
i = i + 1
 
i = i + 1
  +
 
 
local prefix = string.format('stat_column%s_', i)
 
local prefix = string.format('stat_column%s_', i)
 
local col_info = {
 
local col_info = {
Строка 1653: Строка 1825:
 
options = {},
 
options = {},
 
}
 
}
  +
 
 
local j = 0
 
local j = 0
 
repeat
 
repeat
 
j = j +1
 
j = j +1
  +
 
 
local stat_info = {
 
local stat_info = {
 
id = tpl_args[string.format('%sstat%s_id', prefix, j)],
 
id = tpl_args[string.format('%sstat%s_id', prefix, j)],
 
}
 
}
  +
 
 
if stat_info.id then
 
if stat_info.id then
 
col_info.stats[#col_info.stats+1] = stat_info
 
col_info.stats[#col_info.stats+1] = stat_info
query_stats[stat_info.id] = {}
+
query_stats[stat_info.id] = {}
 
else
 
else
 
-- Stop iteration entirely if this was the first index but no stat was supplied. We assume that we stop in this case.
 
-- Stop iteration entirely if this was the first index but no stat was supplied. We assume that we stop in this case.
Строка 1674: Строка 1846:
 
end
 
end
 
until j == nil
 
until j == nil
  +
 
-- Don't add this column if no stats were provided.
+
-- Don't add this column if no stats were provided.
 
if #col_info.stats > 0 then
 
if #col_info.stats > 0 then
 
stat_columns[#stat_columns+1] = col_info
 
stat_columns[#stat_columns+1] = col_info
 
end
 
end
 
until i == nil
 
until i == nil
  +
 
 
for _, col_info in ipairs(stat_columns) do
 
for _, col_info in ipairs(stat_columns) do
 
local row_info = {
 
local row_info = {
Строка 1700: Строка 1872:
 
end
 
end
 
end
 
end
  +
 
 
if num_stats ~= #stat_texts then
 
if num_stats ~= #stat_texts then
 
tr:wikitext(m_util.html.td.na())
 
tr:wikitext(m_util.html.td.na())
Строка 1710: Строка 1882:
 
text = table.concat(stat_texts, ', ')
 
text = table.concat(stat_texts, ', ')
 
end
 
end
  +
 
 
tr:tag('td')
 
tr:tag('td')
 
:attr('data-sort-value', vmax)
 
:attr('data-sort-value', vmax)
Строка 1730: Строка 1902:
 
end
 
end
 
end
 
end
  +
 
 
if col_info.format == nil then
 
if col_info.format == nil then
 
col_info.format = '%s'
 
col_info.format = '%s'
 
end
 
end
  +
 
 
tr:tag('td')
 
tr:tag('td')
 
:attr('data-sort-value', total_stat.max)
 
:attr('data-sort-value', total_stat.max)
Строка 1747: Строка 1919:
 
table.insert(row_infos, row_info)
 
table.insert(row_infos, row_info)
 
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)
  +
 
 
-- Parse query arguments
 
-- Parse query arguments
 
local tables_assoc = {items=true}
 
local tables_assoc = {items=true}
Строка 1764: Строка 1936:
 
'items.size_y',
 
'items.size_y',
 
}
 
}
  +
 
 
--
 
--
 
local prepend = {
 
local prepend = {
Строка 1770: Строка 1942:
 
q_tables=true,
 
q_tables=true,
 
}
 
}
  +
 
 
local query = {}
 
local query = {}
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
 
if prepend[key] then
 
if prepend[key] then
 
value = ',' .. value
 
value = ',' .. value
 
end
 
end
  +
 
 
query[string.sub(key, 3)] = value
 
query[string.sub(key, 3)] = value
 
end
 
end
 
end
 
end
  +
 
  +
-- Namespace condition
  +
-- This is mainly to prevent items from user pages or other testing pages
  +
-- from being returned in the query results.
  +
if tpl_args.namespace ~= 'any' then
  +
local namespace = tonumber(tpl_args.namespace) or cfg.primary_namespace
  +
query.where = string.format('%s AND items._pageNamespace = %i', query.where, namespace)
  +
end
  +
 
local skill_levels = {}
 
local skill_levels = {}
 
for _, rowinfo in ipairs(row_infos) do
 
for _, rowinfo in ipairs(row_infos) do
Строка 1801: Строка 1981:
 
fields[#fields+1] = 'skill.max_level'
 
fields[#fields+1] = 'skill.max_level'
 
tables_assoc.skill = true
 
tables_assoc.skill = true
  +
 
 
end
 
end
  +
 
-- Reformat the tables and fields so they can be retrieved correctly:
+
-- Reformat the tables and fields so they can be retrieved correctly:
 
local tables = {}
 
local tables = {}
 
for table_name,_ in pairs(tables_assoc) do
 
for table_name,_ in pairs(tables_assoc) do
Строка 1811: Строка 1991:
 
local tbls = table.concat(tables,',') .. (query.tables or '')
 
local tbls = table.concat(tables,',') .. (query.tables or '')
 
query.tables = m_util.string.split(tbls, ',')
 
query.tables = m_util.string.split(tbls, ',')
  +
 
 
for index, field in ipairs(fields) do
 
for index, field in ipairs(fields) do
 
fields[index] = string.format('%s=%s', field, field)
 
fields[index] = string.format('%s=%s', field, field)
 
end
 
end
 
query.fields = fields
 
query.fields = fields
  +
 
-- Take care of the minimum required joins, joins from templates
+
-- Take care of the minimum required joins, joins from templates
 
-- must still be userdefined:
 
-- must still be userdefined:
 
local joins = {}
 
local joins = {}
 
for index, table_name in ipairs(tables) do
 
for index, table_name in ipairs(tables) do
 
if table_name ~= 'items' then
 
if table_name ~= 'items' then
joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name)
+
joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name)
 
end
 
end
 
end
 
end
 
if #joins > 0 and query.join then
 
if #joins > 0 and query.join then
query.join = table.concat(joins, ',') .. ',' .. query.join
+
query.join = table.concat(joins, ',') .. ',' .. query.join
 
elseif #joins > 0 and not query.join then
 
elseif #joins > 0 and not query.join then
 
query.join = table.concat(joins, ',')
 
query.join = table.concat(joins, ',')
Строка 1832: Строка 2012:
 
-- leave query.join as is
 
-- leave query.join as is
 
end
 
end
  +
 
 
-- Needed to eliminate duplicates supplied via table joins:
 
-- Needed to eliminate duplicates supplied via table joins:
 
query.groupBy = 'items._pageID' .. (query.groupBy or '')
 
query.groupBy = 'items._pageID' .. (query.groupBy or '')
  +
 
 
-- Query results:
 
-- Query results:
 
local results = m_cargo.query(query.tables, query.fields, query)
 
local results = m_cargo.query(query.tables, query.fields, query)
Строка 1842: Строка 2022:
 
return tpl_args.default
 
return tpl_args.default
 
end
 
end
  +
 
 
if #results > 0 then
 
if #results > 0 then
 
-- Create a list of found pageIDs for column specific queries:
 
-- Create a list of found pageIDs for column specific queries:
for _,v in ipairs(results) do
+
for _,v in ipairs(results) do
 
results2.pageIDs[#results2.pageIDs+1] = v['items._pageID']
 
results2.pageIDs[#results2.pageIDs+1] = v['items._pageID']
 
end
 
end
  +
 
 
-- fetch skill level information
 
-- fetch skill level information
 
if #skill_levels > 0 then
 
if #skill_levels > 0 then
Строка 1874: Строка 2054:
 
end
 
end
 
end
 
end
  +
 
 
if #stat_columns > 0 then
 
if #stat_columns > 0 then
 
local pages = {}
 
local pages = {}
Строка 1880: Строка 2060:
 
pages[#pages+1] = string.format('item_stats._pageID="%s"', row['items._pageID'])
 
pages[#pages+1] = string.format('item_stats._pageID="%s"', row['items._pageID'])
 
end
 
end
  +
 
 
local query_stat_ids = {}
 
local query_stat_ids = {}
 
for stat_id, _ in pairs(query_stats) do
 
for stat_id, _ in pairs(query_stats) do
 
query_stat_ids[#query_stat_ids+1] = string.format('item_stats.id="%s"', stat_id)
 
query_stat_ids[#query_stat_ids+1] = string.format('item_stats.id="%s"', stat_id)
 
end
 
end
  +
 
 
if tpl_args.q_where then
 
if tpl_args.q_where then
 
tpl_args.q_where = string.format(' AND (%s)', tpl_args.q_where)
 
tpl_args.q_where = string.format(' AND (%s)', tpl_args.q_where)
Строка 1891: Строка 2071:
 
tpl_args.q_where = ''
 
tpl_args.q_where = ''
 
end
 
end
  +
 
 
local temp = m_cargo.query(
 
local temp = m_cargo.query(
 
{'items', 'item_stats'},
 
{'items', 'item_stats'},
Строка 1898: Строка 2078:
 
where=string.format('item_stats.is_implicit IS NULL AND (%s) AND (%s)', table.concat(query_stat_ids, ' OR '), table.concat(pages, ' OR ')),
 
where=string.format('item_stats.is_implicit IS NULL AND (%s) AND (%s)', table.concat(query_stat_ids, ' OR '), table.concat(pages, ' OR ')),
 
join='items._pageID=item_stats._pageID',
 
join='items._pageID=item_stats._pageID',
-- Cargo workaround: avoid duplicates using groupBy
+
-- Cargo workaround: avoid duplicates using groupBy
 
groupBy='items._pageID, item_stats.id',
 
groupBy='items._pageID, item_stats.id',
 
}
 
}
Строка 1909: Строка 2089:
 
avg = tonumber(row['item_stats.avg']),
 
avg = tonumber(row['item_stats.avg']),
 
}
 
}
  +
 
 
if results2.stats[row['item_stats._pageName']] == nil then
 
if results2.stats[row['item_stats._pageName']] == nil then
 
results2.stats[row['item_stats._pageName']] = {[row['item_stats.id']] = stat}
 
results2.stats[row['item_stats._pageName']] = {[row['item_stats.id']] = stat}
Строка 1918: Строка 2098:
 
end
 
end
 
end
 
end
  +
 
  +
 
 
--
 
--
 
-- Display the table
 
-- Display the table
 
--
 
--
  +
 
 
local tbl = mw.html.create('table')
 
local tbl = mw.html.create('table')
 
tbl:attr('class', 'wikitable sortable item-table')
 
tbl:attr('class', 'wikitable sortable item-table')
  +
 
 
-- Headers:
 
-- Headers:
 
local tr = tbl:tag('tr')
 
local tr = tbl:tag('tr')
Строка 1933: Строка 2113:
 
:wikitext(modes[tpl_args.mode].header)
 
:wikitext(modes[tpl_args.mode].header)
 
:done()
 
:done()
 
 
for _, row_info in ipairs(row_infos) do
 
for _, row_info in ipairs(row_infos) do
tr
+
local th = tr:tag('th')
  +
:tag('th')
 
:attr('data-sort-type', row_info.sort_type or 'number')
+
if row_info.colspan then
:wikitext(row_info.header)
+
th:attr('colspan', row_info.colspan)
:done()
+
end
  +
  +
th
  +
:attr('data-sort-type', row_info.sort_type or 'number')
  +
:wikitext(row_info.header)
 
end
 
end
  +
 
 
-- Rows:
 
-- Rows:
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
 
tr = tbl:tag('tr')
 
tr = tbl:tag('tr')
  +
 
 
local il_args = {
 
local il_args = {
 
skip_query=true,
 
skip_query=true,
page=row['items._pageName'],
+
page=row['items._pageName'],
name=row['items.name'],
+
name=row['items.name'],
inventory_icon=row['items.inventory_icon'],
+
inventory_icon=row['items.inventory_icon'],
 
width=row['items.size_x'],
 
width=row['items.size_x'],
 
height=row['items.size_y'],
 
height=row['items.size_y'],
 
}
 
}
  +
 
 
if tpl_args.no_html == nil then
 
if tpl_args.no_html == nil then
 
il_args.html = row['items.html']
 
il_args.html = row['items.html']
 
end
 
end
  +
 
 
if tpl_args.large then
 
if tpl_args.large then
 
il_args.large = tpl_args.large
 
il_args.large = tpl_args.large
 
end
 
end
  +
 
 
tr
 
tr
 
:tag('td')
 
:tag('td')
:wikitext(f_item_link(il_args))
+
:wikitext(h.item_link(il_args))
 
:done()
 
:done()
  +
 
 
for _, rowinfo in ipairs(row_infos) do
 
for _, rowinfo in ipairs(row_infos) do
 
-- this has been cast from a function in an earlier step
 
-- this has been cast from a function in an earlier step
Строка 1991: Строка 2174:
 
end
 
end
   
cats = {}
+
local cats = {}
 
if #results == query.limit then
 
if #results == query.limit then
 
cats[#cats+1] = i18n.categories.query_limit
 
cats[#cats+1] = i18n.categories.query_limit
 
end
 
end
  +
 
 
if #results == 0 then
 
if #results == 0 then
 
cats[#cats+1] = i18n.categories.no_results
 
cats[#cats+1] = i18n.categories.no_results
 
end
 
end
  +
 
 
mw.logObject({os.clock() - t, query})
 
mw.logObject({os.clock() - t, query})
  +
 
return tostring(tbl) .. m_util.misc.add_category(cats, {ingore_blacklist=tpl_args.debug})
+
return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist=tpl_args.debug})
 
end
 
end
   
Строка 2012: Строка 2195:
 
function p.map_item_drops(frame)
 
function p.map_item_drops(frame)
 
--[[
 
--[[
Gets the area id from the map item and activates
+
Gets the area id from the map item and activates
 
Template:Area_item_drops.
 
Template:Area_item_drops.
  +
 
 
Examples:
 
Examples:
 
= p.map_item_drops{page='Underground River Map (War for the Atlas)'}
 
= p.map_item_drops{page='Underground River Map (War for the Atlas)'}
 
]]
 
]]
  +
 
 
-- Get args
 
-- Get args
 
local tpl_args = getArgs(frame, {
 
local tpl_args = getArgs(frame, {
Строка 2024: Строка 2207:
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
 
tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())
 
tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())
  +
 
 
local results = m_cargo.query(
 
local results = m_cargo.query(
 
{'maps'},
 
{'maps'},
Строка 2040: Строка 2223:
 
id = results[1]['maps.area_id']
 
id = results[1]['maps.area_id']
 
end
 
end
return frame:expandTemplate{ title = 'Area item drops', args = {area_id=id} }
+
return frame:expandTemplate{ title = 'Area item drops', args = {area_id=id} }
 
end
 
end
   
Строка 2053: Строка 2236:
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
 
tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())
 
tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())
  +
 
 
local results = m_cargo.query(
 
local results = m_cargo.query(
 
{'prophecies'},
 
{'prophecies'},
Строка 2065: Строка 2248:
 
}
 
}
 
)
 
)
  +
 
 
results = results[1]
 
results = results[1]
  +
 
 
local out = {}
 
local out = {}
  +
 
 
if results['prophecies.objective'] then
 
if results['prophecies.objective'] then
 
out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.objective)
 
out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.objective)
 
out[#out+1] = results['prophecies.objective']
 
out[#out+1] = results['prophecies.objective']
 
end
 
end
  +
 
 
if results['prophecies.reward'] then
 
if results['prophecies.reward'] then
 
out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.reward)
 
out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.reward)
 
out[#out+1] = results['prophecies.reward']
 
out[#out+1] = results['prophecies.reward']
 
end
 
end
  +
 
 
return table.concat(out, '\n')
 
return table.concat(out, '\n')
 
end
 
end
Строка 2088: Строка 2271:
 
function h.find_aliases(tpl_args)
 
function h.find_aliases(tpl_args)
 
--[[
 
--[[
This function queries items for an item name, then checks if it has
+
This function queries items for an item name, then checks if it has
 
had any name changes then queries for that name as well.
 
had any name changes then queries for that name as well.
 
]]
 
]]
  +
 
 
-- Get initial name:
 
-- Get initial name:
 
tpl_args.name_list = {
 
tpl_args.name_list = {
 
tpl_args.name or m_util.string.split(
 
tpl_args.name or m_util.string.split(
tostring(mw.title.getCurrentTitle()),
+
tostring(mw.title.getCurrentTitle()),
 
' %('
 
' %('
 
)
 
)
 
}
 
}
  +
 
-- Query for items with similar name, repeat until no new names are
+
-- Query for items with similar name, repeat until no new names are
 
-- found.
 
-- found.
 
local n
 
local n
Строка 2107: Строка 2290:
 
repeat
 
repeat
 
local n_old = #tpl_args.name_list
 
local n_old = #tpl_args.name_list
  +
 
-- Multiple HOLDS doesn't work. Using __FULL and REGEXP instead.
 
 
local where_tbl = {}
 
local where_tbl = {}
 
for _, item_name in ipairs(tpl_args.name_list) do
 
for _, item_name in ipairs(tpl_args.name_list) do
 
for _, prefix in ipairs({'', 'Изменённая '}) do
 
for _, prefix in ipairs({'', 'Изменённая '}) do
 
where_tbl[#where_tbl+1] = string.format(
 
where_tbl[#where_tbl+1] = string.format(
'(items.name_list__FULL REGEXP "(�|^)%s%s(�|$)")',
+
'items__name_list._value = "%s%s"',
 
prefix,
 
prefix,
 
item_name
 
item_name
Строка 2120: Строка 2302:
 
end
 
end
 
local where_str = table.concat(where_tbl, ' OR ')
 
local where_str = table.concat(where_tbl, ' OR ')
  +
 
 
results = m_cargo.query(
 
results = m_cargo.query(
{'items', 'maps'},
+
  +
-- explicitly join to the child list table so that we don't need a REGEXP
  +
{ 'items__name_list', 'items', 'maps'},
 
{
 
{
 
'items._pageName',
 
'items._pageName',
 
'items.name',
 
'items.name',
'items.name_list',
+
  +
-- we need to give this field a different name from `name_list` so that it doesn't conflict
  +
'items.name_list__full=name_list2',
 
'items.release_version',
 
'items.release_version',
 
'items.removal_version',
 
'items.removal_version',
Строка 2132: Строка 2318:
 
},
 
},
 
{
 
{
join='items._pageName=maps._pageName',
+
join='items__name_list._rowID=items._ID, items._pageName=maps._pageName',
 
where=where_str,
 
where=where_str,
 
groupBy='items._pageName',
 
groupBy='items._pageName',
Строка 2138: Строка 2324:
 
}
 
}
 
)
 
)
  +
 
 
-- Filter duplicates:
 
-- Filter duplicates:
 
for i,v in ipairs(results) do
 
for i,v in ipairs(results) do
local r = m_util.string.split(v['items.name_list'], '�')
+
local r = m_util.string.split(v['name_list2'], '�')
 
if type(r) == string then
 
if type(r) == string then
 
r = {r}
 
r = {r}
 
end
 
end
  +
 
 
for j,m in ipairs(r) do
 
for j,m in ipairs(r) do
 
if hash[m] == nil then
 
if hash[m] == nil then
Строка 2154: Строка 2340:
 
end
 
end
 
until #tpl_args.name_list == n_old
 
until #tpl_args.name_list == n_old
  +
 
 
return results
 
return results
 
end
 
end
Строка 2161: Строка 2347:
 
--[[
 
--[[
 
This function finds that items with a name or has had that name.
 
This function finds that items with a name or has had that name.
  +
 
 
To do
 
To do
 
-----
 
-----
 
Should text imply which is the original map, even if it isn't (Original)?
 
Should text imply which is the original map, even if it isn't (Original)?
How to properly sort drop disabled items, with removal version?
+
How to properly sort drop disabled items, with removal version?
 
How to deal with names that have been used multiple times? Terrace Map
 
How to deal with names that have been used multiple times? Terrace Map
  +
 
 
Examples
 
Examples
 
--------
 
--------
Строка 2176: Строка 2362:
 
= p.item_disambiguation{name='Harbinger Map (High Tier)'}
 
= p.item_disambiguation{name='Harbinger Map (High Tier)'}
 
]]
 
]]
  +
 
 
-- Get template arguments.
 
-- Get template arguments.
 
local tpl_args = getArgs(frame, {
 
local tpl_args = getArgs(frame, {
Строка 2182: Строка 2368:
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
 
local current_title = tostring(mw.title.getCurrentTitle())
 
local current_title = tostring(mw.title.getCurrentTitle())
 
-- Get the page name.
 
-- Get the page name.
 
tpl_args.name = tpl_args.name or m_util.string.split(
 
tpl_args.name = tpl_args.name or m_util.string.split(
current_title,
+
current_title,
 
' %('
 
' %('
 
)[1]
 
)[1]
  +
 
 
-- Query for items with similar name.
 
-- Query for items with similar name.
 
local results = h.find_aliases(tpl_args)
 
local results = h.find_aliases(tpl_args)
  +
 
 
-- Format the results:
 
-- Format the results:
 
local out = {}
 
local out = {}
Строка 2202: Строка 2388:
 
local known_release = string.reverse(
 
local known_release = string.reverse(
 
string.match(
 
string.match(
string.reverse(v['items._pageName']),
+
string.reverse(v['items._pageName']),
 
'%)(.-)%('
 
'%)(.-)%('
)
+
) or ''
)
+
)
  +
 
 
local drop_enabled
 
local drop_enabled
if known_release == 'Первое поколение' then
+
if known_release == 'Первое поколение' then
 
drop_enabled = i18n.item_disambiguation.original
 
drop_enabled = i18n.item_disambiguation.original
 
elseif m_util.cast.boolean(v['items.drop_enabled']) then
 
elseif m_util.cast.boolean(v['items.drop_enabled']) then
 
drop_enabled = i18n.item_disambiguation.drop_enabled
 
drop_enabled = i18n.item_disambiguation.drop_enabled
else
+
else
 
drop_enabled = i18n.item_disambiguation.drop_disabled
 
drop_enabled = i18n.item_disambiguation.drop_disabled
 
end
 
end
  +
 
  +
 
if known_release ~= 'Первое поколение' then
+
if known_release ~= 'Первое поколение' and known_release ~= '' then
 
known_release = string.format(
 
known_release = string.format(
i18n.item_disambiguation.known_release,
+
i18n.item_disambiguation.known_release,
 
known_release,
 
known_release,
 
known_release
 
known_release
Строка 2226: Строка 2412:
 
known_release = ''
 
known_release = ''
 
end
 
end
  +
 
 
tbl
 
tbl
 
:tag('li')
 
:tag('li')
 
:wikitext(
 
:wikitext(
 
string.format(
 
string.format(
i18n.item_disambiguation.list_pattern,
+
i18n.item_disambiguation.list_pattern,
f_item_link{page=v['items._pageName']},
+
h.item_link{page=v['items._pageName']},
 
drop_enabled,
 
drop_enabled,
 
known_release
 
known_release
Строка 2240: Строка 2426:
 
end
 
end
 
out[#out+1] = tostring(container)
 
out[#out+1] = tostring(container)
  +
 
 
-- Add a category when the template uses old template inputs:
 
-- Add a category when the template uses old template inputs:
 
local old_args = {
 
local old_args = {
Строка 2251: Строка 2437:
 
'show_current',
 
'show_current',
 
}
 
}
for _,v in ipairs(old_args) do
+
for _,v in ipairs(old_args) do
 
if tpl_args[v] ~= nil then
 
if tpl_args[v] ~= nil then
 
return table.concat(out, '') .. m_util.misc.add_category(
 
return table.concat(out, '') .. m_util.misc.add_category(
Строка 2258: Строка 2444:
 
end
 
end
 
end
 
end
  +
 
return table.concat(out, '')
+
return table.concat(out, '')
end
+
end
   
   
Строка 2266: Строка 2452:
 
--[[
 
--[[
 
Creates a simple list of items.
 
Creates a simple list of items.
  +
 
 
Examples
 
Examples
 
--------
 
--------
 
= p.simple_item_list{
 
= p.simple_item_list{
 
q_tables='maps',
 
q_tables='maps',
q_join='items._pageID=maps._pageID',
+
q_join='items._pageID=maps._pageID',
 
q_where='maps.tier=1 AND items.drop_enabled=1 AND items.rarity_id="normal"',
 
q_where='maps.tier=1 AND items.drop_enabled=1 AND items.rarity_id="normal"',
 
no_html=1,
 
no_html=1,
Строка 2277: Строка 2463:
 
}
 
}
 
]]
 
]]
  +
 
 
-- Args
 
-- Args
 
local tpl_args = getArgs(frame, {
 
local tpl_args = getArgs(frame, {
Строка 2283: Строка 2469:
 
})
 
})
 
frame = m_util.misc.get_frame(frame)
 
frame = m_util.misc.get_frame(frame)
  +
 
 
local query = {}
 
local query = {}
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 fields = {
 
local fields = {
 
'items._pageName',
 
'items._pageName',
Строка 2296: Строка 2482:
 
'items.class',
 
'items.class',
 
}
 
}
  +
 
 
if tpl_args.no_icon == nil then
 
if tpl_args.no_icon == nil then
 
fields[#fields+1] = 'items.inventory_icon'
 
fields[#fields+1] = 'items.inventory_icon'
 
end
 
end
  +
 
 
if tpl_args.no_html == nil then
 
if tpl_args.no_html == nil then
 
fields[#fields+1] = 'items.html'
 
fields[#fields+1] = 'items.html'
 
end
 
end
  +
 
 
local tables = m_util.string.split(tpl_args.q_tables or '', ',%s*')
 
local tables = m_util.string.split(tpl_args.q_tables or '', ',%s*')
 
table.insert(tables, 'items')
 
table.insert(tables, 'items')
  +
 
 
query.groupBy = query.groupBy or 'items._pageID'
 
query.groupBy = query.groupBy or 'items._pageID'
  +
 
 
local results = m_cargo.query(
 
local results = m_cargo.query(
 
tables,
 
tables,
Строка 2315: Строка 2501:
 
query
 
query
 
)
 
)
  +
 
 
local out = {}
 
local out = {}
 
for _, row in ipairs(results) do
 
for _, row in ipairs(results) do
Строка 2323: Строка 2509:
 
else
 
else
 
page = row['items._pageName']
 
page = row['items._pageName']
end
+
end
  +
 
local link = f_item_link{
+
local link = h.item_link{
page=page,
+
page=page,
name=row['items.name'],
+
name=row['items.name'],
inventory_icon=row['items.inventory_icon'] or '',
+
inventory_icon=row['items.inventory_icon'] or '',
html=row['items.html'] or '',
+
html=row['items.html'] or '',
 
skip_query=true
 
skip_query=true
 
}
 
}
  +
 
 
if tpl_args.format == nil then
 
if tpl_args.format == nil then
 
out[#out+1] = string.format('* %s', link)
 
out[#out+1] = string.format('* %s', link)
Строка 2343: Строка 2529:
 
end
 
end
 
end
 
end
  +
 
 
if tpl_args.format == nil then
 
if tpl_args.format == nil then
 
return table.concat(out, '\n')
 
return table.concat(out, '\n')
Строка 2361: Строка 2547:
 
keys = {}
 
keys = {}
 
for _, data in ipairs(tbl) do
 
for _, data in ipairs(tbl) 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
Строка 2369: Строка 2555:
 
end
 
end
 
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

Текущая версия от 17:59, 6 сентября 2023

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

Модуль предоставляет функционал для создания таблиц с предметами.

Шаблоны Item

Модуль:Item2

Все шаблоны, определенные в Модуль:Item2:

Модуль:Item table

Все шаблоны, определенные в Модуль:Item table:

Модуль:Item link

Все шаблоны, определенные в Модуль:Item link:

Модуль:Item acquisition

-------------------------------------------------------------------------------
-- 
--                             Module:Item table
-- 
-- This module implements Template:Item table and other templates that query
-- and display lists of items.
-------------------------------------------------------------------------------

-- Todo list
-- ---------
-- * Handle table columns that can have multiple cargo rows, preferably
--   in one or two queries. Not per column AND row.
-- * Handle template include size? Remove item link when getting close
--   to the limit?
-- * Add a wrapper around f_item_link to be able to disable all
--   hoverboxes, to avoid running into template expansion size issues.

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

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

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

-- Lazy loading
local f_item_link -- require('Module:Item link').item_link
local f_skill_link -- require('Module:Skill link').skill_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:Item table/config/sandbox') or mw.loadData('Module:Item table/config')

local i18n = cfg.i18n

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

local h = {}
h.string = {}

function h.string.format(str, vars)
    --[[
    Allow string replacement using named arguments.
    
    TODO: 
    * Support %d ?
    * Support 0.2f ?

    Parameters
    ----------
    str : String to replace. 
    vars : Table of arguments.

    Examples
    --------
    = h.string.format('{foo} is {bar}.', {foo='Dinner', bar='nice'})

    References
    ----------
    http://lua-users.org/wiki/StringInterpolation
    ]]

    if not vars then
        vars = str
        str = vars[1]
    end
    
    return (string.gsub(str, "({([^}]+)})",
        function(whole, i)
          return vars[i] or whole
        end))
end

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

-- Lazy loading for Module:Skill link
function h.skill_link(args)
    if not f_skill_link then
        f_skill_link = require('Module:Skill link').skill_link
    end
    return f_skill_link(args)
end

function h.na_or_val(tr, value, func)
    if value == nil or value == '' then
        tr:wikitext(m_util.html.td.na())
    else
        local raw_value = value
        if func ~= nil then
            value = func(value)
        end
        tr
            :tag('td')
                :attr('data-sort-value', raw_value)
                :wikitext(value)
                :done()
    end
end

h.tbl = {}

function h.tbl.range_fields(args)
    local suffixes = {'maximum', 'text', 'colour'}
    if args.full then
        suffixes[#suffixes+1] = 'minimum'
    end
        
    return function()
        local fields = {}
        local inner = function (field)
            for _, partial_field in ipairs(suffixes) do
                fields[#fields+1] = string.format('%s_range_%s', field, partial_field)
            end
        end
        
        if type(args.field) == 'table' then 
            for _, field in ipairs(args.field) do
                inner(field)
            end
        else
            inner(args.field)
        end
        return fields
    end
end

h.tbl.display = {}
function h.tbl.display.na_or_val(tr, value, data)
    return h.na_or_val(tr, value)
end

function h.tbl.display.seconds(tr, value, data)
    return h.na_or_val(tr, value, function(value)
        return string.format('%ss', value)
    end)
end

function h.tbl.display.percent(tr, value, data)
    return h.na_or_val(tr, value, function(value)
        return string.format('%s%%', value)
    end)
end

function h.tbl.display.wikilink(tr, value, data)
    return h.na_or_val(tr, value, function(value)
        return string.format('[[%s]]', value)
    end)
end

h.tbl.display.factory = {}
function h.tbl.display.factory.value(args)
    args.options = args.options or {}

    return function(tr, data, fields, data2)
        local values = {}
        local fmt_values = {}
        local sdata = data2.skill_levels[data['items._pageName']]

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

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

function h.tbl.display.factory.range(args)
    -- args: table
    --  property
    return function (tr, data, fields)
        tr
            :tag('td')
                :attr('data-sort-value', data[string.format('%s_range_maximum', args.field)] or '0')
                :attr('class', 'tc -' .. (data[string.format('%s_range_colour', args.field)] or 'default'))
                :wikitext(data[string.format('%s_range_text', args.field)])
                :done()
    end
end

function h.tbl.display.factory.range_composite(args)
    -- division by default
    if args.func == nil then
        args.func = function (a, b)
            if b == 0 then
                return 'fail'
            end
            return a / b
        end
    end
    
    return function(tr, data, fields)
        local field = {}
        for i=1, 2 do 
            local fieldn = args['field' .. i]
            field[i] = {
                min = tonumber(data[string.format('%s_range_minimum', fieldn)]) or 0,
                max = tonumber(data[string.format('%s_range_maximum', fieldn)]) or 0,
                color = data[string.format('%s_range_colour', fieldn)] or 'default',
            }
        end
        
        field.min = args.func(field[1].min, field[2].min)
        field.max = args.func(field[1].max, field[2].max)
        
        if field.min == 'fail' or field.max == 'fail' then
            field.text = ''
            field.color = 'default'
        else
            for i=1, 2 do
                if field[i].color ~= 'default' then
                    field.color = field[i].color
                    break
                end
            end
            if field.min == field.max then
                field.text = string.format('%.2f', field.min)
            else
                field.text = string.format('(%.2f-%.2f)', field.min, field.max)
            end
        end
    
        tr
            :tag('td')
                :attr('data-sort-value', field.max)
                :attr('class', 'tc -' .. field.color)
                :wikitext(field.text)
                :done()
    end
end

function h.tbl.display.factory.descriptor_value(args)
    -- Arguments:
    --  key
    --  tbl
    args = args or {}
    return function (tpl_args, frame, value)
        args.tbl = args.tbl or tpl_args
        if args.tbl[args.key] then
            value = m_util.html.abbr(value, args.tbl[args.key])
        end
        return value
    end
end

function h.tbl.display.factory.atlas_tier(args)
    args = args or {}
    return function (tr, data)
        for i=0,4 do
            local t = tonumber(data['atlas_maps.map_tier' .. i])

            if t == 0 then
                tr
                    :tag('td')
                        :attr('table-sort-value', 0)
                        :attr('class', 'table-cell-xmark')
                        :wikitext('✗')
            else
                if args.is_level then
                    t = t + 67
                end
                tr
                    :tag('td')
                        :attr('class', 'tc -value')
                        :wikitext(t)
                        :done()
            end
        end
    end
end

-- ----------------------------------------------------------------------------
-- Data mappings
-- ----------------------------------------------------------------------------

local data_map = {}

-- for sort type see:
-- https://meta.wikimedia.org/wiki/Help:Sorting
data_map.generic_item = {
    {
        arg = 'base_item',
        header = i18n.item_table.base_item,
        fields = {'items.base_item', 'items.base_item_page'},
        display = function(tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data['items.base_item'])
                    :wikitext(string.format('[[%s|%s]]', data['items.base_item_page'], data['items.base_item']))
        end,
        order = 1000,
        sort_type = 'text',
    },
    {
        arg = 'class',
        header = i18n.item_table.item_class,
        fields = {'items.class'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='[[%s]]',
            },
        }},
        order = 1001,
        sort_type = 'text',
    },
    {
        arg = 'rarity',
        header = i18n.item_table.rarity,
        fields = {'items.rarity'},
        display = h.tbl.display.factory.value{},
        order = 1002,
    },
    {
        arg = 'rarity_id',
        header = i18n.item_table.rarity_id,
        fields = {'items.rarity_id'},
        display = h.tbl.display.factory.value{},
        order = 1003,
    },
    {
        arg = 'metadata_id',
        header = i18n.item_table.metadata_id,
        fields = {'items.metadata_id'},
        display = h.tbl.display.factory.value{},
        order = 1004,
    },
    {
        arg = 'essence',
        header = i18n.item_table.essence_level,
        fields = {'essences.level'},
        display = h.tbl.display.factory.value{},
        order = 2000,
    },
    {
        arg = {'drop', 'drop_level'},
        header = i18n.item_table.drop_level,
        fields = {'items.drop_level'},
        display = h.tbl.display.factory.value{},
        order = 3000,
    },
    {
        arg = 'stack_size',
        header = i18n.item_table.stack_size,
        fields = {'stackables.stack_size'},
        display = h.tbl.display.factory.value{},
        order = 4000,
    },
    {
        arg = 'stack_size_currency_tab',
        header = i18n.item_table.stack_size_currency_tab,
        fields = {'stackables.stack_size_currency_tab'},
        display = h.tbl.display.factory.value{},
        order = 4001,
    },
    {
        arg = 'level',
        header = m_game.level_requirement.icon,
        fields = h.tbl.range_fields{field='items.required_level'},
        display = h.tbl.display.factory.range{field='items.required_level'},
        order = 5000,
    },
    {
        arg = 'ar',
        header = i18n.item_table.armour,
        fields = h.tbl.range_fields{field='armours.armour'},
        display = h.tbl.display.factory.range{field='armours.armour'},
        order = 6000,
    },
    {
        arg = 'ev',
        header =i18n.item_table.evasion,
        fields = h.tbl.range_fields{field='armours.evasion'},
        display = h.tbl.display.factory.range{field='armours.evasion'},
        order = 6001,
    },
    {
        arg = 'es',
        header = i18n.item_table.energy_shield,
        fields = h.tbl.range_fields{field='armours.energy_shield'},
        display = h.tbl.display.factory.range{field='armours.energy_shield'},
        order = 6002,
    },
    {
        arg = 'wd',
        header = i18n.item_table.ward,
        fields = h.tbl.range_fields{field='armours.ward'},
        display = h.tbl.display.factory.range{field='armours.ward'},
        order = 6003,
    },
    {
        arg = 'block',
        header = i18n.item_table.block,
        fields = h.tbl.range_fields{field='shields.block'},
        display = h.tbl.display.factory.range{field='shields.block'},
        order = 6004,
    },
    --[[{
        arg = 'physical_damage_min',
        header = m_util.html.abbr('Min', 'Local minimum weapon damage'),
        fields = h.tbl.range_fields('minimum physical damage'),
        display = h.tbl.display.factory.range{field='minimum physical damage'},
        order = 7000,
    },
    {
        arg = 'physical_damage_max',
        header = m_util.html.abbr('Max', 'Local maximum weapon damage'),
        fields = h.tbl.range_fields('maximum physical damage'),
        display = h.tbl.display.factory.range{field='maximum physical damage'},
        order = 7001,

    },]]--
    {
        arg = {'weapon', 'damage'},
        header = i18n.item_table.damage,
        fields = {'weapons.damage_html', 'weapons.damage_avg'},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data['weapons.damage_avg'])
                    :wikitext(data['weapons.damage_html'])
        end,
        order = 8000,
    },
    {
        arg = {'weapon', 'aps'},
        header = i18n.item_table.attacks_per_second,
        fields = h.tbl.range_fields{field='weapons.attack_speed'},
        display = h.tbl.display.factory.range{field='weapons.attack_speed'},
        order = 8001,
    },
    {
        arg = {'weapon', 'crit'},
        header = i18n.item_table.local_critical_strike_chance,
        fields = h.tbl.range_fields{field='weapons.critical_strike_chance'},
        display = h.tbl.display.factory.range{field='weapons.critical_strike_chance'},
        order = 8002,
    },
    {
        arg = {'physical_dps'},
        header = i18n.item_table.physical_dps,
        fields = h.tbl.range_fields{field='weapons.physical_dps'},
        display = h.tbl.display.factory.range{field='weapons.physical_dps'},
        order = 8100,
    },
    {
        arg = {'lightning_dps'},
        header = i18n.item_table.lightning_dps,
        fields = h.tbl.range_fields{field='weapons.lightning_dps'},
        display = h.tbl.display.factory.range{field='weapons.lightning_dps'},
        order = 8101,
    },
    {
        arg = {'cold_dps'},
        header = i18n.item_table.cold_dps,
        fields = h.tbl.range_fields{field='weapons.cold_dps'},
        display = h.tbl.display.factory.range{field='weapons.cold_dps'},
        order = 8102,
    },
    {
        arg = {'fire_dps'},
        header = i18n.item_table.fire_dps,
        fields = h.tbl.range_fields{field='weapons.fire_dps'},
        display = h.tbl.display.factory.range{field='weapons.fire_dps'},
        order = 8103,
    },
    {
        arg = {'chaos_dps'},
        header = i18n.item_table.chaos_dps,
        fields = h.tbl.range_fields{field='weapons.chaos_dps'},
        display = h.tbl.display.factory.range{field='weapons.chaos_dps'},
        order = 8104,
    },
    {
        arg = {'elemental_dps'},
        header = i18n.item_table.elemental_dps,
        fields = h.tbl.range_fields{field='weapons.elemental_dps'},
        display = h.tbl.display.factory.range{field='weapons.elemental_dps'},
        order = 8105,
    },
    {
        arg = {'poison_dps'},
        header = i18n.item_table.poison_dps,
        fields = h.tbl.range_fields{field='weapons.poison_dps'},
        display = h.tbl.display.factory.range{field='weapons.poison_dps'},
        order = 8106,
    },
    {
        arg = {'dps'},
        header = i18n.item_table.dps,
        fields = h.tbl.range_fields{field='weapons.dps'},
        display = h.tbl.display.factory.range{field='weapons.dps'},
        order = 8107,
    },
    {
        arg = 'flask_life',
        header = i18n.item_table.flask_life,
        fields = h.tbl.range_fields{field='flasks.life'},
        display = h.tbl.display.factory.range{field='flasks.life'},
        order = 9000,
    },
    {
        arg = 'flask_life_per_second',
        header = i18n.item_table.flask_life_per_second,
        fields = h.tbl.range_fields{field={'flasks.life', 'flasks.duration'}, full=true},
        display = h.tbl.display.factory.range_composite{field1='flasks.life', field2='flasks.duration'},
        order = 9001,
    },
    {
        arg = 'flask_life_per_charge',
        header = i18n.item_table.flask_life_per_charge,
        fields = h.tbl.range_fields{field={'flasks.life', 'flasks.charges_per_use'}, full=true},
        display = h.tbl.display.factory.range_composite{field1='flasks.life', field2='flasks.charges_per_use'},
        order = 9002,
    },
    {
        arg = 'flask_mana',
        header = i18n.item_table.flask_mana,
        fields = h.tbl.range_fields{field='flasks.mana'},
        display = h.tbl.display.factory.range{field='flasks.mana'},
        order = 9010,
    },
    {
        arg = 'flask_mana_per_second',
        header = i18n.item_table.flask_mana_per_second,
        fields = h.tbl.range_fields{field={'flasks.mana', 'flasks.duration'}, full=true},
        display = h.tbl.display.factory.range_composite{field1='flasks.mana', field2='flasks.duration'},
        order = 9011,
    },
    {
        arg = 'flask_mana_per_charge',
        header = i18n.item_table.flask_mana_per_charge,
        fields = h.tbl.range_fields{field={'flasks.mana', 'flasks.charges_per_use'}, full=true},
        display = h.tbl.display.factory.range_composite{field1='flasks.mana', field2='flasks.charges_per_use'},
        order = 9012,
    },
    {
        arg = 'flask',
        header = i18n.item_table.flask_duration,
        fields = h.tbl.range_fields{field='flasks.duration'},
        display = h.tbl.display.factory.range{field='flasks.duration'},
        order = 9020,
    },
    {
        arg = 'flask',
        header = i18n.item_table.flask_charges_per_use,
        fields = h.tbl.range_fields{field='flasks.charges_per_use'},
        display = h.tbl.display.factory.range{field='flasks.charges_per_use'},
        order = 9030,
    },
    {
        arg = 'flask',
        header = i18n.item_table.flask_maximum_charges,
        fields = h.tbl.range_fields{field='flasks.charges_max'},
        display = h.tbl.display.factory.range{field='flasks.charges_max'},
        order = 9031,
    },
    -- Seed data 91xx,
    {
        arg = {'seed', 'seed_type'},
        header = i18n.item_table.seed_type,
        fields = {'harvest_seeds.type', 'harvest_seeds.type_id'},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('table-sort-value', data['harvest_seeds.type'])
                    :attr('class', 'tc -' .. data['harvest_seeds.type_id'])
                    :wikitext(data['harvest_seeds.type'])
        end,
        order = 9100,
    },
    {
        arg = {'seed', 'seed_tier'},
        header = i18n.item_table.seed_tier,
        fields = {'harvest_seeds.tier'},
        display = h.tbl.display.factory.value{},
        order = 9101,
    },
    {
        arg = {'seed', 'seed_growth_cycles'},
        header = i18n.item_table.seed_growth_cycles,
        fields = {'harvest_seeds.growth_cycles'},
        display = h.tbl.display.factory.value{},
        order = 9102,
    },
    {
        arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_primal_lifeforce_percentage'},
        header = i18n.item_table.seed_consumed_primal_lifeforce_percentage,
        fields = {'harvest_seeds.consumed_primal_lifeforce_percentage'},
        display = h.tbl.display.factory.value{color='primal'},
        order = 9110,
    },
    {
        arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_vivid_lifeforce_percentage'},
        header = i18n.item_table.seed_consumed_vivid_lifeforce_percentage,
        fields = {'harvest_seeds.consumed_vivid_lifeforce_percentage'},
        display = h.tbl.display.factory.value{color='vivid'},
        order = 9110,
    },
    {
        arg = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_wild_lifeforce_percentage'},
        header = i18n.item_table.seed_consumed_wild_lifeforce_percentage,
        fields = {'harvest_seeds.consumed_wild_lifeforce_percentage'},
        display = h.tbl.display.factory.value{color='wild'},
        order = 9110,
    },
    {
        arg = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_amount'},
        header = i18n.item_table.seed_required_nearby_seed_amount,
        fields = {'harvest_seeds.required_nearby_seed_amount'},
        display = h.tbl.display.factory.value{},
        order = 9113,
    },
    {
        arg = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_tier'},
        header = i18n.item_table.seed_required_nearby_seed_tier,
        fields = {'harvest_seeds.required_nearby_seed_tier'},
        display = h.tbl.display.factory.value{},
        order = 9114,
    },
    -- 9120 show crafting options?
    {
        arg = 'item_limit',
        header = i18n.item_table.item_limit,
        fields = {'jewels.item_limit'},
        display = h.tbl.display.factory.value{},
        order = 10000,
    },
    {
        arg = 'jewel_radius',
        header = i18n.item_table.jewel_radius,
        fields = {'jewels.radius_html'},
        display = function (tr, data)
            tr
                :tag('td')
                    :wikitext(data['jewels.radius_html'])
        end,
        order = 10001,
    },
    {
        arg = 'map_tier',
        header = i18n.item_table.map_tier,
        fields = {'maps.tier'},
        display = h.tbl.display.factory.value{},
        order = 11000,
    },
    {
        arg = 'map_level',
        header = i18n.item_table.map_level,
        fields = {'maps.area_level'},
        display = h.tbl.display.factory.value{},
        order = 11010,
    },
    {
        arg = 'map_guild_character',
        header = i18n.item_table.map_guild_character,
        fields = {'maps.guild_character'},
        display = h.tbl.display.factory.value{},
        order = 11020,
        sort_type = 'text',
    },
    {
        arg = 'atlas_tier',
        header = i18n.item_table.atlas_tier,
        colspan = 5,
        fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
        display = h.tbl.display.factory.atlas_tier{is_level=false},
        order = 11050,
    },
    {
        arg = 'atlas_level',
        header = i18n.item_table.atlas_level,
        colspan = 5,
        fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
        display = h.tbl.display.factory.atlas_tier{is_level=true},
        order = 11060,
    },
    {
        arg = {'doodad', 'master_level_requirement'},
        header = i18n.item_table.master_level_requirement,
        fields = {'hideout_doodads.level_requirement'},
        display = h.tbl.display.factory.value{},
        order = 11100,
    },
    {
        arg = {'doodad', 'master'},
        header = i18n.item_table.master,
        fields = {'hideout_doodads.master'},
        display = h.tbl.display.factory.value{},
        order = 11101,
        sort_type = 'text',
    },
    {
        arg = {'doodad', 'master_favour_cost'},
        header = i18n.item_table.master_favour_cost,
        fields = {'hideout_doodads.favour_cost'},
        display = h.tbl.display.factory.value{colour='currency'},
        order = 11102,
    },
    {
        arg = {'doodad', 'variation_count'},
        header = i18n.item_table.variation_count,
        fields = {'hideout_doodads.variation_count'},
        display = h.tbl.display.factory.value{colour='mod'},
        order = 11105,
    },
    {
        arg = 'buff',
        header = i18n.item_table.buff_effects,
        fields = {'item_buffs.stat_text'},
        display = h.tbl.display.factory.value{colour='mod'},
        order = 12000,
        sort_type = 'text',
    },
    {
        arg = 'stat',
        header = i18n.item_table.stats,
        fields = {'items.stat_text'},
        display = h.tbl.display.factory.value{colour='mod'},
        order = 12001,
        sort_type = 'text',
    },
    {
        arg = 'description',
        header = i18n.item_table.effects,
        fields = {'items.description'},
        display = h.tbl.display.factory.value{colour='mod'},
        order = 12002,
        sort_type = 'text',
    },
    {
        arg = {'seed', 'seed_effect'},
        header = i18n.item_table.effects,
        fields = {'harvest_seeds.effect'},
        display = h.tbl.display.factory.value{colour='crafted'},
        order = 12003,
        sort_type = 'text',
    },
    {
        arg = 'incubator_effect',
        header = i18n.item_table.incubator_effect,
        fields = {'incubators.effect'},
        display = h.tbl.display.factory.value{colour='crafted'},
        order = 12004,
        sort_type = 'text',
    },
    {
        arg = 'flavour_text',
        header = i18n.item_table.flavour_text,
        fields = {'items.flavour_text'},
        display = h.tbl.display.factory.value{colour='flavour'},
        order = 12006,
        sort_type = 'text',
    },
    {
        arg = 'help_text',
        header = i18n.item_table.help_text,
        fields = {'items.help_text'},
        display = h.tbl.display.factory.value{colour='help'},
        order = 12008,
        sort_type = 'text',
    },
    {
        arg = {'prophecy', 'objective'},
        header = i18n.item_table.objective,
        fields = {'prophecies.objective'},
        display = h.tbl.display.factory.value{},
        order = 13002,
    },
        {
        arg = {'prophecy', 'reward'},
        header = i18n.item_table.reward,
        fields = {'prophecies.reward'},
        display = h.tbl.display.factory.value{},
        order = 13001,
    },
    {
        arg = {'prophecy', 'seal_cost'},
        header = i18n.item_table.seal_cost,
        fields = {'prophecies.seal_cost'},
        display = h.tbl.display.factory.value{colour='currency'},
        order = 13002,
    },
    {
        arg = {'prediction_text'},
        header = i18n.item_table.prediction_text,
        fields = {'prophecies.prediction_text'},
        display = h.tbl.display.factory.value{colour='value'},
        order = 12004,
        sort_type = 'text',
    },
    {
        arg = 'buff_icon',
        header = i18n.item_table.buff_icon,
        fields = {'item_buffs.icon'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='[[%s]]',
            },
        }},
        order = 14000,
        sort_type = 'text',
    },
    {
        arg = {'version', 'release_version'},
        header = i18n.item_table.release_version,
        fields = {'items.release_version'},
        display = function(tr, data)
            tr
                :tag('td')
                    :wikitext(
                        string.format(
                            i18n.item_table.version_link,
                            data['items.release_version'],
                            data['items.release_version']
                        )
                    )
        end,
        order = 15000,
    },
    {
        arg = {'version', 'removal_version'},
        header = i18n.item_table.removal_version,
        fields = {'items.removal_version'},
        display = function(tr, data)
            tr
                :tag('td')
                    :wikitext(
                        string.format(
                            i18n.item_table.version_link,
                            data['items.removal_version'],
                            data['items.removal_version']
                        )
                    )
        end,
        order = 15001,
    },
    {
        arg = {'drop', 'drop_enabled'},
        header = i18n.item_table.drop_enabled,
        fields = {'items.drop_enabled'},
        display = h.tbl.display.factory.value{},
        order = 15002,
    },
    {
        arg = {'drop', 'drop_leagues'},
        header = i18n.item_table.drop_leagues,
        fields = {'items.drop_leagues'},
        display = function(tr, data)
            local s = m_util.string.split(data['items.drop_leagues'], ',')
            for i, v in ipairs(s) do
                s[i] = string.format(i18n.item_table.drop_leagues_link, v, v)
            end
            tr
                :tag('td')
                    :wikitext(table.concat(s, '<br>'))
        end,
        order = 15003,
        sort_type = 'text',
    },
    {
        arg = {'drop', 'drop_areas'},
        header = i18n.item_table.drop_areas,
        fields = {'items.drop_areas_html'},
        display = h.tbl.display.factory.value{},
        order = 15004,
        sort_type = 'text',
    },
    {
        arg = {'drop', 'drop_monsters'},
        header = i18n.item_table.drop_monsters,
        fields = {'items.drop_monsters'},
        display = function(tr, data, na, results2)
            if results2['drop_monsters_query'] == nil then
                results2['drop_monsters_query'] = m_cargo.query(
                    {'items', 'monsters', 'main_pages'},
                    {
                        'items._pageName',
                        'monsters._pageName',
                        'monsters.name',
                        'main_pages._pageName',
                    },
                    {
                        join=[[
                            items.drop_monsters HOLDS monsters.metadata_id,
                            monsters.metadata_id=main_pages.id
                        ]],
                        where=string.format([[
                            items._pageID IN (%s)
                            AND monsters.metadata_id IS NOT NULL
                        ]],
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='items.drop_monsters',
                    }
                )

                results2['drop_monsters_query'] = m_cargo.map_results_to_id{
                    results=results2['drop_monsters_query'],
                    field='items._pageName',
                }
            end
            local results = results2['drop_monsters_query'][data['items._pageName']] or {}
            local tbl = {}
            for _,v in ipairs(results) do
                local page = v['main_pages._pageName'] or v['monsters._pageName'] or ''
                local name = v['monsters.name'] or v['items.drop_monsters'] or ''
                tbl[#tbl+1] = string.format('[[%s|%s]]', page, name)
            end
            h.na_or_val(tr, table.concat(tbl, '<br>'))
        end,
        order = 15005,
        sort_type = 'text',
    },
    {
        arg = {'drop', 'drop_text'},
        header = i18n.item_table.drop_text,
        fields = {'items.drop_text'},
        display = h.tbl.display.factory.value{},
        order = 15006,
        sort_type = 'text',
    },
    {
        arg = {'quest'},
        header = i18n.item_table.quest_rewards,
        fields = {'items._pageName'},
        display = function(tr, data, na, results2)
            if results2['quest_query'] == nil then
                results2['quest_query'] = m_cargo.query(
                    {'items', 'quest_rewards'},
                    {
                        'quest_rewards._pageName',
                        'quest_rewards.classes',
                        'quest_rewards.act',
                        'quest_rewards.quest'
                    },
                    {
                        join='items._pageName=quest_rewards._pageName',
                        where=string.format(
                            'items._pageID IN (%s) AND quest_rewards._pageName IS NOT NULL',
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='quest_rewards.act, quest_rewards.quest',
                    }
                )
                results2['quest_query'] = m_cargo.map_results_to_id{
                    results=results2['quest_query'],
                    field='quest_rewards._pageName',
                }
            end

            local results = results2['quest_query'][data['items._pageName']] or {}
            local tbl = {}
            for _, v in ipairs(results) do
                local classes = table.concat(m_util.string.split(v['quest_rewards.classes'] or '', ',%s*'), ', ')
                if classes == '' or classes == nil then
                    classes = i18n.item_table.quest_rewards_any_classes
                end

                tbl[#tbl+1] = string.format(
                    i18n.item_table.quest_rewards_row_format,
                    v['quest_rewards.act'],
                    v['quest_rewards.quest'],
                    classes
                )
            end

            local value = table.concat(tbl, '<br>')
            if value == nil or value == '' then
                tr:wikitext(m_util.html.td.na())
            else
                tr
                    :tag('td')
                        :attr('style', 'text-align:left')
                        :wikitext(value)
            end
        end,
        order = 16001,
        sort_type = 'text',
    },
    {
        arg = {'vendor'},
        header = i18n.item_table.vendor_rewards,
        fields = {'items._pageName'},
        display = function(tr, data, na, results2)
            if results2['vendor_query'] == nil then
                results2['vendor_query'] = m_cargo.query(
                    {'items', 'vendor_rewards'},
                    {
                        'vendor_rewards._pageName',
                        'vendor_rewards.classes',
                        'vendor_rewards.act',
                        'vendor_rewards.npc',
                        'vendor_rewards.quest',
                    },
                    {
                        join='items._pageName=vendor_rewards._pageName',
                        where=string.format(
                            'items._pageID IN (%s) AND vendor_rewards._pageName IS NOT NULL',
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='vendor_rewards.act, vendor_rewards.quest',
                    }
                )
                results2['vendor_query'] = m_cargo.map_results_to_id{
                    results=results2['vendor_query'],
                    field='vendor_rewards._pageName',
                }
            end
            local results = results2['vendor_query'][data['items._pageName']] or {}

            local tbl = {}
            for _, v in ipairs(results) do
                local classes = table.concat(m_util.string.split(v['vendor_rewards.classes'] or '', ',%s*'), ', ')
                if classes == '' or classes == nil then
                    classes = i18n.item_table.vendor_rewards_any_classes
                end

                tbl[#tbl+1] = string.format(
                    i18n.item_table.vendor_rewards_row_format,
                    v['vendor_rewards.act'],
                    v['vendor_rewards.quest'],
                    v['vendor_rewards.npc'],
                    classes
                )
            end

            local value = table.concat(tbl, '<br>')
            if value == nil or value == '' then
                tr:wikitext(m_util.html.td.na())
            else
                tr
                    :tag('td')
                        :attr('style', 'text-align:left')
                        :wikitext(value)
            end
        end,
        order = 17001,
        sort_type = 'text',
    },
    {
        arg = {'price', 'purchase_cost'},
        header = i18n.item_table.purchase_costs,
        fields = {'item_purchase_costs.name', 'item_purchase_costs.amount'},
        display = function (tr, data)
            -- Can purchase costs have multiple currencies and rows?
            -- Just switch to the same method as in sell_price then.
            local tbl = {}
            if data['item_purchase_costs.name'] ~= nil then
                tbl[#tbl+1] = string.format(
                        '%sx %s',
                        data['item_purchase_costs.amount'],
                        h.item_link{data['item_purchase_costs.name']}
                    )
            end
            h.na_or_val(tr, table.concat(tbl, '<br>'))
        end,
        order = 18001,
        sort_type = 'text',
    },
    {
        arg = {'price', 'sell_price'},
        header = i18n.item_table.sell_price,
        fields = {'item_sell_prices.name', 'item_sell_prices.amount'},
        display = function(tr, data, na, results2)
            if results2['sell_price_query'] == nil then
                results2['sell_price_query'] = m_cargo.query(
                    {'items', 'item_sell_prices'},
                    {
                        'item_sell_prices.name',
                        'item_sell_prices.amount',
                        'item_sell_prices._pageID'
                    },
                    {
                        join='items._pageID=item_sell_prices._pageID',
                        where=string.format(
                            'items._pageID IN (%s) AND item_sell_prices._pageID IS NOT NULL',
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='item_sell_prices.name',
                    }
                )
                results2['sell_price_query'] = m_cargo.map_results_to_id{
                    results=results2['sell_price_query'],
                    field='item_sell_prices._pageID',
                }
            end
            local results = results2['sell_price_query'][data['items._pageID']] or {}

            local tbl = {}
            for _,v in ipairs(results) do
                tbl[#tbl+1] = string.format(
                    '%sx %s',
                    v['item_sell_prices.amount'],
                    h.item_link{v['item_sell_prices.name']}
                )
            end
            h.na_or_val(tr, table.concat(tbl, '<br>'))
        end,
        order = 18002,
        sort_type = 'text',
    },
    {
        arg = {'boss', 'boss_name'},
        header = i18n.item_table.boss_name,
        fields = {'maps.area_id'},
        display = function(tr, data, na, results2)
            if results2['boss_query'] == nil then
                results2['boss_query'] = m_cargo.query(
                    {'items', 'maps', 'areas', 'monsters', 'main_pages'},
                    {
                        'items._pageName',
                        'maps.area_id',
                        'areas.id',
                        'areas.boss_monster_ids',
                        'monsters._pageName',
                        'monsters.name',
                        'main_pages._pageName',
                    },
                    {
                        join=[[
                            items._pageID=maps._pageID,
                            maps.area_id=areas.id,
                            areas.boss_monster_ids HOLDS monsters.metadata_id,
                            monsters.metadata_id=main_pages.id
                        ]],
                        where=string.format([[
                            items._pageID IN (%s)
                            AND maps.area_id IS NOT NULL
                            AND areas.boss_monster_ids HOLDS LIKE "%%"
                        ]],
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='areas.boss_monster_ids',
                    }
                )

                results2['boss_query'] = m_cargo.map_results_to_id{
                    results=results2['boss_query'],
                    field='items._pageName',
                }
            end
            local results = results2['boss_query'][data['items._pageName']] or {}
            local tbl = {}
            for _,v in ipairs(results) do
                local page = v['main_pages._pageName'] or v['monsters._pageName'] or ''
                local name = v['monsters.name'] or v['areas.boss_monster_ids'] or ''
                tbl[#tbl+1] = string.format('[[%s|%s]]', page, name)
            end
            h.na_or_val(tr, table.concat(tbl, '<br>'))
        end,
        order = 19001,
        sort_type = 'text',
    },
    {
        arg = {'boss', 'boss_number'},
        header = i18n.item_table.boss_number,
        fields = {'maps.area_id'},
        display = function(tr, data, na, results2)
            if results2['boss_query'] == nil then
                results2['boss_query'] = m_cargo.query(
                    {'items', 'maps', 'areas', 'monsters', 'main_pages'},
                    {
                        'items._pageName',
                        'maps.area_id',
                        'areas.id',
                        'areas.boss_monster_ids',
                        'monsters._pageName',
                        'monsters.name',
                        'main_pages._pageName',
                    },
                    {
                        join=[[
                            items._pageID=maps._pageID,
                            maps.area_id=areas.id,
                            areas.boss_monster_ids HOLDS monsters.metadata_id,
                            monsters.metadata_id=main_pages.id
                        ]],
                        where=string.format([[
                            items._pageID IN (%s)
                            AND maps.area_id IS NOT NULL
                            AND areas.boss_monster_ids HOLDS LIKE "%%"
                        ]],
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='areas.boss_monster_ids',
                    }
                )

                results2['boss_query'] = m_cargo.map_results_to_id{
                    results=results2['boss_query'],
                    field='items._pageName',
                }
            end
            local results = results2['boss_query'][data['items._pageName']] or {}
            local tbl = {}
            for _,v in ipairs(results) do
                tbl[#tbl+1] = v['areas.boss_monster_ids']
            end
            h.na_or_val(tr, #tbl)
        end,
        order = 19002,
    },
    {
        arg = {'legacy'},
        header = i18n.item_table.legacy,
        fields = {'items.name'},
        display = function(tr, data, na, results2)
            if results2['legacy_query'] == nil then
                results2['legacy_query'] = m_cargo.query(
                    {'items', 'legacy_variants'},
                    {
                        'items._pageID',
                        'items._pageName',
                        'items.frame_type',
                        'legacy_variants.removal_version',
                        'legacy_variants.implicit_stat_text',
                        'legacy_variants.explicit_stat_text',
                        'legacy_variants.stat_text',
                        'legacy_variants.base_item',
                        'legacy_variants.required_level'
                    },
                    {
                        join='items._pageID=legacy_variants._pageID',
                        where='legacy_variants.removal_version IS NOT NULL',
                        where=string.format(
                            'items._pageID IN (%s) AND legacy_variants.removal_version IS NOT NULL',
                            table.concat(results2.pageIDs, ', ')
                        ),
                        orderBy='items._pageName',
                    }
                )

                results2['legacy_query'] = m_cargo.map_results_to_id{
                    results=results2['legacy_query'],
                    field='items._pageName',
                }
            end
            local results = results2['legacy_query'][data['items._pageName']] or {}

            local tbl = mw.html.create('table')
                :attr('width', '100%')
            for _, v in ipairs(results) do
                local cell = {}
                local l = {
                    'legacy_variants.base_item',
                    'legacy_variants.stat_text'
                }

                -- Clean up data:
                for _, k in ipairs(l) do
                    if v[k] ~= nil then
                        local s = m_util.string.split(v[k], '*')
                        local s_flt = {}
                        for _, sss in ipairs(s) do
                            if sss ~= nil and sss ~= '' then
                                s_flt[#s_flt+1] = string.gsub(sss, '\n', '')
                            end
                        end

                        cell[#cell+1] = table.concat(s_flt, '<br>')
                    end
                end

                local sep = string.format(
                    '<span class="item-stat-separator -%s"></span>',
                    v['items.frame_type']
                )
                tbl
                    :tag('tr')
                        :attr('class', 'upgraded-from-set')
                        :tag('td')
                            :wikitext(
                                v['legacy_variants.removal_version']
                            )
                            :done()
                        :tag('td')
                            :attr('class', 'group legacy-stats plainlist')
                            :wikitext(table.concat(cell, sep))
                            :done()
                    :done()
            end
            tr
                :tag('td')
                    :node(tbl)
        end,
        order = 21001,
        sort_type = 'text',
    },
    {
        arg = {'granted_skills'},
        header = i18n.item_table.granted_skills,
        fields = {'items.name'},
        display = function(tr, data, na, results2)
            if results2['granted_skills_query'] == nil then
                results2['granted_skills_query'] = m_cargo.query(
                    {'items', 'item_mods', 'mods', 'skill', 'items=items2'},
                    {
                        'items._pageName',
                        'items.name',
                        'item_mods.id',
                        'mods._pageName',
                        'mods.id',
                        'mods.granted_skill',
                        'mods.stat_text_raw',
                        'skill._pageName',
                        'skill.skill_id',
                        'skill.active_skill_name',
                        'skill.skill_icon',
                        'skill.stat_text',
                        'items2.class',
                        'items2.name',
                        'items2.inventory_icon',
                        'items2.size_x',
                        'items2.size_y',
                        'items2.html',
                    },
                    {
                        join='items._pageID=item_mods._pageID, item_mods.id=mods.id, mods.granted_skill=skill.skill_id, skill._pageID=items2._pageID',
                        where=string.format(
                            'items._pageID IN (%s) AND mods.granted_skill IS NOT NULL',
                            table.concat(results2.pageIDs, ', ')
                        ),
                    }
                )

                results2['granted_skills_query'] = m_cargo.map_results_to_id{
                    results=results2['granted_skills_query'],
                    field='items._pageName',
                }
            end
            local results = results2['granted_skills_query'][data['items._pageName']] or {}

            local tbl = {}
            for _, v in ipairs(results) do

                -- Check if a level for the skill is specified in the
                -- mod stat text.
                -- Stat ids have unreliable naming convention so using
                -- the mod stat text instead.
                local level = ''
                local stat_text = v['mods.stat_text_raw'] or ''
                local level_number = string.match(
                    stat_text:lower(),
                    h.string.format(
                        i18n.item_table.granted_skills_level_pattern,
                        {
                            granted_skills_level_label = i18n.item_table.granted_skills_level_label:lower()
                        }
                    )
                )

                -- If a level number was specified in the stat text
                -- then add it to the cell:
                if level_number then
                    level = h.string.format(
                        i18n.item_table.granted_skills_level_format,
                        {
                            granted_skills_level_label = i18n.item_table.granted_skills_level_label,
                            level_number = level_number,
                        }
                    )
                end

                -- Use different formats depending on if it's a gem or
                -- not:
                if v['items2.class'] == nil  then
                    tbl[#tbl+1] = h.string.format(
                        i18n.item_table.granted_skills_skill_output_format,
                        {
                            level = level,
                            sl = h.skill_link{
                                skip_query=true,
                                page = v['skill.active_skill_name']
                                    or v['skill._pageName']
                                    or v['mods._pageName']
                                    or '',
                                name = v['skill.active_skill_name']
                                    or v['skill.stat_text']
                                    or v['mods.granted_skill'],
                                icon = v['skill.skill_icon'],
                            },
                        }
                    )
                else
                    local il_args = {
                        skip_query=true,
                        page=v['items2._pageName'],
                        name=v['items2.name'],
                        inventory_icon=v['items2.inventory_icon'],
                        width=v['items2.size_x'],
                        height=v['items2.size_y'],
                    }

                    -- TODO: add in tpl_args.
                    if no_html == nil then
                        il_args.html = v['items2.html']
                    end
                    tbl[#tbl+1] = h.string.format(
                        i18n.item_table.granted_skills_gem_output_format,
                        {
                            level = level,
                            il = h.item_link(il_args),
                        }
                    )
                end
            end
            h.na_or_val(tr, table.concat(tbl, '<br>'))
        end,
        order = 22001,
        sort_type = 'text',
    },
    {
        arg = 'alternate_art',
        header = i18n.item_table.alternate_art,
        fields = {'items.alternate_art_inventory_icons'},
        display = function (tr, data)
            local alt_art = m_util.string.split(
                data['items.alternate_art_inventory_icons'],
                ','
            )

            -- TODO: Use il instead to handle size?
            -- local size = 39
            local out = {}
            for i,v in ipairs(alt_art) do
                out[#out+1] = string.format(
                    '[[%s|link=|%s]]',
                    v,
                    v
                )
            end

            tr
                :tag('td')
                    :wikitext(table.concat(out, ''))
        end,
        order = 23000,
        sort_type = 'text',
    },
}

data_map.skill_gem_new = {
    {
        arg = 'icon',
        header = i18n.item_table.support_gem_letter,
        fields = {'skill_gems.support_gem_letter_html'},
        display = h.tbl.display.factory.value{},
        order = 1000,
        sort_type = 'text',
    },
    {
        arg = 'skill_icon',
        header = i18n.item_table.skill_icon,
        fields = {'skill.skill_icon'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='[[%s]]',
            },
        }},
        order = 1001,
        sort_type = 'text',
    },
    {
        arg = {'stat', 'stat_text'},
        header = i18n.item_table.stats,
        fields = {'skill.stat_text'},
        display = h.tbl.display.factory.value{},
        order = 2000,
        sort_type = 'text',
    },
    {
        arg = {'quality', 'quality_stat_text'},
        header = i18n.item_table.quality_stats,
        fields = {'skill.quality_stat_text'},
        display = h.tbl.display.factory.value{},
        order = 2001,
        sort_type = 'text',
    },
    {
        arg = 'description',
        header = i18n.item_table.description,
        fields = {'skill.description'},
        display = h.tbl.display.factory.value{},
        order = 2100,
        sort_type = 'text',
    },
    {
        arg = 'level',
        header = m_game.level_requirement.icon,
        fields = h.tbl.range_fields{field='items.required_level'},
        display = h.tbl.display.factory.range{field='items.required_level'},
        order = 3004,
    },
    {
        arg = 'crit',
        header = i18n.item_table.skill_critical_strike_chance,
        fields = {'skill_levels.critical_strike_chance'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='%s%%',
                skill_levels = true,
            },
        }},
        order = 4000,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'cast_time',
        header = i18n.item_table.cast_time,
        fields = {'skill.cast_time'},
        display = h.tbl.display.factory.value{options = {
        }},
        order = 4001,
        options = {
        },
    },
    {
        arg = {'aspd', 'attack_speed', 'attack_speed_multiplier'},
        header = i18n.item_table.attack_speed_multiplier,
        fields = {'skill_levels.attack_speed_multiplier'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='%s%%',
                skill_levels = true,
            },
        }},
        order = 4002,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'dmgeff',
        header = i18n.item_table.damage_effectiveness,
        fields = {'skill_levels.damage_effectiveness'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='%s%%',
                skill_levels = true,
            },
        }},
        order = 4003,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'mcm',
        header = i18n.item_table.mana_cost_multiplier,
        fields = {'skill_levels.mana_multiplier'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                fmt='%s%%',
                skill_levels = true,
            },
        }},
        order = 5000,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'mana',
        header = i18n.item_table.mana_cost,
        fields = {'skill_levels.mana_cost', 'skill.has_percentage_mana_cost', 'skill.has_reservation_mana_cost'},
        display = function (tr, data, fields, data2)
            local appendix = ''
            if m_util.cast.boolean(data['skill.has_percentage_mana_cost']) then
                appendix = appendix .. '%%'
            end
            if m_util.cast.boolean(data['skill.has_reservation_mana_cost']) then
                appendix = appendix .. ' ' .. i18n.item_table.reserves_mana_suffix
            end

            h.tbl.display.factory.value{options = {
                [1] = {
                    fmt='%d' .. appendix,
                    skill_levels = true,
                },
            }}(tr, data, {'skill_levels.mana_cost'}, data2)
        end,
        order = 5001,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'vaal',
        header = i18n.item_table.vaal_souls_requirement,
        fields = {'skill_levels.vaal_souls_requirement'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                skill_levels = true,
            },
        }},
        order = 6000,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'vaal',
        header = i18n.item_table.stored_uses,
        fields = {'skill_levels.vaal_stored_uses'},
        display = h.tbl.display.factory.value{options = {
            [1] = {
                skill_levels = true,
            },
        }},
        order = 6001,
        options = {
            [1] = {
                skill_levels = true,
            },
        },
    },
    {
        arg = 'radius',
        header = i18n.item_table.primary_radius,
        fields = {'skill.radius', 'skill.radius_description'},
        options = {[2] = {optional = true}},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data['skill.radius'])
                    :wikitext(h.tbl.display.factory.descriptor_value{tbl=data, key='skill.radius_description'}(nil, nil, data['skill.radius']))
        end,
        order = 7000,
    },
    {
        arg = 'radius',
        header = i18n.item_table.secondary_radius,
        fields = {'skill.radius_secondary', 'skill.radius_secondary_description'},
        options = {[2] = {optional = true}},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data['skill.radius_secondary'])
                    :wikitext(h.tbl.display.factory.descriptor_value{tbl=data, key='skill.radius_secondary_description'}(nil, nil, data['skill.radius_secondary']))
        end,
        order = 7001,
    },
    {
        arg = 'radius',
        header = i18n.item_table.tertiary_radius,
        fields = {'skill.radius_tertiary', 'skill.radius_tertiary_description'},
        options = {[2] = {optional = true}},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data['skill.radius_tertiary'])
                   :wikitext(h.tbl.display.factory.descriptor_value{tbl=data, key='skill.radius_tertiary_description'}(nil, nil, data['skill.radius_tertiary']))
        end,
        order = 7002,
    },
}

for i, attr in ipairs(m_game.constants.attribute_order) do
    local attr_data = m_game.constants.attributes[attr]
    table.insert(data_map.generic_item, 7, {
        arg = attr_data.arg,
        header = attr_data.icon,
        fields = h.tbl.range_fields{field=string.format('items.required_%s', attr)},
        display = h.tbl.display.factory.range{field=string.format('items.required_%s', attr)},
        order = 5000+i,
    })
    table.insert(data_map.skill_gem_new, 1, {
        arg = attr_data.arg,
        header = attr_data.icon,
        fields = {string.format('skill_gems.%s_percent', attr)},
        display = function (tr, data)
            tr
                :tag('td')
                    :attr('data-sort-value', data[string.format('skill_gems.%s_percent', attr)])
                    :wikitext('[[File:Yes.png|да|link=]]')
        end,
        order = 3000+i,
    })
end


-- ----------------------------------------------------------------------------
-- Invoke callables
-- ----------------------------------------------------------------------------

local p = {}

--
-- Template:Item table
--

function p.item_table(frame)
    --[[
    Creates a generic table for items.

    Examples
    --------
    = p.item_table{
        q_tables='vendor_rewards, quest_rewards, skill_gems',
        q_join='items._pageID=vendor_rewards._pageID, items._pageID=quest_rewards._pageID, items._pageID=skill_gems._pageID',
        q_where='(items.class="Active Skill Gems" OR items.class = "Support Skill Gems") AND (vendor_rewards.quest_id IS NOT NULL OR quest_rewards.quest_id IS NOT NULL)',
        vendor=1,
    }
    ]]

    local t = os.clock()
    -- args
    local tpl_args = getArgs(frame, {
            parentFirst = true
        })
    frame = m_util.misc.get_frame(frame)

    tpl_args.q_where = m_cargo.replace_holds{string=tpl_args.q_where}

    local modes = {
        skill = {
            data = data_map.skill_gem_new,
            header = i18n.item_table.skill_gem,
        },
        item = {
            data = data_map.generic_item,
            header = i18n.item_table.item,
        },
    }

    if tpl_args.mode == nil then
        tpl_args.mode = 'item'
    end

    if modes[tpl_args.mode] == nil then
        error(i18n.errors.invalid_item_table_mode)
    end

    local results2 = {
        stats = {},
        skill_levels = {},
        pageIDs = {},
    }

    local row_infos = {}
    for _, row_info in ipairs(modes[tpl_args.mode].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

    -- Parse stat arguments
    local stat_columns = {}
    local query_stats = {}
    local i = 0
    repeat
        i = i + 1

        local prefix = string.format('stat_column%s_', i)
        local col_info = {
            header = tpl_args[prefix .. 'header'] or tostring(i),
            format = tpl_args[prefix .. 'format'],
            stat_format = tpl_args[prefix .. 'stat_format'] or 'separate',
            order = tonumber(tpl_args[prefix .. 'order']) or (10000000 + i),
            stats = {},
            options = {},
        }

        local j = 0
        repeat
            j = j +1

            local stat_info = {
                id = tpl_args[string.format('%sstat%s_id', prefix, j)],
            }

            if stat_info.id then
                col_info.stats[#col_info.stats+1] = stat_info
                query_stats[stat_info.id] = {}
            else
                -- Stop iteration entirely if this was the first index but no stat was supplied. We assume that we stop in this case.
                if j == 1 then
                    i = nil
                end
                -- stop iteration
                j = nil
            end
        until j == nil

        -- Don't add this column if no stats were provided.
        if #col_info.stats > 0 then
            stat_columns[#stat_columns+1] = col_info
        end
    until i == nil

    for _, col_info in ipairs(stat_columns) do
        local row_info = {
            --arg
            header = col_info.header,
            fields = {},
            display = function(tr, data, properties)
                if col_info.stat_format == 'separate' then
                    local stat_texts = {}
                    local num_stats = 0
                    local vmax = 0
                    for _, stat_info in ipairs(col_info.stats) do
                        num_stats = num_stats + 1
                        -- stat results from outside body
                        local stat = (results2.stats[data['items._pageName']] or {})[stat_info.id]
                        if stat ~= nil then
                            stat_texts[#stat_texts+1] = m_util.html.format_value(tpl_args, frame, stat, {no_color=true})
                            vmax = vmax + stat.max
                        end
                    end

                    if num_stats ~= #stat_texts then
                        tr:wikitext(m_util.html.td.na())
                    else
                        local text
                        if col_info.format then
                            text = string.format(col_info.format, unpack(stat_texts))
                        else
                            text = table.concat(stat_texts, ', ')
                        end

                        tr:tag('td')
                            :attr('data-sort-value', vmax)
                            :attr('class', 'tc -mod')
                            :wikitext(text)
                    end
                 elseif col_info.stat_format == 'add' then
                    local total_stat = {
                        min = 0,
                        max = 0,
                        avg = 0,
                    }
                    for _, stat_info in ipairs(col_info.stats) do
                        local stat = (results2.stats[data['items._pageName']] or {})[stat_info.id]
                        if stat ~= nil then
                            for k, v in pairs(total_stat) do
                                total_stat[k] = v + stat[k]
                            end
                        end
                    end

                    if col_info.format == nil then
                        col_info.format = '%s'
                    end

                    tr:tag('td')
                        :attr('data-sort-value', total_stat.max)
                        :attr('class', 'tc -mod')
                        :wikitext(string.format(col_info.format, m_util.html.format_value(tpl_args, frame, total_stat, {no_color=true})))
                 else
                    error(string.format(i18n.errors.generic_argument_parameter, 'stat_format', col_info.stat_format))
                 end
            end,
            order = col_info.order,
        }
        table.insert(row_infos, row_info)
    end

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

    -- Parse query arguments
    local tables_assoc = {items=true}
    local fields = {
        'items._pageID',
        'items._pageName',
        'items.name',
        'items.inventory_icon',
        'items.html',
        'items.size_x',
        'items.size_y',
    }

    --
    local prepend = {
        q_groupBy=true,
        q_tables=true,
    }

    local query = {}
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            if prepend[key] then
                value = ',' .. value
            end

            query[string.sub(key, 3)] = value
        end
    end

    -- Namespace condition
    -- This is mainly to prevent items from user pages or other testing pages 
    -- from being returned in the query results.
    if tpl_args.namespace ~= 'any' then
        local namespace = tonumber(tpl_args.namespace) or cfg.primary_namespace
        query.where = string.format('%s AND items._pageNamespace = %i', query.where, namespace)
    end

    local skill_levels = {}
    for _, rowinfo in ipairs(row_infos) do
        if type(rowinfo.fields) == 'function' then
            rowinfo.fields = rowinfo.fields()
        end
        for index, field in ipairs(rowinfo.fields) do
            rowinfo.options[index] = rowinfo.options[index] or {}
            if rowinfo.options[index].skill_levels then
                skill_levels[#skill_levels+1] = field
            else
                fields[#fields+1] = field
                tables_assoc[m_util.string.split(field, '%.')[1]] = true
            end
        end
    end

    if #skill_levels > 0 then
        fields[#fields+1] = 'skill.max_level'
        tables_assoc.skill = true

    end

    -- Reformat the tables and fields so they can be retrieved correctly:
    local tables = {}
    for table_name,_ in pairs(tables_assoc) do
        tables[#tables+1] = table_name
    end
    local tbls = table.concat(tables,',') .. (query.tables or '')
    query.tables = m_util.string.split(tbls, ',')

    for index, field in ipairs(fields) do
        fields[index] = string.format('%s=%s', field, field)
    end
    query.fields = fields

    -- Take care of the minimum required joins, joins from templates
    -- must still be userdefined:
    local joins = {}
    for index, table_name in ipairs(tables) do
        if table_name ~= 'items' then
            joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name)
        end
    end
    if #joins > 0 and query.join then
        query.join = table.concat(joins, ',') .. ',' .. query.join
    elseif #joins > 0 and not query.join then
        query.join = table.concat(joins, ',')
    elseif #joins == 0 and query.join then
        -- leave query.join as is
    end

    -- Needed to eliminate duplicates supplied via table joins:
    query.groupBy = 'items._pageID' .. (query.groupBy or '')

    -- Query results:
    local results = m_cargo.query(query.tables, query.fields, query)

    if #results == 0 and tpl_args.default ~= nil then
        return tpl_args.default
    end

    if #results > 0 then
        -- Create a list of found pageIDs for column specific queries:
        for _,v in ipairs(results) do
            results2.pageIDs[#results2.pageIDs+1] = v['items._pageID']
        end

        -- fetch skill level information
        if #skill_levels > 0 then
            skill_levels[#skill_levels+1] = 'skill_levels._pageName'
            skill_levels[#skill_levels+1] = 'skill_levels.level'
            local pages = {}
            for _, row in ipairs(results) do
                pages[#pages+1] = string.format('(skill_levels._pageID="%s" AND skill_levels.level IN (0, 1, %s))', row['items._pageID'], row['skill.max_level'])
            end
            local temp = m_cargo.query(
                {'skill_levels'},
                skill_levels,
                {
                    where=table.concat(pages, ' OR '),
                    groupBy='skill_levels._pageID, skill_levels.level',
                }
            )
            -- map to results
            for _, row in ipairs(temp) do
                if results2.skill_levels[row['skill_levels._pageName']] == nil then
                   results2.skill_levels[row['skill_levels._pageName']] = {}
                end
                -- TODO: convert to int?
                results2.skill_levels[row['skill_levels._pageName']][row['skill_levels.level']] = row
            end
        end

        if #stat_columns > 0 then
            local pages = {}
            for _, row in ipairs(results) do
                pages[#pages+1] = string.format('item_stats._pageID="%s"', row['items._pageID'])
            end

            local query_stat_ids = {}
            for stat_id, _ in pairs(query_stats) do
                query_stat_ids[#query_stat_ids+1] = string.format('item_stats.id="%s"', stat_id)
            end

            if tpl_args.q_where then
                tpl_args.q_where = string.format(' AND (%s)', tpl_args.q_where)
            else
                tpl_args.q_where = ''
            end

            local temp = m_cargo.query(
                {'items', 'item_stats'},
                {'item_stats._pageName', 'item_stats.id', 'item_stats.min', 'item_stats.max', 'item_stats.avg'},
                {
                    where=string.format('item_stats.is_implicit IS NULL AND (%s) AND (%s)', table.concat(query_stat_ids, ' OR '), table.concat(pages, ' OR ')),
                    join='items._pageID=item_stats._pageID',
                    -- Cargo workaround: avoid duplicates using groupBy
                    groupBy='items._pageID, item_stats.id',
                }
            )

            for _, row in ipairs(temp) do
                local stat = {
                    min = tonumber(row['item_stats.min']),
                    max = tonumber(row['item_stats.max']),
                    avg = tonumber(row['item_stats.avg']),
                }

                if results2.stats[row['item_stats._pageName']] == nil then
                    results2.stats[row['item_stats._pageName']] = {[row['item_stats.id']] = stat}
                else
                    results2.stats[row['item_stats._pageName']][row['item_stats.id']] = stat
                end
            end
        end
    end


    --
    -- Display the table
    --

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

    -- Headers:
    local tr = tbl:tag('tr')
    tr
        :tag('th')
            :wikitext(modes[tpl_args.mode].header)
            :done()
    for _, row_info in ipairs(row_infos) do
        local th = tr:tag('th')

        if row_info.colspan then
            th:attr('colspan', row_info.colspan)
        end

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

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

        local il_args = {
            skip_query=true,
            page=row['items._pageName'],
            name=row['items.name'],
            inventory_icon=row['items.inventory_icon'],
            width=row['items.size_x'],
            height=row['items.size_y'],
        }

        if tpl_args.no_html == nil then
            il_args.html = row['items.html']
        end

        if tpl_args.large then
            il_args.large = tpl_args.large
        end

        tr
            :tag('td')
                :wikitext(h.item_link(il_args))
                :done()

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

    local cats = {}
    if #results == query.limit then
        cats[#cats+1] = i18n.categories.query_limit
    end

    if #results == 0 then
        cats[#cats+1] = i18n.categories.no_results
    end

    mw.logObject({os.clock() - t, query})

    return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist=tpl_args.debug})
end


-------------------------------------------------------------------------------
-- Map item drops
-------------------------------------------------------------------------------

function p.map_item_drops(frame)
    --[[
    Gets the area id from the map item and activates
    Template:Area_item_drops.

    Examples:
    = p.map_item_drops{page='Underground River Map (War for the Atlas)'}
    ]]

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

    tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())

    local results = m_cargo.query(
        {'maps'},
        {'maps.area_id'},
        {
            where=string.format('maps._pageName="%s" AND maps.area_id IS NOT NULL', tpl_args.page),
            -- Only need each page name once
            groupBy='maps._pageName',
        }
    )
    local id = ''
    if #results > 0 then
        id = results[1]['maps.area_id']
    end
    return frame:expandTemplate{ title = 'Area item drops', args = {area_id=id} }
end

-------------------------------------------------------------------------------
-- Prophecy description
-------------------------------------------------------------------------------

function p.prophecy_description(frame)
    -- Get args
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)

    tpl_args.page = tpl_args.page or tostring(mw.title.getCurrentTitle())

    local results = m_cargo.query(
        {'prophecies'},
        {'prophecies.objective', 'prophecies.reward'},
        {
            where=string.format('prophecies._pageName="%s"', tpl_args.page),
            -- Only need each page name once
            groupBy='prophecies._pageName',
        }
    )

    results = results[1]

    local out = {}

    if results['prophecies.objective'] then
        out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.objective)
        out[#out+1] = results['prophecies.objective']
    end

    if results['prophecies.reward'] then
        out[#out+1] = string.format('<h2>%s</h2>', i18n.prophecy_description.reward)
        out[#out+1] = results['prophecies.reward']
    end

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

-- ----------------------------------------------------------------------------
-- Item disambiguation
-- ----------------------------------------------------------------------------
function h.find_aliases(tpl_args)
   --[[
   This function queries items for an item name, then checks if it has
   had any name changes then queries for that name as well.
   ]]

    -- Get initial name:
    tpl_args.name_list = {
        tpl_args.name or m_util.string.split(
            tostring(mw.title.getCurrentTitle()),
            ' %('
        )
    }

    -- Query for items with similar name, repeat until no new names are
    -- found.
    local n
    local results = {}
    local hash = {}
    repeat
        local n_old = #tpl_args.name_list

        local where_tbl = {}
        for _, item_name in ipairs(tpl_args.name_list) do
            for _, prefix in ipairs({'', 'Изменённая '}) do
                where_tbl[#where_tbl+1] = string.format(
                    'items__name_list._value = "%s%s"',
                    prefix,
                    item_name
                )
            end
        end
        local where_str = table.concat(where_tbl, ' OR ')

        results = m_cargo.query(
            
            -- explicitly join to the child list table so that we don't need a REGEXP
            { 'items__name_list', 'items', 'maps'},
            {
                'items._pageName',
                'items.name',
                
                -- we need to give this field a different name from `name_list` so that it doesn't conflict
                'items.name_list__full=name_list2',
                'items.release_version',
                'items.removal_version',
                'items.drop_enabled',
            },
            {
                join='items__name_list._rowID=items._ID, items._pageName=maps._pageName',
                where=where_str,
                groupBy='items._pageName',
                orderBy='items.release_version DESC, items.removal_version DESC, items.name ASC, maps.area_id ASC',
            }
        )

        -- Filter duplicates:
        for i,v in ipairs(results) do
            local r = m_util.string.split(v['name_list2'], '�')
            if type(r) == string then
                r = {r}
            end

            for j,m in ipairs(r) do
                if hash[m] == nil then
                    hash[m] = m
                    tpl_args.name_list[#tpl_args.name_list+1] = m
                end
            end
        end
    until #tpl_args.name_list == n_old

    return results
end

function p.item_disambiguation(frame)
    --[[
    This function finds that items with a name or has had that name.

    To do
    -----
    Should text imply which is the original map, even if it isn't (Original)?
    How to properly sort drop disabled items, with removal version?
    How to deal with names that have been used multiple times? Terrace Map

    Examples
    --------
    = p.item_disambiguation{name='Abyss Map'}
    = p.item_disambiguation{name='Caldera Map'}
    = p.item_disambiguation{name='Crypt Map'}
    = p.item_disambiguation{name='Catacombs Map'}
    = p.item_disambiguation{name='Harbinger Map (High Tier)'}
    ]]

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

    local current_title = tostring(mw.title.getCurrentTitle())
    -- Get the page name.
    tpl_args.name = tpl_args.name or m_util.string.split(
        current_title,
        ' %('
    )[1]

    -- Query for items with similar name.
    local results = h.find_aliases(tpl_args)

    -- Format the results:
    local out = {}
    local container = mw.html.create('div')
    local tbl = container:tag('ul')
    for i,v in ipairs(results) do
        if v['items._pageName'] ~= current_title then
            -- Get the content inside the last parentheses:
            local known_release = string.reverse(
                string.match(
                    string.reverse(v['items._pageName']),
                    '%)(.-)%('
                ) or ''
            )

            local drop_enabled
            if known_release == 'Первое поколение' then
                drop_enabled = i18n.item_disambiguation.original
            elseif m_util.cast.boolean(v['items.drop_enabled']) then
                drop_enabled = i18n.item_disambiguation.drop_enabled
            else
                drop_enabled = i18n.item_disambiguation.drop_disabled
            end


            if known_release ~= 'Первое поколение' and known_release ~= '' then
                known_release = string.format(
                    i18n.item_disambiguation.known_release,
                    known_release,
                    known_release
                )
            else
                known_release = ''
            end

            tbl
                :tag('li')
                    :wikitext(
                        string.format(
                            i18n.item_disambiguation.list_pattern,
                            h.item_link{page=v['items._pageName']},
                            drop_enabled,
                            known_release
                        )
                    )
        end
    end
    out[#out+1] = tostring(container)

    -- Add a category when the template uses old template inputs:
    local old_args = {
        'war',
        'atlas',
        'awakening',
        'original',
        'heading',
        'hide_heading',
        'show_current',
    }
    for _,v in ipairs(old_args) do
        if tpl_args[v] ~= nil then
            return table.concat(out, '') .. m_util.misc.add_category(
                {'Pages with old template arguments'}
            )
        end
    end

    return table.concat(out, '')
end


function p.simple_item_list(frame)
    --[[
    Creates a simple list of items.

    Examples
    --------
    = p.simple_item_list{
        q_tables='maps',
        q_join='items._pageID=maps._pageID',
        q_where='maps.tier=1 AND items.drop_enabled=1 AND items.rarity_id="normal"',
        no_html=1,
        link_from_name=1,
    }
    ]]

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

    local query = {}
    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 fields = {
        'items._pageName',
        'items.name',
        'items.class',
    }

    if tpl_args.no_icon == nil then
        fields[#fields+1] = 'items.inventory_icon'
    end

    if tpl_args.no_html == nil then
        fields[#fields+1] = 'items.html'
    end

    local tables = m_util.string.split(tpl_args.q_tables or '', ',%s*')
    table.insert(tables, 'items')

    query.groupBy = query.groupBy or 'items._pageID'

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

    local out = {}
    for _, row in ipairs(results) do
        local page
        if tpl_args.use_name_as_link ~= nil then
            page = row['items.name']
        else
            page = row['items._pageName']
        end

        local link = h.item_link{
            page=page,
            name=row['items.name'],
            inventory_icon=row['items.inventory_icon'] or '',
            html=row['items.html'] or '',
            skip_query=true
        }

        if tpl_args.format == nil then
            out[#out+1] = string.format('* %s', link)
        elseif tpl_args.format == 'none' then
            out[#out+1] = link
        elseif tpl_args.format == 'li' then
            out[#out+1] = string.format('<li>%s</li>', link)
        else
            error(string.format(i18n.errors.generic_argument_parameter, 'format', tpl_args.format))
        end
    end

    if tpl_args.format == nil then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'none' then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'li' then
        return table.concat(out)
    end
end

-- ----------------------------------------------------------------------------
-- Debug stuff
-- ----------------------------------------------------------------------------
p.debug = {}

function p.debug._tbl_data(tbl)
    keys = {}
    for _, data in ipairs(tbl) 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

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

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

function p.debug.generic_item_all()
    return p.debug._tbl_data(data_map.generic_item)
end

function p.debug.skill_gem_all()
    return p.debug._tbl_data(data_map.skill_gem_new)
end

-- ----------------------------------------------------------------------------
-- Return
-- ----------------------------------------------------------------------------

return p