Module:Item table: Difference between revisions
Jump to navigation
Jump to search
(Added parameter to make table responsive.) |
(Minor refactoring in preparation to drop the "q_" from the query arguments. The error message when `where` param is missing is now more informative.) |
||
(25 intermediate revisions by 3 users not shown) | |||
Line 4: | Line 4: | ||
-- | -- | ||
-- This module implements Template:Item table and other templates that query | -- This module implements Template:Item table and other templates that query | ||
-- and display lists of items. | -- and display tables or lists of items. | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
require('Module:No globals') | require('Module:No globals') | ||
local m_util = require('Module:Util') | local m_util = require('Module:Util') | ||
local m_cargo = require('Module:Cargo') | local m_cargo = require('Module:Cargo') | ||
-- Should we use the sandbox version of our submodules? | -- Should we use the sandbox version of our submodules? | ||
local use_sandbox = m_util.misc.maybe_sandbox('Item table') | local use_sandbox = m_util.misc.maybe_sandbox('Item table') | ||
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game') | |||
-- Lazy loading | -- Lazy loading | ||
Line 93: | Line 83: | ||
end | end | ||
function h. | function h.range_fields_factory(args) | ||
-- Returns a function that gets the range fields for the given keys | |||
function | |||
local suffixes = {'maximum', 'text', 'colour'} | local suffixes = {'maximum', 'text', 'colour'} | ||
if args.full then | if args.full then | ||
suffixes[#suffixes+1] = 'minimum' | suffixes[#suffixes+1] = 'minimum' | ||
end | end | ||
return function () | |||
return function() | |||
local fields = {} | local fields = {} | ||
for _, field in ipairs(args.fields) do | |||
for _, | for _, suffix in ipairs(suffixes) do | ||
fields[#fields+1] = string.format('%s_range_%s', field, | fields[#fields+1] = string.format('%s_range_%s', field, suffix) | ||
end | end | ||
end | end | ||
return fields | return fields | ||
Line 136: | Line 100: | ||
end | end | ||
function h.display_value_factory(args) | |||
args.fmt_options = args.fmt_options or {} | |||
return function(tr, data, fields, data2) | |||
local values = {} | |||
local fmt_values = {} | |||
for index, field in ipairs(fields) do | |||
local value = { | |||
function h. | |||
args. | |||
return function(tr, data, fields, data2) | |||
local values = {} | |||
local fmt_values = {} | |||
for index, field in ipairs(fields) do | |||
local value = { | |||
min=data[field], | min=data[field], | ||
max=data[field], | max=data[field], | ||
base=data[field], | base=data[field], | ||
} | } | ||
local sdata = data2 and data2.skill_levels[data['items._pageName']] | |||
if sdata then --For skill data | if sdata then --For skill data | ||
-- Use the fixed value, or the first-level value. | -- Use the fixed value, or the first-level value. | ||
Line 182: | Line 120: | ||
if value.min then | if value.min then | ||
values[#values+1] = value.max | values[#values+1] = value.max | ||
local | local options = args.fmt_options[index] or {} | ||
-- global | -- global color is set, no overrides | ||
if args. | if args.color ~= nil then | ||
options.color = false | |||
end | end | ||
fmt_values[#fmt_values+1] = m_util.html.format_value( | fmt_values[#fmt_values+1] = m_util.html.format_value(nil, value, options) | ||
end | end | ||
end | end | ||
if #values == 0 then | if #values == 0 then | ||
tr: | tr:node(m_util.html.table_cell('na')) | ||
else | else | ||
local td = tr:tag('td') | local td = tr:tag('td') | ||
td:attr('data-sort-value', table.concat(values, ', ')) | td | ||
:attr('data-sort-value', table.concat(values, ', ')) | |||
if args. | :wikitext(table.concat(fmt_values, ', ')) | ||
td:attr('class', 'tc -' .. args. | if args.color then | ||
td:attr('class', 'tc -' .. args.color) | |||
end | end | ||
end | end | ||
Line 204: | Line 142: | ||
end | end | ||
function h. | function h.display_range_factory(args) | ||
-- args: table | -- args: table | ||
-- property | -- property | ||
Line 217: | Line 155: | ||
end | end | ||
function h. | function h.display_range_composite_factory(args) | ||
-- division by default | -- division by default | ||
if args.func == nil then | if args.func == nil then | ||
Line 268: | Line 206: | ||
end | end | ||
function h. | function h.display_descriptor_value_factory(args) | ||
-- Arguments: | -- Arguments: | ||
-- key | -- key | ||
-- tbl | -- tbl | ||
args = args or {} | args = args or {} | ||
return function ( | return function (value) | ||
if args.tbl[args.key] then | if args.tbl[args.key] then | ||
value = m_util.html.abbr(value, args.tbl[args.key]) | value = m_util.html.abbr(value, args.tbl[args.key]) | ||
Line 282: | Line 219: | ||
end | end | ||
function h. | function h.display_atlas_tier_factory(args) | ||
args = args or {} | args = args or {} | ||
return function (tr, data) | return function (tr, data) | ||
Line 308: | Line 245: | ||
end | end | ||
-- ---- | function h.display_yesno_factory(args) | ||
-- args: | |||
-- field | |||
-- condition | |||
args = args or {} | |||
args.condition = args.condition or function (value) | |||
return m_util.cast.boolean(value, {cast_nil=false}) | |||
end | |||
return function (tr, data) | |||
local type = args.condition(data[args.field]) and 'yes' or 'no' | |||
tr:node(m_util.html.table_cell(type)) | |||
end | |||
end | |||
function h.value_greater_than_zero(value) | |||
value = m_util.cast.number(value, {default=0}) | |||
if value > 0 then | |||
return true | |||
end | |||
return false | |||
end | |||
-- for sort type see: | function h.na_or_val(tr, value) | ||
-- https://meta.wikimedia.org/wiki/Help:Sorting | if value == nil or value == '' then | ||
tr:node(m_util.html.table_cell('na')) | |||
else | |||
tr | |||
:tag('td') | |||
:attr('data-sort-value', value) | |||
:wikitext(value) | |||
end | |||
end | |||
-- ---------------------------------------------------------------------------- | |||
-- Data mappings | |||
-- ---------------------------------------------------------------------------- | |||
local data_map = {} | |||
-- for sort type see: | |||
-- https://meta.wikimedia.org/wiki/Help:Sorting | |||
data_map.generic_item = { | data_map.generic_item = { | ||
{ | { | ||
order = 1000, | |||
args = {'base_item'}, | |||
header = i18n.item_table.base_item, | header = i18n.item_table.base_item, | ||
fields = {'items.base_item', 'items.base_item_page'}, | fields = {'items.base_item', 'items.base_item_page'}, | ||
Line 327: | Line 298: | ||
:wikitext(string.format('[[%s|%s]]', data['items.base_item_page'], data['items.base_item'])) | :wikitext(string.format('[[%s|%s]]', data['items.base_item_page'], data['items.base_item'])) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 1001, | |||
args = {'class'}, | |||
header = i18n.item_table.item_class, | header = i18n.item_table.item_class, | ||
fields = {'items.class'}, | fields = {'items.class'}, | ||
display = h. | display = h.display_value_factory{ | ||
fmt_options = { | |||
[1] = { | |||
fmt = '[[%s]]', | |||
}, | |||
}, | }, | ||
} | }, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 1002, | |||
args = {'rarity'}, | |||
header = i18n.item_table.rarity, | header = i18n.item_table.rarity, | ||
fields = {'items.rarity'}, | fields = {'items.rarity'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 1003, | |||
args = {'rarity_id'}, | |||
header = i18n.item_table.rarity_id, | header = i18n.item_table.rarity_id, | ||
fields = {'items.rarity_id'}, | fields = {'items.rarity_id'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 1004, | |||
args = {'metadata_id'}, | |||
header = i18n.item_table.metadata_id, | header = i18n.item_table.metadata_id, | ||
fields = {'items.metadata_id'}, | fields = {'items.metadata_id'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 1005, | |||
args = {'size'}, | |||
header = i18n.item_table.inventory_size, | |||
fields = {'items.size_x', 'items.size_y'}, | |||
display = function (tr, data) | |||
tr | |||
:tag('td') | |||
:attr('data-sort-value', data['items.size_x'] * data['items.size_y']) | |||
:wikitext(string.format('%s×%s', data['items.size_x'], data['items.size_y'])) | |||
end, | |||
}, | |||
{ | |||
order = 1100, | |||
args = {'essence'}, | |||
header = i18n.item_table.essence_level, | header = i18n.item_table.essence_level, | ||
fields = {'essences.level'}, | fields = {'essences.level'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 1300, | |||
args = {'stack_size'}, | |||
header = i18n.item_table.stack_size, | header = i18n.item_table.stack_size, | ||
fields = {'stackables.stack_size'}, | fields = {'stackables.stack_size'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 1301, | |||
args = {'stack_size_currency_tab'}, | |||
header = i18n.item_table.stack_size_currency_tab, | header = i18n.item_table.stack_size_currency_tab, | ||
fields = {'stackables.stack_size_currency_tab'}, | fields = {'stackables.stack_size_currency_tab'}, | ||
display = h. | display = h.display_value_factory{}, | ||
order = | }, | ||
-- Requirements | |||
{ | |||
order = 1400, | |||
args = {'level'}, | |||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.level_icon, | |||
i18n.item_table.required_level | |||
), | |||
i18n.item_table.required_level | |||
), | |||
fields = h.range_fields_factory{fields={'items.required_level'}}, | |||
display = h.display_range_factory{field='items.required_level'}, | |||
}, | |||
{ | |||
order = 1401, | |||
args = {'str'}, | |||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.str_icon, | |||
i18n.item_table.required_str | |||
), | |||
i18n.item_table.required_str | |||
), | |||
fields = h.range_fields_factory{fields={'items.required_strength'}}, | |||
display = h.display_range_factory{field='items.required_strength'}, | |||
}, | |||
{ | |||
order = 1402, | |||
args = {'dex'}, | |||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.dex_icon, | |||
i18n.item_table.required_dex | |||
), | |||
i18n.item_table.required_dex | |||
), | |||
fields = h.range_fields_factory{fields={'items.required_dexterity'}}, | |||
display = h.display_range_factory{field='items.required_dexterity'}, | |||
}, | }, | ||
{ | { | ||
order = 1403, | |||
header = | args = {'int'}, | ||
fields = h. | header = m_util.html.abbr( | ||
display = h. | string.format( | ||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.int_icon, | |||
i18n.item_table.required_int | |||
), | |||
i18n.item_table.required_int | |||
), | |||
fields = h.range_fields_factory{fields={'items.required_intelligence'}}, | |||
display = h.display_range_factory{field='items.required_intelligence'}, | |||
}, | }, | ||
-- Armour 15xx | |||
{ | { | ||
order = 1500, | |||
args = {'ar'}, | |||
header = i18n.item_table.armour, | header = i18n.item_table.armour, | ||
fields = h. | fields = h.range_fields_factory{fields={'armours.armour'}}, | ||
display = h. | display = h.display_range_factory{field='armours.armour'}, | ||
}, | }, | ||
{ | { | ||
order = 1501, | |||
args = {'ev'}, | |||
header =i18n.item_table.evasion, | header =i18n.item_table.evasion, | ||
fields = h. | fields = h.range_fields_factory{fields={'armours.evasion'}}, | ||
display = h. | display = h.display_range_factory{field='armours.evasion'}, | ||
}, | }, | ||
{ | { | ||
order = 1502, | |||
args = {'es'}, | |||
header = i18n.item_table.energy_shield, | header = i18n.item_table.energy_shield, | ||
fields = h. | fields = h.range_fields_factory{fields={'armours.energy_shield'}}, | ||
display = h. | display = h.display_range_factory{field='armours.energy_shield'}, | ||
}, | }, | ||
{ | { | ||
order = 1503, | |||
args = {'wd'}, | |||
header = i18n.item_table.ward, | header = i18n.item_table.ward, | ||
fields = h. | fields = h.range_fields_factory{fields={'armours.ward'}}, | ||
display = h. | display = h.display_range_factory{field='armours.ward'}, | ||
}, | }, | ||
{ | { | ||
order = 1504, | |||
args = {'block'}, | |||
header = i18n.item_table.block, | header = i18n.item_table.block, | ||
fields = h. | fields = h.range_fields_factory{fields={'shields.block'}}, | ||
display = h. | display = h.display_range_factory{field='shields.block'}, | ||
}, | }, | ||
-- Weapons 16xx | |||
{ | { | ||
order = 1600, | |||
args = {'weapon', 'damage'}, | |||
order = | |||
header = i18n.item_table.damage, | header = i18n.item_table.damage, | ||
fields = {'weapons.damage_html', 'weapons.damage_avg'}, | fields = {'weapons.damage_html', 'weapons.damage_avg'}, | ||
Line 458: | Line 473: | ||
:wikitext(data['weapons.damage_html']) | :wikitext(data['weapons.damage_html']) | ||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 1601, | |||
args = {'weapon', 'aps'}, | |||
header = i18n.item_table.attacks_per_second, | header = i18n.item_table.attacks_per_second, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.attack_speed'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.attack_speed'}, | ||
}, | }, | ||
{ | { | ||
order = 1602, | |||
args = {'weapon', 'crit'}, | |||
header = i18n.item_table.local_critical_strike_chance, | header = i18n.item_table.local_critical_strike_chance, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.critical_strike_chance'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.critical_strike_chance'}, | ||
}, | }, | ||
{ | { | ||
order = 1603, | |||
args = {'physical_dps'}, | |||
header = i18n.item_table.physical_dps, | header = i18n.item_table.physical_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.physical_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.physical_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1604, | |||
args = {'lightning_dps'}, | |||
header = i18n.item_table.lightning_dps, | header = i18n.item_table.lightning_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.lightning_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.lightning_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1605, | |||
args = {'cold_dps'}, | |||
header = i18n.item_table.cold_dps, | header = i18n.item_table.cold_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.cold_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.cold_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1606, | |||
args = {'fire_dps'}, | |||
header = i18n.item_table.fire_dps, | header = i18n.item_table.fire_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.fire_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.fire_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1607, | |||
args = {'chaos_dps'}, | |||
header = i18n.item_table.chaos_dps, | header = i18n.item_table.chaos_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.chaos_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.chaos_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1608, | |||
args = {'elemental_dps'}, | |||
header = i18n.item_table.elemental_dps, | header = i18n.item_table.elemental_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.elemental_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.elemental_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1609, | |||
args = {'poison_dps'}, | |||
header = i18n.item_table.poison_dps, | header = i18n.item_table.poison_dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.poison_dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.poison_dps'}, | ||
}, | }, | ||
{ | { | ||
order = 1610, | |||
args = {'dps'}, | |||
header = i18n.item_table.dps, | header = i18n.item_table.dps, | ||
fields = h. | fields = h.range_fields_factory{fields={'weapons.dps'}}, | ||
display = h. | display = h.display_range_factory{field='weapons.dps'}, | ||
}, | }, | ||
-- Flasks 17xx | |||
{ | { | ||
order = 1700, | |||
args = {'flask_life'}, | |||
header = i18n.item_table.flask_life, | header = i18n.item_table.flask_life, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.life'}}, | ||
display = h. | display = h.display_range_factory{field='flasks.life'}, | ||
}, | }, | ||
{ | { | ||
order = 1701, | |||
args = {'flask_life_per_second'}, | |||
header = i18n.item_table.flask_life_per_second, | header = i18n.item_table.flask_life_per_second, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.life', 'flasks.duration'}, full=true}, | ||
display = h. | display = h.display_range_composite_factory{field1='flasks.life', field2='flasks.duration'}, | ||
}, | }, | ||
{ | { | ||
order = 1702, | |||
args = {'flask_life_per_charge'}, | |||
header = i18n.item_table.flask_life_per_charge, | header = i18n.item_table.flask_life_per_charge, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.life', 'flasks.charges_per_use'}, full=true}, | ||
display = h. | display = h.display_range_composite_factory{field1='flasks.life', field2='flasks.charges_per_use'}, | ||
}, | }, | ||
{ | { | ||
order = 1703, | |||
args = {'flask_mana'}, | |||
header = i18n.item_table.flask_mana, | header = i18n.item_table.flask_mana, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.mana'}}, | ||
display = h. | display = h.display_range_factory{field='flasks.mana'}, | ||
}, | }, | ||
{ | { | ||
order = 1704, | |||
args = {'flask_mana_per_second'}, | |||
header = i18n.item_table.flask_mana_per_second, | header = i18n.item_table.flask_mana_per_second, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.mana', 'flasks.duration'}, full=true}, | ||
display = h. | display = h.display_range_composite_factory{field1='flasks.mana', field2='flasks.duration'}, | ||
}, | }, | ||
{ | { | ||
order = 1705, | |||
args = {'flask_mana_per_charge'}, | |||
header = i18n.item_table.flask_mana_per_charge, | header = i18n.item_table.flask_mana_per_charge, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.mana', 'flasks.charges_per_use'}, full=true}, | ||
display = h. | display = h.display_range_composite_factory{field1='flasks.mana', field2='flasks.charges_per_use'}, | ||
}, | }, | ||
{ | { | ||
order = 1706, | |||
args = {'flask'}, | |||
header = i18n.item_table.flask_duration, | header = i18n.item_table.flask_duration, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.duration'}}, | ||
display = h. | display = h.display_range_factory{field='flasks.duration'}, | ||
}, | }, | ||
{ | { | ||
order = 1707, | |||
args = {'flask'}, | |||
header = i18n.item_table.flask_charges_per_use, | header = i18n.item_table.flask_charges_per_use, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.charges_per_use'}}, | ||
display = h. | display = h.display_range_factory{field='flasks.charges_per_use'}, | ||
}, | }, | ||
{ | { | ||
order = 1708, | |||
args = {'flask'}, | |||
header = i18n.item_table.flask_maximum_charges, | header = i18n.item_table.flask_maximum_charges, | ||
fields = h. | fields = h.range_fields_factory{fields={'flasks.charges_max'}}, | ||
display = h. | display = h.display_range_factory{field='flasks.charges_max'}, | ||
}, | }, | ||
-- | -- Jewels 18xx | ||
{ | { | ||
order = 1800, | |||
header = i18n.item_table. | args = {'jewel_limit'}, | ||
fields = {' | header = i18n.item_table.jewel_limit, | ||
fields = {'jewels.jewel_limit'}, | |||
display = h.display_value_factory{}, | |||
}, | |||
{ | |||
order = 1801, | |||
args = {'jewel_radius'}, | |||
header = i18n.item_table.jewel_radius, | |||
fields = {'jewels.radius_html'}, | |||
display = function (tr, data) | display = function (tr, data) | ||
tr | tr | ||
:tag('td') | :tag('td') | ||
:wikitext(data['jewels.radius_html']) | |||
:wikitext(data[' | |||
end, | end, | ||
}, | }, | ||
-- Maps 19xx | |||
{ | { | ||
order = 1900, | |||
header = i18n.item_table. | args = {'map_tier'}, | ||
fields = {' | header = i18n.item_table.map_tier, | ||
display = h. | fields = {'maps.tier'}, | ||
display = h.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 1901, | |||
header = i18n.item_table. | args = {'map_level'}, | ||
fields = {' | header = i18n.item_table.map_level, | ||
display = h. | fields = {'maps.area_level'}, | ||
display = h.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 1902, | |||
header = i18n.item_table. | args = {'map_guild_character'}, | ||
fields = {' | header = i18n.item_table.map_guild_character, | ||
display = h. | fields = {'maps.guild_character'}, | ||
display = h.display_value_factory{}, | |||
sort_type = 'text', | |||
}, | }, | ||
{ | { | ||
order = 1903, | |||
header = i18n.item_table. | args = {'map_series'}, | ||
fields = {' | header = i18n.item_table.map_series, | ||
display = h. | fields = {'maps.series'}, | ||
display = h.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 1904, | |||
header = i18n.item_table. | args = {'atlas_tier'}, | ||
fields = {' | header = i18n.item_table.atlas_tier, | ||
display = h. | fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'}, | ||
display = h.display_atlas_tier_factory{is_level=false}, | |||
colspan = 5, | |||
}, | }, | ||
{ | { | ||
order = 1905, | |||
header = i18n.item_table. | args = {'atlas_level'}, | ||
fields = {' | header = i18n.item_table.atlas_level, | ||
display = h. | fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'}, | ||
display = h.display_atlas_tier_factory{is_level=true}, | |||
colspan = 5, | |||
}, | }, | ||
-- Map fragments 20xx | |||
{ | { | ||
order = 2000, | |||
header = i18n.item_table. | args = {'map_fragment', 'map_fragment_limit'}, | ||
fields = {' | header = i18n.item_table.map_fragment_limit, | ||
display = h. | fields = {'map_fragments.map_fragment_limit'}, | ||
display = h.display_value_factory{}, | |||
}, | }, | ||
-- | -- Hideout decorations 21xx | ||
{ | { | ||
order = 2100, | |||
header = i18n.item_table. | args = {'doodad', 'variation_count'}, | ||
fields = {' | header = i18n.item_table.variation_count, | ||
display = h. | fields = {'hideout_doodads.variation_count'}, | ||
display = h.display_value_factory{ | |||
color = 'mod', | |||
}, | |||
}, | }, | ||
-- Corpse items 22xx | |||
{ | { | ||
order = 2200, | |||
header = i18n.item_table. | args = {'corpse', 'monster_category'}, | ||
fields = {' | header = i18n.item_table.monster_category, | ||
fields = {'corpse_items.monster_category', 'corpse_items.monster_category_html'}, | |||
display = function (tr, data) | display = function (tr, data) | ||
tr | tr | ||
:tag('td') | :tag('td') | ||
:wikitext(data[' | :attr('data-sort-value', m_game.constants.monster.categories[data['corpse_items.monster_category']].id) | ||
:wikitext(data['corpse_items.monster_category_html']) | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 2201, | |||
header = i18n.item_table. | args = {'corpse', 'monster_abilities'}, | ||
fields = {' | header = i18n.item_table.monster_abilities, | ||
display = h. | fields = {'corpse_items.monster_abilities'}, | ||
display = h.display_value_factory{ | |||
color = 'mod', | |||
}, | |||
sort_type = 'text', | |||
}, | }, | ||
-- Tinctures 23xx | |||
{ | { | ||
order = 2300, | |||
header = i18n.item_table. | args = {'tincture'}, | ||
fields = {' | header = i18n.item_table.tincture_debuff_interval, | ||
display = h. | fields = h.range_fields_factory{fields={'tinctures.debuff_interval'}}, | ||
display = h.display_range_factory{field='tinctures.debuff_interval'}, | |||
}, | }, | ||
{ | { | ||
order = 2301, | |||
header = i18n.item_table. | args = {'tincture'}, | ||
fields = {' | header = i18n.item_table.tincture_cooldown, | ||
display = h. | fields = h.range_fields_factory{fields={'tinctures.cooldown'}}, | ||
display = h.display_range_factory{field='tinctures.cooldown'}, | |||
}, | }, | ||
-- Seed data 91xx, | |||
{ | { | ||
order = 9100, | |||
header = i18n.item_table. | args = {'seed', 'seed_type'}, | ||
header = i18n.item_table.seed_type, | |||
fields = {' | 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 = 9101, | |||
header = i18n.item_table. | args = {'seed', 'seed_tier'}, | ||
header = i18n.item_table.seed_tier, | |||
fields = {' | fields = {'harvest_seeds.tier'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 9102, | |||
header = i18n.item_table. | args = {'seed', 'seed_growth_cycles'}, | ||
fields = {' | header = i18n.item_table.seed_growth_cycles, | ||
display = h. | fields = {'harvest_seeds.growth_cycles'}, | ||
display = h.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 9110, | |||
header = i18n.item_table. | args = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_primal_lifeforce_percentage'}, | ||
fields = {' | header = i18n.item_table.seed_consumed_primal_lifeforce_percentage, | ||
display = h. | fields = {'harvest_seeds.consumed_primal_lifeforce_percentage'}, | ||
display = h.display_value_factory{ | |||
color = 'primal', | |||
}, | |||
}, | }, | ||
{ | { | ||
order = 9110, | |||
header = i18n.item_table. | args = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_vivid_lifeforce_percentage'}, | ||
fields = {' | header = i18n.item_table.seed_consumed_vivid_lifeforce_percentage, | ||
display = h. | fields = {'harvest_seeds.consumed_vivid_lifeforce_percentage'}, | ||
display = h.display_value_factory{ | |||
color = 'vivid', | |||
}, | |||
}, | }, | ||
{ | { | ||
order = 9110, | |||
header = i18n.item_table. | args = {'seed', 'seed_cosumed_lifeforce', 'seed_consumed_lifeforce_percentage', 'seed_consumed_wild_lifeforce_percentage'}, | ||
fields = {' | header = i18n.item_table.seed_consumed_wild_lifeforce_percentage, | ||
display = h. | fields = {'harvest_seeds.consumed_wild_lifeforce_percentage'}, | ||
display = h.display_value_factory{ | |||
color = 'wild', | |||
}, | |||
}, | |||
{ | |||
order = 9113, | |||
args = {'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.display_value_factory{}, | |||
}, | |||
{ | |||
order = 9114, | |||
args = {'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.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 12000, | |||
args = {'buff'}, | |||
header = i18n.item_table.buff_effects, | header = i18n.item_table.buff_effects, | ||
fields = {'item_buffs.stat_text'}, | fields = {'item_buffs.stat_text'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'mod', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12001, | |||
args = {'stat'}, | |||
header = i18n.item_table.stats, | header = i18n.item_table.stats, | ||
fields = {'items.stat_text'}, | fields = {'items.stat_text'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'mod', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12002, | order = 12002, | ||
args = {'description'}, | |||
header = i18n.item_table.description, | |||
fields = {'items.description'}, | |||
display = h.display_value_factory{ | |||
color = 'mod', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12003, | |||
header = i18n.item_table. | args = {'seed', 'seed_effect'}, | ||
header = i18n.item_table.seed_effects, | |||
fields = {'harvest_seeds.effect'}, | fields = {'harvest_seeds.effect'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'crafted', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12100, | |||
args = {'flavour_text'}, | |||
order = | |||
header = i18n.item_table.flavour_text, | header = i18n.item_table.flavour_text, | ||
fields = {'items.flavour_text'}, | fields = {'items.flavour_text'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'flavour', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12200, | |||
args = {'help_text'}, | |||
header = i18n.item_table.help_text, | header = i18n.item_table.help_text, | ||
fields = {'items.help_text'}, | fields = {'items.help_text'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'help', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 12300, | |||
args = {'buff_icon'}, | |||
header = i18n.item_table.buff_icon, | |||
fields = {'item_buffs.icon'}, | |||
display = h.display_value_factory{ | |||
fmt_options = { | |||
[1] = { | |||
fmt = '[[%s]]', | |||
}, | |||
}, | |||
}, | |||
sort_type = 'text', | |||
}, | |||
{ | |||
order = 13000, | |||
args = {'prophecy', 'objective'}, | |||
header = i18n.item_table.objective, | header = i18n.item_table.objective, | ||
fields = {'prophecies.objective'}, | fields = {'prophecies.objective'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | |||
order = 13001, | |||
args = {'prophecy', 'reward'}, | |||
header = i18n.item_table.reward, | header = i18n.item_table.reward, | ||
fields = {'prophecies.reward'}, | fields = {'prophecies.reward'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 13002, | |||
args = {'prophecy', 'seal_cost'}, | |||
header = i18n.item_table.seal_cost, | header = i18n.item_table.seal_cost, | ||
fields = {'prophecies.seal_cost'}, | fields = {'prophecies.seal_cost'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'currency', | |||
}, | |||
}, | }, | ||
{ | { | ||
order = 13003, | |||
args = {'prediction_text'}, | |||
header = i18n.item_table.prediction_text, | header = i18n.item_table.prediction_text, | ||
fields = {'prophecies.prediction_text'}, | fields = {'prophecies.prediction_text'}, | ||
display = h. | display = h.display_value_factory{ | ||
color = 'value', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 14000, | order = 14000, | ||
args = {'version', 'release_version'}, | |||
header = i18n.item_table.release_version, | header = i18n.item_table.release_version, | ||
fields = {'items.release_version'}, | fields = {'items.release_version'}, | ||
Line 854: | Line 921: | ||
) | ) | ||
end, | end, | ||
}, | |||
{ | |||
order = 15000, | order = 15000, | ||
args = {'drop', 'drop_level'}, | |||
header = i18n.item_table.drop_level, | |||
fields = {'items.drop_level'}, | |||
display = h.display_value_factory{}, | |||
}, | }, | ||
{ | { | ||
order = 15001, | |||
args = {'drop_level_maximum'}, | |||
header = i18n.item_table.drop_level_maximum, | |||
fields = {'items.drop_level_maximum'}, | |||
display = h.display_value_factory{}, | |||
}, | |||
{ | |||
order = 15002, | |||
args = {'version', 'removal_version'}, | |||
header = i18n.item_table.removal_version, | header = i18n.item_table.removal_version, | ||
fields = {'items.removal_version'}, | fields = {'items.removal_version'}, | ||
Line 871: | Line 952: | ||
) | ) | ||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 15003, | |||
args = {'drop', 'drop_enabled'}, | |||
header = i18n.item_table.drop_enabled, | header = i18n.item_table.drop_enabled, | ||
fields = {'items.drop_enabled'}, | fields = {'items.drop_enabled'}, | ||
display = h. | display = h.display_value_factory{}, | ||
display = h.display_yesno_factory{ | |||
field = 'items.drop_enabled', | |||
}, | |||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 15004, | |||
args = {'drop', 'drop_areas'}, | |||
header = i18n.item_table.drop_areas, | header = i18n.item_table.drop_areas, | ||
fields = {'items.drop_areas_html'}, | fields = {'items.drop_areas_html'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 15005, | |||
args = {'drop', 'drop_monsters'}, | |||
header = i18n.item_table.drop_monsters, | header = i18n.item_table.drop_monsters, | ||
fields = {'items.drop_monsters'}, | fields = {'items.drop_monsters'}, | ||
Line 947: | Line 1,016: | ||
h.na_or_val(tr, table.concat(tbl, '<br>')) | h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 15006, | |||
args = {'drop', 'drop_text'}, | |||
header = i18n.item_table.drop_text, | header = i18n.item_table.drop_text, | ||
fields = {'items.drop_text'}, | fields = {'items.drop_text'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 16000, | |||
args = {'quest'}, | |||
header = i18n.item_table.quest_rewards, | header = i18n.item_table.quest_rewards, | ||
fields = {'items._pageName'}, | fields = {'items._pageName'}, | ||
Line 1,005: | Line 1,074: | ||
local value = table.concat(tbl, '<br>') | local value = table.concat(tbl, '<br>') | ||
if value == nil or value == '' then | if value == nil or value == '' then | ||
tr: | tr:node(m_util.html.table_cell('na')) | ||
else | else | ||
tr | tr | ||
Line 1,013: | Line 1,082: | ||
end | end | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 17000, | |||
args = {'vendor'}, | |||
header = i18n.item_table.vendor_rewards, | header = i18n.item_table.vendor_rewards, | ||
fields = {'items._pageName'}, | fields = {'items._pageName'}, | ||
Line 1,065: | Line 1,134: | ||
local value = table.concat(tbl, '<br>') | local value = table.concat(tbl, '<br>') | ||
if value == nil or value == '' then | if value == nil or value == '' then | ||
tr: | tr:node(m_util.html.table_cell('na')) | ||
else | else | ||
tr | tr | ||
Line 1,073: | Line 1,142: | ||
end | end | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 18000, | |||
args = {'price', 'purchase_cost'}, | |||
header = i18n.item_table.purchase_costs, | header = i18n.item_table.purchase_costs, | ||
fields = {'item_purchase_costs.name', 'item_purchase_costs.amount'}, | fields = {'item_purchase_costs.name', 'item_purchase_costs.amount'}, | ||
Line 1,093: | Line 1,162: | ||
h.na_or_val(tr, table.concat(tbl, '<br>')) | h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 18001, | |||
args = {'price', 'sell_price'}, | |||
header = i18n.item_table.sell_price, | header = i18n.item_table.sell_price, | ||
fields = {'item_sell_prices.name', 'item_sell_prices.amount'}, | fields = {'item_sell_prices.name', 'item_sell_prices.amount'}, | ||
Line 1,135: | Line 1,204: | ||
h.na_or_val(tr, table.concat(tbl, '<br>')) | h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 19000, | |||
args = {'boss', 'boss_name'}, | |||
header = i18n.item_table.boss_name, | header = i18n.item_table.boss_name, | ||
fields = {'maps.area_id'}, | fields = {'maps.area_id'}, | ||
Line 1,187: | Line 1,256: | ||
h.na_or_val(tr, table.concat(tbl, '<br>')) | h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 19001, | |||
args = {'boss', 'boss_number'}, | |||
header = i18n.item_table.boss_number, | header = i18n.item_table.boss_number, | ||
fields = {'maps.area_id'}, | fields = {'maps.area_id'}, | ||
Line 1,235: | Line 1,304: | ||
tbl[#tbl+1] = v['areas.boss_monster_ids'] | tbl[#tbl+1] = v['areas.boss_monster_ids'] | ||
end | end | ||
tr | |||
:tag('td') | |||
:attr('data-sort-value', #tbl) | |||
:wikitext(#tbl) | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 20000, | |||
args = {'legacy'}, | |||
header = i18n.item_table.legacy, | header = i18n.item_table.legacy, | ||
fields = {'items.name'}, | fields = {'items.name'}, | ||
Line 1,322: | Line 1,394: | ||
:node(tbl) | :node(tbl) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 21000, | |||
args = {'granted_skills'}, | |||
header = i18n.item_table.granted_skills, | header = i18n.item_table.granted_skills, | ||
fields = {'items.name'}, | fields = {'items.name'}, | ||
Line 1,445: | Line 1,517: | ||
h.na_or_val(tr, table.concat(tbl, '<br>')) | h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 23000, | |||
args = {'alternate_art'}, | |||
header = i18n.item_table.alternate_art, | header = i18n.item_table.alternate_art, | ||
fields = {'items.alternate_art_inventory_icons'}, | fields = {'items.alternate_art_inventory_icons'}, | ||
Line 1,473: | Line 1,545: | ||
:wikitext(table.concat(out, '')) | :wikitext(table.concat(out, '')) | ||
end, | end, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
} | } | ||
data_map. | data_map.skill_gem = { | ||
{ | { | ||
order = 1000, | |||
args = {'icon'}, | |||
header = i18n.item_table.support_gem_letter, | header = i18n.item_table.support_gem_letter, | ||
fields = {'skill_gems.support_gem_letter_html'}, | fields = {'skill_gems.support_gem_letter_html'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 1001, | |||
args = {'skill_icon'}, | |||
header = i18n.item_table.skill_icon, | header = i18n.item_table.skill_icon, | ||
fields = {'skill.skill_icon'}, | fields = {'skill.skill_icon'}, | ||
display = h. | display = h.display_value_factory{ | ||
fmt_options = { | |||
[1] = { | |||
fmt = '[[%s]]', | |||
}, | |||
}, | }, | ||
} | }, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 2000, | |||
args = {'stat', 'stat_text'}, | |||
header = i18n.item_table.stats, | header = i18n.item_table.stats, | ||
fields = {'skill.stat_text'}, | fields = {'skill.stat_text'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 2001, | |||
args = {'quality', 'quality_stat_text'}, | |||
header = i18n.item_table.quality_stats, | header = i18n.item_table.quality_stats, | ||
fields = {'skill.quality_stat_text'}, | fields = {'skill.quality_stat_text'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 2100, | |||
args = {'description'}, | |||
header = i18n.item_table.description, | header = i18n.item_table.description, | ||
fields = {'skill.description'}, | fields = {'skill.description'}, | ||
display = h. | display = h.display_value_factory{}, | ||
sort_type = 'text', | sort_type = 'text', | ||
}, | }, | ||
{ | { | ||
order = 3000, | |||
header = | args = {'level'}, | ||
fields = h. | header = m_util.html.abbr( | ||
display = h. | string.format( | ||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.level_icon, | |||
i18n.item_table.gem_level_requirement | |||
), | |||
i18n.item_table.gem_level_requirement | |||
), | |||
fields = h.range_fields_factory{fields={'items.required_level'}}, | |||
display = h.display_range_factory{field='items.required_level'}, | |||
}, | }, | ||
{ | { | ||
order = 3001, | |||
header = i18n.item_table.skill_critical_strike_chance, | args = {'str'}, | ||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.str_icon, | |||
i18n.item_table.str_gem | |||
), | |||
i18n.item_table.str_gem | |||
), | |||
fields = {'skill_gems.strength_percent'}, | |||
display = h.display_yesno_factory{ | |||
field = 'skill_gems.strength_percent', | |||
condition = h.value_greater_than_zero, | |||
}, | |||
sort_type = 'text', | |||
}, | |||
{ | |||
order = 3002, | |||
args = {'dex'}, | |||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.dex_icon, | |||
i18n.item_table.dex_gem | |||
), | |||
i18n.item_table.dex_gem | |||
), | |||
fields = {'skill_gems.dexterity_percent'}, | |||
display = h.display_yesno_factory{ | |||
field = 'skill_gems.dexterity_percent', | |||
condition = h.value_greater_than_zero, | |||
}, | |||
sort_type = 'text', | |||
}, | |||
{ | |||
order = 3003, | |||
args = {'int'}, | |||
header = m_util.html.abbr( | |||
string.format( | |||
'[[%s|link=|alt=%s]]', | |||
i18n.item_table.int_icon, | |||
i18n.item_table.int_gem | |||
), | |||
i18n.item_table.int_gem | |||
), | |||
fields = {'skill_gems.intelligence_percent'}, | |||
display = h.display_yesno_factory{ | |||
field = 'skill_gems.intelligence_percent', | |||
condition = h.value_greater_than_zero, | |||
}, | |||
sort_type = 'text', | |||
}, | |||
{ | |||
order = 4000, | |||
args = {'crit'}, | |||
header = i18n.item_table.skill_critical_strike_chance, | |||
fields = {'skill_levels.critical_strike_chance'}, | fields = {'skill_levels.critical_strike_chance'}, | ||
display = h. | display = h.display_value_factory{ | ||
fmt_options = { | |||
[1] = { | |||
fmt = '%s%%', | |||
}, | |||
}, | }, | ||
}, | |||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,548: | Line 1,683: | ||
}, | }, | ||
{ | { | ||
order = 4001, | |||
args = {'cast_time'}, | |||
header = i18n.item_table.cast_time, | header = i18n.item_table.cast_time, | ||
fields = {'skill.cast_time'}, | fields = {'skill.cast_time'}, | ||
display = h. | display = h.display_value_factory{}, | ||
}, | }, | ||
{ | { | ||
order = 4002, | |||
args = {'aspd', 'attack_speed', 'attack_speed_multiplier'}, | |||
header = i18n.item_table.attack_speed_multiplier, | header = i18n.item_table.attack_speed_multiplier, | ||
fields = {'skill_levels.attack_speed_multiplier'}, | fields = {'skill_levels.attack_speed_multiplier'}, | ||
display = h. | display = h.display_value_factory{ | ||
fmt_options = { | |||
[1] = { | |||
fmt = '%s%%', | |||
}, | |||
}, | }, | ||
}, | |||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,575: | Line 1,708: | ||
}, | }, | ||
{ | { | ||
order = 4003, | |||
args = {'dmgeff'}, | |||
header = i18n.item_table.damage_effectiveness, | header = i18n.item_table.damage_effectiveness, | ||
fields = {'skill_levels.damage_effectiveness'}, | fields = {'skill_levels.damage_effectiveness'}, | ||
display = h. | display = h.display_value_factory{ | ||
fmt_options = { | |||
[1] = { | |||
fmt = '%s%%', | |||
}, | |||
}, | }, | ||
}, | |||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,592: | Line 1,726: | ||
}, | }, | ||
{ | { | ||
order = 5000, | |||
header = i18n.item_table. | args = {'mcm', 'cost_multiplier'}, | ||
fields = {'skill_levels. | header = i18n.item_table.cost_multiplier, | ||
display = h. | fields = {'skill_levels.cost_multiplier'}, | ||
display = h.display_value_factory{ | |||
fmt_options = { | |||
[1] = { | |||
fmt = '%s%%', | |||
}, | |||
}, | }, | ||
}, | |||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,609: | Line 1,744: | ||
}, | }, | ||
{ | { | ||
order = 5001, | |||
args = {'mana'}, | |||
header = i18n.item_table.mana_cost, | header = i18n.item_table.mana_cost, | ||
fields = {'skill_levels.cost_amounts', 'skill_levels.mana_reservation_percent', 'skill_levels.mana_reservation_flat'}, | fields = {'skill_levels.cost_amounts', 'skill_levels.mana_reservation_percent', 'skill_levels.mana_reservation_flat'}, | ||
Line 1,628: | Line 1,764: | ||
cost_field = 'skill_levels.cost_amounts' | cost_field = 'skill_levels.cost_amounts' | ||
end | end | ||
h.display_value_factory{ | |||
h. | fmt_options = { | ||
[1] = { | |||
fmt = '%d' .. appendix, | |||
}, | |||
}, | }, | ||
}(tr, data, {cost_field}, data2) | |||
end, | end, | ||
-- Need one set of options per field. | -- Need one set of options per field. | ||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,651: | Line 1,786: | ||
}, | }, | ||
{ | { | ||
order = 6000, | |||
args = {'vaal'}, | |||
header = i18n.item_table.vaal_souls_requirement, | header = i18n.item_table.vaal_souls_requirement, | ||
fields = {'skill_levels.vaal_souls_requirement'}, | fields = {'skill_levels.vaal_souls_requirement'}, | ||
display = h. | display = h.display_value_factory{}, | ||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,667: | Line 1,798: | ||
}, | }, | ||
{ | { | ||
order = 6001, | |||
args = {'vaal'}, | |||
header = i18n.item_table.stored_uses, | header = i18n.item_table.stored_uses, | ||
fields = {'skill_levels.vaal_stored_uses'}, | fields = {'skill_levels.vaal_stored_uses'}, | ||
display = h. | display = h.display_value_factory{}, | ||
field_options = { | |||
[1] = { | [1] = { | ||
skill_levels = true, | skill_levels = true, | ||
Line 1,683: | Line 1,810: | ||
}, | }, | ||
{ | { | ||
order = 7000, | |||
args = {'radius'}, | |||
header = i18n.item_table.primary_radius, | header = i18n.item_table.primary_radius, | ||
fields = {'skill.radius', 'skill.radius_description'}, | fields = {'skill.radius', 'skill.radius_description'}, | ||
field_options = { | |||
[2] = { | |||
optional = true, | |||
}, | |||
}, | |||
display = function (tr, data) | display = function (tr, data) | ||
tr | tr | ||
:tag('td') | :tag('td') | ||
:attr('data-sort-value', data['skill.radius']) | :attr('data-sort-value', data['skill.radius']) | ||
:wikitext(h. | :wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_description'}(data['skill.radius'])) | ||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 7001, | |||
args = {'radius'}, | |||
header = i18n.item_table.secondary_radius, | header = i18n.item_table.secondary_radius, | ||
fields = {'skill.radius_secondary', 'skill.radius_secondary_description'}, | fields = {'skill.radius_secondary', 'skill.radius_secondary_description'}, | ||
field_options = { | |||
[2] = { | |||
optional = true, | |||
}, | |||
}, | |||
display = function (tr, data) | display = function (tr, data) | ||
tr | tr | ||
:tag('td') | :tag('td') | ||
:attr('data-sort-value', data['skill.radius_secondary']) | :attr('data-sort-value', data['skill.radius_secondary']) | ||
:wikitext(h. | :wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_secondary_description'}(data['skill.radius_secondary'])) | ||
end, | end, | ||
}, | }, | ||
{ | { | ||
order = 7002, | |||
args = {'radius'}, | |||
header = i18n.item_table.tertiary_radius, | header = i18n.item_table.tertiary_radius, | ||
fields = {'skill.radius_tertiary', 'skill.radius_tertiary_description'}, | fields = {'skill.radius_tertiary', 'skill.radius_tertiary_description'}, | ||
field_options = { | |||
[2] = { | |||
optional = true, | |||
}, | |||
}, | |||
display = function (tr, data) | display = function (tr, data) | ||
tr | tr | ||
:tag('td') | :tag('td') | ||
:attr('data-sort-value', data['skill.radius_tertiary']) | :attr('data-sort-value', data['skill.radius_tertiary']) | ||
:wikitext(h. | :wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_tertiary_description'}(data['skill.radius_tertiary'])) | ||
end, | end, | ||
}, | }, | ||
} | } | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Main functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local function _item_table(args) | ||
function | |||
--[[ | --[[ | ||
Creates a generic table for items. | Creates a generic table for items. | ||
Line 1,774: | Line 1,883: | ||
local t = os.clock() | local t = os.clock() | ||
args.mode = args.mode or 'item' | |||
local modes = { | local modes = { | ||
skill = { | skill = { | ||
data = data_map. | data = data_map.skill_gem, | ||
header = i18n.item_table.skill_gem, | header = i18n.item_table.skill_gem, | ||
}, | }, | ||
Line 1,792: | Line 1,895: | ||
}, | }, | ||
} | } | ||
if modes[args.mode] == nil then | |||
error(i18n.errors.invalid_item_table_mode) | |||
end | |||
-- Handle deprecated "q_" args | |||
local query_params = { | |||
'tables', | |||
'join', | |||
'where', | |||
'groupBy', | |||
'having', | |||
'orderBy', | |||
'limit', | |||
'offset', | |||
} | |||
for _, v in ipairs(query_params) do | |||
args[v] = args[v] or args['q_' .. v] | |||
end | end | ||
if | -- A where clause is required; there are far too many items to list in one table | ||
error(i18n.errors. | if args.where == nil then | ||
error(string.format(i18n.errors.generic_required_parameter, 'where')) | |||
end | end | ||
Line 1,808: | Line 1,926: | ||
local row_infos = {} | local row_infos = {} | ||
for _, row_info in ipairs(modes[ | for _, row_info in ipairs(modes[args.mode].data) do | ||
local enabled = false | local enabled = false | ||
if | if type(row_info.args) == 'table' then | ||
for _, a in ipairs(row_info.args) do | |||
if m_util.cast.boolean(args[a]) then | |||
for _, | |||
if m_util.cast.boolean( | |||
enabled = true | enabled = true | ||
break | break | ||
end | end | ||
end | end | ||
else | |||
enabled = true | |||
end | end | ||
if enabled then | if enabled then | ||
row_info. | row_info.field_options = row_info.field_options or {} | ||
row_infos[#row_infos+1] = row_info | row_infos[#row_infos+1] = row_info | ||
end | end | ||
Line 1,832: | Line 1,947: | ||
local stat_columns = {} | local stat_columns = {} | ||
local query_stats = {} | local query_stats = {} | ||
for i=1, math.huge do -- repeat until no more columns are found | |||
local prefix = string.format('stat_column%s_', i) | local prefix = string.format('stat_column%s_', i) | ||
if args[prefix .. 'stat1_id'] == nil then | |||
-- Each column requires at least one stat id | |||
break | |||
end | |||
local col_info = { | local col_info = { | ||
header = | header = args[prefix .. 'header'] or tostring(i), | ||
format = | format = args[prefix .. 'format'], | ||
stat_format = | stat_format = args[prefix .. 'stat_format'] or 'separate', | ||
order = tonumber( | order = tonumber(args[prefix .. 'order']) or (10000000 + i), | ||
stats = {}, | stats = {}, | ||
} | } | ||
for j=1, math.huge do | |||
local stat_id = args[string.format('%sstat%s_id', prefix, j)] | |||
if stat_id == nil then | |||
break | |||
local | |||
end | end | ||
table.insert(col_info.stats, {id=stat_id}) | |||
query_stats[stat_id] = true | |||
end | end | ||
table.insert(stat_columns, col_info) | |||
end | |||
for _, col_info in ipairs(stat_columns) do | for _, col_info in ipairs(stat_columns) do | ||
local row_info = { | local row_info = { | ||
header = col_info.header, | header = col_info.header, | ||
fields = {}, | fields = {}, | ||
display = function(tr, data | display = function (tr, data) | ||
if col_info.stat_format == 'separate' then | if col_info.stat_format == 'separate' then | ||
local stat_texts = {} | local stat_texts = {} | ||
Line 1,888: | Line 1,985: | ||
local stat = (results2.stats[data['items._pageName']] or {})[stat_info.id] | local stat = (results2.stats[data['items._pageName']] or {})[stat_info.id] | ||
if stat ~= nil then | if stat ~= nil then | ||
stat_texts[#stat_texts+1] = m_util.html.format_value( | stat_texts[#stat_texts+1] = m_util.html.format_value(args, stat, {color=false}) | ||
vmax = vmax + stat.max | vmax = vmax + stat.max | ||
end | end | ||
Line 1,894: | Line 1,991: | ||
if num_stats ~= #stat_texts then | if num_stats ~= #stat_texts then | ||
tr: | tr:node(m_util.html.table_cell('na')) | ||
else | else | ||
local text | local text | ||
Line 1,930: | Line 2,027: | ||
:attr('data-sort-value', total_stat.max) | :attr('data-sort-value', total_stat.max) | ||
:attr('class', 'tc -mod') | :attr('class', 'tc -mod') | ||
:wikitext(string.format(col_info.format, m_util.html.format_value( | :wikitext(string.format(col_info.format, m_util.html.format_value(args, total_stat, {no_color=true}))) | ||
else | else | ||
error(string.format(i18n.errors.generic_argument_parameter, 'stat_format', col_info.stat_format)) | error(string.format(i18n.errors.generic_argument_parameter, 'stat_format', col_info.stat_format)) | ||
Line 1,945: | Line 2,042: | ||
end) | end) | ||
-- | -- Build Cargo query | ||
local | local tables = {'items'} | ||
local fields = { | local fields = { | ||
'items._pageID', | 'items._pageID', | ||
Line 1,956: | Line 2,053: | ||
'items.size_y', | 'items.size_y', | ||
} | } | ||
local query = { | |||
where = args.where, | |||
groupBy = table.concat({'items._pageID', args.groupBy}, ', '), | |||
having = args.having, | |||
orderBy = args.orderBy, | |||
limit = args.limit, | |||
offset = args.offset, | |||
} | } | ||
-- Namespace condition | |||
-- This is mainly to prevent items from user pages or other testing pages | |||
-- from being returned in the query results. | |||
if args.namespaces ~= 'any' then | |||
local namespaces = m_util.cast.table(args.namespaces, {callback=m_util.cast.number}) | |||
if #namespaces > 0 then | |||
namespaces = table.concat(namespaces, ',') | |||
else | |||
namespaces = m_item_util.get_item_namespaces{format = 'list'} | |||
end | end | ||
query.where = string.format('(%s) AND items._pageNamespace IN (%s)', query.where, namespaces) | |||
end | end | ||
-- | -- Minimum required tables and fields, based on display options | ||
local skill_levels = {} | local skill_levels = {} | ||
for _, rowinfo in ipairs(row_infos) do | for _, rowinfo in ipairs(row_infos) do | ||
Line 1,988: | Line 2,082: | ||
end | end | ||
for index, field in ipairs(rowinfo.fields) do | for index, field in ipairs(rowinfo.fields) do | ||
rowinfo. | rowinfo.field_options[index] = rowinfo.field_options[index] or {} | ||
if rowinfo. | if rowinfo.field_options[index].skill_levels then | ||
skill_levels[#skill_levels+1] = field | skill_levels[#skill_levels+1] = field | ||
else | else | ||
fields[#fields+1] = field | fields[#fields+1] = field | ||
tables[#tables+1] = m_util.string.split(field, '.', true)[1] | |||
end | end | ||
end | end | ||
end | end | ||
if #skill_levels > 0 then | if #skill_levels > 0 then | ||
fields[#fields+1] = 'skill.max_level' | fields[#fields+1] = 'skill.max_level' | ||
tables[#tables+1] = 'skill' | |||
tables[#tables+1] = | |||
end | end | ||
tables = m_util.table.remove_duplicates(tables) | |||
-- Minimum required joins, based on display options | |||
-- | |||
local joins = {} | local joins = {} | ||
for | for _, 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 | ||
-- Append additional tables | |||
args.tables = m_util.cast.table(args.tables) | |||
if type(args.tables) == 'table' and #args.tables > 0 then | |||
tables = m_util.table.merge(tables, args.tables) | |||
end | end | ||
-- | -- Make join clause | ||
if #joins > 0 or args.join then | |||
-- m_util.table.merge rebuilds the table, which removes empty values | |||
query.join = table.concat(m_util.table.merge(joins, {args.join}), ', ') | |||
end | |||
-- Query results | -- Query results | ||
local results = m_cargo.query( | local results = m_cargo.query(tables, fields, query) | ||
if #results == 0 and | if #results == 0 and args.default ~= nil then | ||
return | return args.default | ||
end | end | ||
Line 2,075: | Line 2,157: | ||
if #stat_columns > 0 then | if #stat_columns > 0 then | ||
local | local stat_results = m_cargo.query( | ||
{'items', 'item_stats'}, | {'items', 'item_stats'}, | ||
{'item_stats._pageName', 'item_stats.id', 'item_stats.min', 'item_stats.max', 'item_stats.avg'}, | {'item_stats._pageName', 'item_stats.id', 'item_stats.min', 'item_stats.max', 'item_stats.avg'}, | ||
{ | { | ||
where=string.format('item_stats. | join = 'items._pageID=item_stats._pageID', | ||
where = string.format( | |||
'item_stats._pageID IN (%s) AND item_stats.id IN ("%s")', | |||
groupBy='items._pageID, item_stats.id', | table.concat(m_util.table.column(results, 'items._pageID'), ','), | ||
table.concat(m_util.table.keys(query_stats), '","') | |||
), | |||
groupBy = 'items._pageID, item_stats.id', | |||
} | } | ||
) | ) | ||
for _, row in ipairs(stat_results) do | |||
for _, row in ipairs( | |||
local stat = { | local stat = { | ||
min = tonumber(row['item_stats.min']), | min = tonumber(row['item_stats.min']), | ||
Line 2,117: | Line 2,185: | ||
end | end | ||
end | end | ||
-- | -- | ||
Line 2,125: | Line 2,192: | ||
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') | ||
if m_util.cast.boolean( | if m_util.cast.boolean(args.responsive) then | ||
tbl:addClass('responsive-table') | tbl:addClass('responsive-table') | ||
end | end | ||
Line 2,133: | Line 2,200: | ||
tr | tr | ||
:tag('th') | :tag('th') | ||
:wikitext(modes[ | :wikitext(modes[args.mode].header) | ||
:done() | :done() | ||
for _, row_info in ipairs(row_infos) do | for _, row_info in ipairs(row_infos) do | ||
Line 2,160: | Line 2,227: | ||
} | } | ||
if | if args.no_html == nil then | ||
il_args.html = row['items.html'] | il_args.html = row['items.html'] | ||
end | end | ||
if | if args.large then | ||
il_args.large = | il_args.large = args.large | ||
end | end | ||
Line 2,180: | Line 2,247: | ||
-- this will bet set to an empty value not nil confusingly | -- this will bet set to an empty value not nil confusingly | ||
if row[field] == nil or row[field] == '' then | if row[field] == nil or row[field] == '' then | ||
local | local options = rowinfo.field_options[index] | ||
if | if options.optional ~= true and options.skill_levels ~= true then | ||
display = false | display = false | ||
break | break | ||
Line 2,192: | Line 2,259: | ||
rowinfo.display(tr, row, rowinfo.fields, results2) | rowinfo.display(tr, row, rowinfo.fields, results2) | ||
else | else | ||
tr: | tr:node(m_util.html.table_cell('na')) | ||
end | end | ||
end | end | ||
Line 2,201: | Line 2,268: | ||
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, {tables=tables, fields=fields, query=query}}) | ||
return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist= | return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist=args.debug}) | ||
end | end | ||
Line 2,216: | Line 2,282: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
function | local function _map_item_drops(args) | ||
--[[ | --[[ | ||
Gets the area id from the map item and activates | Gets the area id from the map item and activates | ||
Line 2,224: | Line 2,290: | ||
= p.map_item_drops{page='Underground River Map (War for the Atlas)'} | = p.map_item_drops{page='Underground River Map (War for the Atlas)'} | ||
]] | ]] | ||
local tables = {'maps'} | local tables = {'maps'} | ||
Line 2,239: | Line 2,299: | ||
groupBy = 'maps._pageName', | groupBy = 'maps._pageName', | ||
} | } | ||
if | if args.page then | ||
-- Join with _pageData in order to check for page redirect | -- Join with _pageData in order to check for page redirect | ||
tables[#tables+1] = '_pageData' | tables[#tables+1] = '_pageData' | ||
Line 2,245: | Line 2,305: | ||
query.where = string.format( | query.where = string.format( | ||
'_pageData._pageName="%s"', | '_pageData._pageName="%s"', | ||
args.page | |||
) | ) | ||
query.join = 'maps._pageName = _pageData._pageNameOrRedirect' | query.join = 'maps._pageName = _pageData._pageNameOrRedirect' | ||
Line 2,260: | Line 2,320: | ||
id = results[1]['maps.area_id'] | id = results[1]['maps.area_id'] | ||
end | end | ||
return | return mw.getCurrentFrame():expandTemplate{ title = 'Area item drops', args = {area_id=id} } | ||
end | end | ||
Line 2,267: | Line 2,327: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
function | local function _prophecy_description(args) | ||
args.page = args.page or tostring(mw.title.getCurrentTitle()) | |||
local results = m_cargo.query( | local results = m_cargo.query( | ||
Line 2,280: | Line 2,334: | ||
{'prophecies.objective', 'prophecies.reward'}, | {'prophecies.objective', 'prophecies.reward'}, | ||
{ | { | ||
where=string.format('prophecies._pageName="%s"', | where=string.format('prophecies._pageName="%s"', args.page), | ||
-- Only need each page name once | -- Only need each page name once | ||
groupBy='prophecies._pageName', | groupBy='prophecies._pageName', | ||
Line 2,303: | Line 2,357: | ||
end | end | ||
-- | local function _simple_item_list(args) | ||
--[[ | |||
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, | |||
} | } | ||
]] | |||
local query = {} | |||
for key, value in pairs(args) do | |||
if string.sub(key, 0, 2) == 'q_' then | |||
query[string.sub(key, 3)] = value | |||
local | |||
end | end | ||
end | |||
local fields = { | |||
'items._pageName', | |||
'items.name', | |||
'items.class', | |||
} | |||
if args.no_icon == nil then | |||
fields[#fields+1] = 'items.inventory_icon' | |||
end | |||
if args.no_html == nil then | |||
fields[#fields+1] = 'items.html' | |||
end | |||
local tables = m_util.cast.table(args.q_tables) | |||
table.insert(tables, 1, 'items') | |||
query.groupBy = query.groupBy or 'items._pageID' | |||
local results = m_cargo.query( | |||
tables, | |||
fields, | |||
query | |||
) | |||
local out = {} | local out = {} | ||
for _, row in ipairs(results) do | |||
local page | |||
for | if args.use_name_as_link ~= nil then | ||
if | 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 args.format == nil then | |||
out[#out+1] = string.format('* %s', link) | |||
elseif args.format == 'none' then | |||
out[#out+1] = link | |||
elseif args.format == 'li' then | |||
out[#out+1] = string.format('<li>%s</li>', link) | |||
else | |||
error(string.format(i18n.errors.generic_argument_parameter, 'format', args.format)) | |||
end | end | ||
end | end | ||
if args.format == nil then | |||
return table.concat(out, '\n') | |||
elseif args.format == 'none' then | |||
return table.concat(out, '\n') | |||
elseif args.format == 'li' then | |||
return table.concat(out) | |||
end | end | ||
end | end | ||
-- ---------------------------------------------------------------------------- | |||
-- Exported functions | |||
-- ---------------------------------------------------------------------------- | |||
local p = {} | |||
-- | |||
-- Template:Item table | |||
-- | |||
p.item_table = m_util.misc.invoker_factory(_item_table) | |||
-- | |||
-- Template:Map item drops | |||
-- | |||
p.map_item_drops = m_util.misc.invoker_factory(_map_item_drops, { | |||
wrappers = cfg.wrappers.map_item_drops, | |||
}) | |||
-- | |||
-- Template:Prophecy description | |||
-- | |||
p.prophecy_description = m_util.misc.invoker_factory(_prophecy_description, { | |||
wrappers = cfg.wrappers.prophecy_description, | |||
}) | |||
-- | |||
-- Template:Simple item list | |||
-- | |||
p.simple_item_list = m_util.misc.invoker_factory(_simple_item_list, { | |||
wrappers = cfg.wrappers.simple_item_list, | |||
}) | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- Debug | -- Debug | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
p.debug = {} | p.debug = {} | ||
Line 2,606: | Line 2,504: | ||
function p.debug.skill_gem_all() | function p.debug.skill_gem_all() | ||
return p.debug._tbl_data(data_map. | return p.debug._tbl_data(data_map.skill_gem) | ||
end | end | ||
return p | return p |
Latest revision as of 23:10, 25 October 2024
The item module provides functionality for creating item tables.
Implemented templates
This module implements the following templates:
The above documentation is transcluded from Module:Item table/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Item table
--
-- This module implements Template:Item table and other templates that query
-- and display tables or lists of items.
-------------------------------------------------------------------------------
require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item table')
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game')
-- Lazy loading
local m_item_util -- require('Module:Item util')
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.range_fields_factory(args)
-- Returns a function that gets the range fields for the given keys
local suffixes = {'maximum', 'text', 'colour'}
if args.full then
suffixes[#suffixes+1] = 'minimum'
end
return function ()
local fields = {}
for _, field in ipairs(args.fields) do
for _, suffix in ipairs(suffixes) do
fields[#fields+1] = string.format('%s_range_%s', field, suffix)
end
end
return fields
end
end
function h.display_value_factory(args)
args.fmt_options = args.fmt_options or {}
return function(tr, data, fields, data2)
local values = {}
local fmt_values = {}
for index, field in ipairs(fields) do
local value = {
min=data[field],
max=data[field],
base=data[field],
}
local sdata = data2 and data2.skill_levels[data['items._pageName']]
if sdata then --For skill data
-- Use the fixed value, or the first-level value.
value.min = value.min or sdata['0'][field] or sdata['1'][field]
-- Fall back to the fixed value, and then the max level value.
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 options = args.fmt_options[index] or {}
-- global color is set, no overrides
if args.color ~= nil then
options.color = false
end
fmt_values[#fmt_values+1] = m_util.html.format_value(nil, value, options)
end
end
if #values == 0 then
tr:node(m_util.html.table_cell('na'))
else
local td = tr:tag('td')
td
:attr('data-sort-value', table.concat(values, ', '))
:wikitext(table.concat(fmt_values, ', '))
if args.color then
td:attr('class', 'tc -' .. args.color)
end
end
end
end
function h.display_range_factory(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.display_range_composite_factory(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.display_descriptor_value_factory(args)
-- Arguments:
-- key
-- tbl
args = args or {}
return function (value)
if args.tbl[args.key] then
value = m_util.html.abbr(value, args.tbl[args.key])
end
return value
end
end
function h.display_atlas_tier_factory(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
function h.display_yesno_factory(args)
-- args:
-- field
-- condition
args = args or {}
args.condition = args.condition or function (value)
return m_util.cast.boolean(value, {cast_nil=false})
end
return function (tr, data)
local type = args.condition(data[args.field]) and 'yes' or 'no'
tr:node(m_util.html.table_cell(type))
end
end
function h.value_greater_than_zero(value)
value = m_util.cast.number(value, {default=0})
if value > 0 then
return true
end
return false
end
function h.na_or_val(tr, value)
if value == nil or value == '' then
tr:node(m_util.html.table_cell('na'))
else
tr
:tag('td')
:attr('data-sort-value', value)
:wikitext(value)
end
end
-- ----------------------------------------------------------------------------
-- Data mappings
-- ----------------------------------------------------------------------------
local data_map = {}
-- for sort type see:
-- https://meta.wikimedia.org/wiki/Help:Sorting
data_map.generic_item = {
{
order = 1000,
args = {'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,
sort_type = 'text',
},
{
order = 1001,
args = {'class'},
header = i18n.item_table.item_class,
fields = {'items.class'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '[[%s]]',
},
},
},
sort_type = 'text',
},
{
order = 1002,
args = {'rarity'},
header = i18n.item_table.rarity,
fields = {'items.rarity'},
display = h.display_value_factory{},
},
{
order = 1003,
args = {'rarity_id'},
header = i18n.item_table.rarity_id,
fields = {'items.rarity_id'},
display = h.display_value_factory{},
},
{
order = 1004,
args = {'metadata_id'},
header = i18n.item_table.metadata_id,
fields = {'items.metadata_id'},
display = h.display_value_factory{},
},
{
order = 1005,
args = {'size'},
header = i18n.item_table.inventory_size,
fields = {'items.size_x', 'items.size_y'},
display = function (tr, data)
tr
:tag('td')
:attr('data-sort-value', data['items.size_x'] * data['items.size_y'])
:wikitext(string.format('%s×%s', data['items.size_x'], data['items.size_y']))
end,
},
{
order = 1100,
args = {'essence'},
header = i18n.item_table.essence_level,
fields = {'essences.level'},
display = h.display_value_factory{},
},
{
order = 1300,
args = {'stack_size'},
header = i18n.item_table.stack_size,
fields = {'stackables.stack_size'},
display = h.display_value_factory{},
},
{
order = 1301,
args = {'stack_size_currency_tab'},
header = i18n.item_table.stack_size_currency_tab,
fields = {'stackables.stack_size_currency_tab'},
display = h.display_value_factory{},
},
-- Requirements
{
order = 1400,
args = {'level'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.level_icon,
i18n.item_table.required_level
),
i18n.item_table.required_level
),
fields = h.range_fields_factory{fields={'items.required_level'}},
display = h.display_range_factory{field='items.required_level'},
},
{
order = 1401,
args = {'str'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.str_icon,
i18n.item_table.required_str
),
i18n.item_table.required_str
),
fields = h.range_fields_factory{fields={'items.required_strength'}},
display = h.display_range_factory{field='items.required_strength'},
},
{
order = 1402,
args = {'dex'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.dex_icon,
i18n.item_table.required_dex
),
i18n.item_table.required_dex
),
fields = h.range_fields_factory{fields={'items.required_dexterity'}},
display = h.display_range_factory{field='items.required_dexterity'},
},
{
order = 1403,
args = {'int'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.int_icon,
i18n.item_table.required_int
),
i18n.item_table.required_int
),
fields = h.range_fields_factory{fields={'items.required_intelligence'}},
display = h.display_range_factory{field='items.required_intelligence'},
},
-- Armour 15xx
{
order = 1500,
args = {'ar'},
header = i18n.item_table.armour,
fields = h.range_fields_factory{fields={'armours.armour'}},
display = h.display_range_factory{field='armours.armour'},
},
{
order = 1501,
args = {'ev'},
header =i18n.item_table.evasion,
fields = h.range_fields_factory{fields={'armours.evasion'}},
display = h.display_range_factory{field='armours.evasion'},
},
{
order = 1502,
args = {'es'},
header = i18n.item_table.energy_shield,
fields = h.range_fields_factory{fields={'armours.energy_shield'}},
display = h.display_range_factory{field='armours.energy_shield'},
},
{
order = 1503,
args = {'wd'},
header = i18n.item_table.ward,
fields = h.range_fields_factory{fields={'armours.ward'}},
display = h.display_range_factory{field='armours.ward'},
},
{
order = 1504,
args = {'block'},
header = i18n.item_table.block,
fields = h.range_fields_factory{fields={'shields.block'}},
display = h.display_range_factory{field='shields.block'},
},
-- Weapons 16xx
{
order = 1600,
args = {'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 = 1601,
args = {'weapon', 'aps'},
header = i18n.item_table.attacks_per_second,
fields = h.range_fields_factory{fields={'weapons.attack_speed'}},
display = h.display_range_factory{field='weapons.attack_speed'},
},
{
order = 1602,
args = {'weapon', 'crit'},
header = i18n.item_table.local_critical_strike_chance,
fields = h.range_fields_factory{fields={'weapons.critical_strike_chance'}},
display = h.display_range_factory{field='weapons.critical_strike_chance'},
},
{
order = 1603,
args = {'physical_dps'},
header = i18n.item_table.physical_dps,
fields = h.range_fields_factory{fields={'weapons.physical_dps'}},
display = h.display_range_factory{field='weapons.physical_dps'},
},
{
order = 1604,
args = {'lightning_dps'},
header = i18n.item_table.lightning_dps,
fields = h.range_fields_factory{fields={'weapons.lightning_dps'}},
display = h.display_range_factory{field='weapons.lightning_dps'},
},
{
order = 1605,
args = {'cold_dps'},
header = i18n.item_table.cold_dps,
fields = h.range_fields_factory{fields={'weapons.cold_dps'}},
display = h.display_range_factory{field='weapons.cold_dps'},
},
{
order = 1606,
args = {'fire_dps'},
header = i18n.item_table.fire_dps,
fields = h.range_fields_factory{fields={'weapons.fire_dps'}},
display = h.display_range_factory{field='weapons.fire_dps'},
},
{
order = 1607,
args = {'chaos_dps'},
header = i18n.item_table.chaos_dps,
fields = h.range_fields_factory{fields={'weapons.chaos_dps'}},
display = h.display_range_factory{field='weapons.chaos_dps'},
},
{
order = 1608,
args = {'elemental_dps'},
header = i18n.item_table.elemental_dps,
fields = h.range_fields_factory{fields={'weapons.elemental_dps'}},
display = h.display_range_factory{field='weapons.elemental_dps'},
},
{
order = 1609,
args = {'poison_dps'},
header = i18n.item_table.poison_dps,
fields = h.range_fields_factory{fields={'weapons.poison_dps'}},
display = h.display_range_factory{field='weapons.poison_dps'},
},
{
order = 1610,
args = {'dps'},
header = i18n.item_table.dps,
fields = h.range_fields_factory{fields={'weapons.dps'}},
display = h.display_range_factory{field='weapons.dps'},
},
-- Flasks 17xx
{
order = 1700,
args = {'flask_life'},
header = i18n.item_table.flask_life,
fields = h.range_fields_factory{fields={'flasks.life'}},
display = h.display_range_factory{field='flasks.life'},
},
{
order = 1701,
args = {'flask_life_per_second'},
header = i18n.item_table.flask_life_per_second,
fields = h.range_fields_factory{fields={'flasks.life', 'flasks.duration'}, full=true},
display = h.display_range_composite_factory{field1='flasks.life', field2='flasks.duration'},
},
{
order = 1702,
args = {'flask_life_per_charge'},
header = i18n.item_table.flask_life_per_charge,
fields = h.range_fields_factory{fields={'flasks.life', 'flasks.charges_per_use'}, full=true},
display = h.display_range_composite_factory{field1='flasks.life', field2='flasks.charges_per_use'},
},
{
order = 1703,
args = {'flask_mana'},
header = i18n.item_table.flask_mana,
fields = h.range_fields_factory{fields={'flasks.mana'}},
display = h.display_range_factory{field='flasks.mana'},
},
{
order = 1704,
args = {'flask_mana_per_second'},
header = i18n.item_table.flask_mana_per_second,
fields = h.range_fields_factory{fields={'flasks.mana', 'flasks.duration'}, full=true},
display = h.display_range_composite_factory{field1='flasks.mana', field2='flasks.duration'},
},
{
order = 1705,
args = {'flask_mana_per_charge'},
header = i18n.item_table.flask_mana_per_charge,
fields = h.range_fields_factory{fields={'flasks.mana', 'flasks.charges_per_use'}, full=true},
display = h.display_range_composite_factory{field1='flasks.mana', field2='flasks.charges_per_use'},
},
{
order = 1706,
args = {'flask'},
header = i18n.item_table.flask_duration,
fields = h.range_fields_factory{fields={'flasks.duration'}},
display = h.display_range_factory{field='flasks.duration'},
},
{
order = 1707,
args = {'flask'},
header = i18n.item_table.flask_charges_per_use,
fields = h.range_fields_factory{fields={'flasks.charges_per_use'}},
display = h.display_range_factory{field='flasks.charges_per_use'},
},
{
order = 1708,
args = {'flask'},
header = i18n.item_table.flask_maximum_charges,
fields = h.range_fields_factory{fields={'flasks.charges_max'}},
display = h.display_range_factory{field='flasks.charges_max'},
},
-- Jewels 18xx
{
order = 1800,
args = {'jewel_limit'},
header = i18n.item_table.jewel_limit,
fields = {'jewels.jewel_limit'},
display = h.display_value_factory{},
},
{
order = 1801,
args = {'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,
},
-- Maps 19xx
{
order = 1900,
args = {'map_tier'},
header = i18n.item_table.map_tier,
fields = {'maps.tier'},
display = h.display_value_factory{},
},
{
order = 1901,
args = {'map_level'},
header = i18n.item_table.map_level,
fields = {'maps.area_level'},
display = h.display_value_factory{},
},
{
order = 1902,
args = {'map_guild_character'},
header = i18n.item_table.map_guild_character,
fields = {'maps.guild_character'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 1903,
args = {'map_series'},
header = i18n.item_table.map_series,
fields = {'maps.series'},
display = h.display_value_factory{},
},
{
order = 1904,
args = {'atlas_tier'},
header = i18n.item_table.atlas_tier,
fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
display = h.display_atlas_tier_factory{is_level=false},
colspan = 5,
},
{
order = 1905,
args = {'atlas_level'},
header = i18n.item_table.atlas_level,
fields = {'atlas_maps.map_tier0', 'atlas_maps.map_tier1', 'atlas_maps.map_tier2', 'atlas_maps.map_tier3', 'atlas_maps.map_tier4'},
display = h.display_atlas_tier_factory{is_level=true},
colspan = 5,
},
-- Map fragments 20xx
{
order = 2000,
args = {'map_fragment', 'map_fragment_limit'},
header = i18n.item_table.map_fragment_limit,
fields = {'map_fragments.map_fragment_limit'},
display = h.display_value_factory{},
},
-- Hideout decorations 21xx
{
order = 2100,
args = {'doodad', 'variation_count'},
header = i18n.item_table.variation_count,
fields = {'hideout_doodads.variation_count'},
display = h.display_value_factory{
color = 'mod',
},
},
-- Corpse items 22xx
{
order = 2200,
args = {'corpse', 'monster_category'},
header = i18n.item_table.monster_category,
fields = {'corpse_items.monster_category', 'corpse_items.monster_category_html'},
display = function (tr, data)
tr
:tag('td')
:attr('data-sort-value', m_game.constants.monster.categories[data['corpse_items.monster_category']].id)
:wikitext(data['corpse_items.monster_category_html'])
end,
},
{
order = 2201,
args = {'corpse', 'monster_abilities'},
header = i18n.item_table.monster_abilities,
fields = {'corpse_items.monster_abilities'},
display = h.display_value_factory{
color = 'mod',
},
sort_type = 'text',
},
-- Tinctures 23xx
{
order = 2300,
args = {'tincture'},
header = i18n.item_table.tincture_debuff_interval,
fields = h.range_fields_factory{fields={'tinctures.debuff_interval'}},
display = h.display_range_factory{field='tinctures.debuff_interval'},
},
{
order = 2301,
args = {'tincture'},
header = i18n.item_table.tincture_cooldown,
fields = h.range_fields_factory{fields={'tinctures.cooldown'}},
display = h.display_range_factory{field='tinctures.cooldown'},
},
-- Seed data 91xx,
{
order = 9100,
args = {'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 = 9101,
args = {'seed', 'seed_tier'},
header = i18n.item_table.seed_tier,
fields = {'harvest_seeds.tier'},
display = h.display_value_factory{},
},
{
order = 9102,
args = {'seed', 'seed_growth_cycles'},
header = i18n.item_table.seed_growth_cycles,
fields = {'harvest_seeds.growth_cycles'},
display = h.display_value_factory{},
},
{
order = 9110,
args = {'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.display_value_factory{
color = 'primal',
},
},
{
order = 9110,
args = {'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.display_value_factory{
color = 'vivid',
},
},
{
order = 9110,
args = {'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.display_value_factory{
color = 'wild',
},
},
{
order = 9113,
args = {'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.display_value_factory{},
},
{
order = 9114,
args = {'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.display_value_factory{},
},
{
order = 12000,
args = {'buff'},
header = i18n.item_table.buff_effects,
fields = {'item_buffs.stat_text'},
display = h.display_value_factory{
color = 'mod',
},
sort_type = 'text',
},
{
order = 12001,
args = {'stat'},
header = i18n.item_table.stats,
fields = {'items.stat_text'},
display = h.display_value_factory{
color = 'mod',
},
sort_type = 'text',
},
{
order = 12002,
args = {'description'},
header = i18n.item_table.description,
fields = {'items.description'},
display = h.display_value_factory{
color = 'mod',
},
sort_type = 'text',
},
{
order = 12003,
args = {'seed', 'seed_effect'},
header = i18n.item_table.seed_effects,
fields = {'harvest_seeds.effect'},
display = h.display_value_factory{
color = 'crafted',
},
sort_type = 'text',
},
{
order = 12100,
args = {'flavour_text'},
header = i18n.item_table.flavour_text,
fields = {'items.flavour_text'},
display = h.display_value_factory{
color = 'flavour',
},
sort_type = 'text',
},
{
order = 12200,
args = {'help_text'},
header = i18n.item_table.help_text,
fields = {'items.help_text'},
display = h.display_value_factory{
color = 'help',
},
sort_type = 'text',
},
{
order = 12300,
args = {'buff_icon'},
header = i18n.item_table.buff_icon,
fields = {'item_buffs.icon'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '[[%s]]',
},
},
},
sort_type = 'text',
},
{
order = 13000,
args = {'prophecy', 'objective'},
header = i18n.item_table.objective,
fields = {'prophecies.objective'},
display = h.display_value_factory{},
},
{
order = 13001,
args = {'prophecy', 'reward'},
header = i18n.item_table.reward,
fields = {'prophecies.reward'},
display = h.display_value_factory{},
},
{
order = 13002,
args = {'prophecy', 'seal_cost'},
header = i18n.item_table.seal_cost,
fields = {'prophecies.seal_cost'},
display = h.display_value_factory{
color = 'currency',
},
},
{
order = 13003,
args = {'prediction_text'},
header = i18n.item_table.prediction_text,
fields = {'prophecies.prediction_text'},
display = h.display_value_factory{
color = 'value',
},
sort_type = 'text',
},
{
order = 14000,
args = {'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,
args = {'drop', 'drop_level'},
header = i18n.item_table.drop_level,
fields = {'items.drop_level'},
display = h.display_value_factory{},
},
{
order = 15001,
args = {'drop_level_maximum'},
header = i18n.item_table.drop_level_maximum,
fields = {'items.drop_level_maximum'},
display = h.display_value_factory{},
},
{
order = 15002,
args = {'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 = 15003,
args = {'drop', 'drop_enabled'},
header = i18n.item_table.drop_enabled,
fields = {'items.drop_enabled'},
display = h.display_value_factory{},
display = h.display_yesno_factory{
field = 'items.drop_enabled',
},
sort_type = 'text',
},
{
order = 15004,
args = {'drop', 'drop_areas'},
header = i18n.item_table.drop_areas,
fields = {'items.drop_areas_html'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 15005,
args = {'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,
sort_type = 'text',
},
{
order = 15006,
args = {'drop', 'drop_text'},
header = i18n.item_table.drop_text,
fields = {'items.drop_text'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 16000,
args = {'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:node(m_util.html.table_cell('na'))
else
tr
:tag('td')
:attr('style', 'text-align:left')
:wikitext(value)
end
end,
sort_type = 'text',
},
{
order = 17000,
args = {'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:node(m_util.html.table_cell('na'))
else
tr
:tag('td')
:attr('style', 'text-align:left')
:wikitext(value)
end
end,
sort_type = 'text',
},
{
order = 18000,
args = {'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,
sort_type = 'text',
},
{
order = 18001,
args = {'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,
sort_type = 'text',
},
{
order = 19000,
args = {'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,
sort_type = 'text',
},
{
order = 19001,
args = {'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
tr
:tag('td')
:attr('data-sort-value', #tbl)
:wikitext(#tbl)
end,
},
{
order = 20000,
args = {'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,
sort_type = 'text',
},
{
order = 21000,
args = {'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,
sort_type = 'text',
},
{
order = 23000,
args = {'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,
sort_type = 'text',
},
}
data_map.skill_gem = {
{
order = 1000,
args = {'icon'},
header = i18n.item_table.support_gem_letter,
fields = {'skill_gems.support_gem_letter_html'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 1001,
args = {'skill_icon'},
header = i18n.item_table.skill_icon,
fields = {'skill.skill_icon'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '[[%s]]',
},
},
},
sort_type = 'text',
},
{
order = 2000,
args = {'stat', 'stat_text'},
header = i18n.item_table.stats,
fields = {'skill.stat_text'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 2001,
args = {'quality', 'quality_stat_text'},
header = i18n.item_table.quality_stats,
fields = {'skill.quality_stat_text'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 2100,
args = {'description'},
header = i18n.item_table.description,
fields = {'skill.description'},
display = h.display_value_factory{},
sort_type = 'text',
},
{
order = 3000,
args = {'level'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.level_icon,
i18n.item_table.gem_level_requirement
),
i18n.item_table.gem_level_requirement
),
fields = h.range_fields_factory{fields={'items.required_level'}},
display = h.display_range_factory{field='items.required_level'},
},
{
order = 3001,
args = {'str'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.str_icon,
i18n.item_table.str_gem
),
i18n.item_table.str_gem
),
fields = {'skill_gems.strength_percent'},
display = h.display_yesno_factory{
field = 'skill_gems.strength_percent',
condition = h.value_greater_than_zero,
},
sort_type = 'text',
},
{
order = 3002,
args = {'dex'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.dex_icon,
i18n.item_table.dex_gem
),
i18n.item_table.dex_gem
),
fields = {'skill_gems.dexterity_percent'},
display = h.display_yesno_factory{
field = 'skill_gems.dexterity_percent',
condition = h.value_greater_than_zero,
},
sort_type = 'text',
},
{
order = 3003,
args = {'int'},
header = m_util.html.abbr(
string.format(
'[[%s|link=|alt=%s]]',
i18n.item_table.int_icon,
i18n.item_table.int_gem
),
i18n.item_table.int_gem
),
fields = {'skill_gems.intelligence_percent'},
display = h.display_yesno_factory{
field = 'skill_gems.intelligence_percent',
condition = h.value_greater_than_zero,
},
sort_type = 'text',
},
{
order = 4000,
args = {'crit'},
header = i18n.item_table.skill_critical_strike_chance,
fields = {'skill_levels.critical_strike_chance'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '%s%%',
},
},
},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 4001,
args = {'cast_time'},
header = i18n.item_table.cast_time,
fields = {'skill.cast_time'},
display = h.display_value_factory{},
},
{
order = 4002,
args = {'aspd', 'attack_speed', 'attack_speed_multiplier'},
header = i18n.item_table.attack_speed_multiplier,
fields = {'skill_levels.attack_speed_multiplier'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '%s%%',
},
},
},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 4003,
args = {'dmgeff'},
header = i18n.item_table.damage_effectiveness,
fields = {'skill_levels.damage_effectiveness'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '%s%%',
},
},
},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 5000,
args = {'mcm', 'cost_multiplier'},
header = i18n.item_table.cost_multiplier,
fields = {'skill_levels.cost_multiplier'},
display = h.display_value_factory{
fmt_options = {
[1] = {
fmt = '%s%%',
},
},
},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 5001,
args = {'mana'},
header = i18n.item_table.mana_cost,
fields = {'skill_levels.cost_amounts', 'skill_levels.mana_reservation_percent', 'skill_levels.mana_reservation_flat'},
display = function (tr, data, fields, data2)
local appendix = ''
local cost_field = ''
local sdata = data2.skill_levels[data['items._pageName']]
-- Percentage Mana reservation is in level 0 of most of the skill_levels at this point, but spellslinger and awakened blasphemy change it per-level
-- Flat Mana resservation is in real gem levels, but maybe there will be fixed flat mana reservations in the future.
-- Per-use mana costs are stored in the real gem levels if they change, or in 0 if it's fixed.
if(sdata['1']['skill_levels.mana_reservation_percent'] ~= nil or sdata['0']['skill_levels.mana_reservation_percent'] ~= nil) then
cost_field = 'skill_levels.mana_reservation_percent'
appendix = appendix .. '%%'
elseif(sdata['1']['skill_levels.mana_reservation_flat'] ~= nil or sdata['0']['skill_levels.mana_reservation_flat'] ~= nil) then
cost_field = 'skill_levels.mana_reservation_flat'
appendix = appendix .. ' ' .. i18n.item_table.reserves_mana_suffix
elseif(sdata['1']['skill_levels.cost_amounts'] ~= nil or sdata['0']['skill_levels.cost_amounts'] ~= nil) then
cost_field = 'skill_levels.cost_amounts'
end
h.display_value_factory{
fmt_options = {
[1] = {
fmt = '%d' .. appendix,
},
},
}(tr, data, {cost_field}, data2)
end,
-- Need one set of options per field.
field_options = {
[1] = {
skill_levels = true,
},
[2] = {
skill_levels = true,
},
[3] = {
skill_levels = true,
},
},
},
{
order = 6000,
args = {'vaal'},
header = i18n.item_table.vaal_souls_requirement,
fields = {'skill_levels.vaal_souls_requirement'},
display = h.display_value_factory{},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 6001,
args = {'vaal'},
header = i18n.item_table.stored_uses,
fields = {'skill_levels.vaal_stored_uses'},
display = h.display_value_factory{},
field_options = {
[1] = {
skill_levels = true,
},
},
},
{
order = 7000,
args = {'radius'},
header = i18n.item_table.primary_radius,
fields = {'skill.radius', 'skill.radius_description'},
field_options = {
[2] = {
optional = true,
},
},
display = function (tr, data)
tr
:tag('td')
:attr('data-sort-value', data['skill.radius'])
:wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_description'}(data['skill.radius']))
end,
},
{
order = 7001,
args = {'radius'},
header = i18n.item_table.secondary_radius,
fields = {'skill.radius_secondary', 'skill.radius_secondary_description'},
field_options = {
[2] = {
optional = true,
},
},
display = function (tr, data)
tr
:tag('td')
:attr('data-sort-value', data['skill.radius_secondary'])
:wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_secondary_description'}(data['skill.radius_secondary']))
end,
},
{
order = 7002,
args = {'radius'},
header = i18n.item_table.tertiary_radius,
fields = {'skill.radius_tertiary', 'skill.radius_tertiary_description'},
field_options = {
[2] = {
optional = true,
},
},
display = function (tr, data)
tr
:tag('td')
:attr('data-sort-value', data['skill.radius_tertiary'])
:wikitext(h.display_descriptor_value_factory{tbl=data, key='skill.radius_tertiary_description'}(data['skill.radius_tertiary']))
end,
},
}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _item_table(args)
--[[
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,
}
]]
m_item_util = m_item_util or require('Module:Item util')
local t = os.clock()
args.mode = args.mode or 'item'
local modes = {
skill = {
data = data_map.skill_gem,
header = i18n.item_table.skill_gem,
},
item = {
data = data_map.generic_item,
header = i18n.item_table.item,
},
}
if modes[args.mode] == nil then
error(i18n.errors.invalid_item_table_mode)
end
-- Handle deprecated "q_" args
local query_params = {
'tables',
'join',
'where',
'groupBy',
'having',
'orderBy',
'limit',
'offset',
}
for _, v in ipairs(query_params) do
args[v] = args[v] or args['q_' .. v]
end
-- A where clause is required; there are far too many items to list in one table
if args.where == nil then
error(string.format(i18n.errors.generic_required_parameter, 'where'))
end
local results2 = {
stats = {},
skill_levels = {},
pageIDs = {},
}
local row_infos = {}
for _, row_info in ipairs(modes[args.mode].data) do
local enabled = false
if type(row_info.args) == 'table' then
for _, a in ipairs(row_info.args) do
if m_util.cast.boolean(args[a]) then
enabled = true
break
end
end
else
enabled = true
end
if enabled then
row_info.field_options = row_info.field_options or {}
row_infos[#row_infos+1] = row_info
end
end
-- Parse stat arguments
local stat_columns = {}
local query_stats = {}
for i=1, math.huge do -- repeat until no more columns are found
local prefix = string.format('stat_column%s_', i)
if args[prefix .. 'stat1_id'] == nil then
-- Each column requires at least one stat id
break
end
local col_info = {
header = args[prefix .. 'header'] or tostring(i),
format = args[prefix .. 'format'],
stat_format = args[prefix .. 'stat_format'] or 'separate',
order = tonumber(args[prefix .. 'order']) or (10000000 + i),
stats = {},
}
for j=1, math.huge do
local stat_id = args[string.format('%sstat%s_id', prefix, j)]
if stat_id == nil then
break
end
table.insert(col_info.stats, {id=stat_id})
query_stats[stat_id] = true
end
table.insert(stat_columns, col_info)
end
for _, col_info in ipairs(stat_columns) do
local row_info = {
header = col_info.header,
fields = {},
display = function (tr, data)
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(args, stat, {color=false})
vmax = vmax + stat.max
end
end
if num_stats ~= #stat_texts then
tr:node(m_util.html.table_cell('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(args, 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)
-- Build Cargo query
local tables = {'items'}
local fields = {
'items._pageID',
'items._pageName',
'items.name',
'items.inventory_icon',
'items.html',
'items.size_x',
'items.size_y',
}
local query = {
where = args.where,
groupBy = table.concat({'items._pageID', args.groupBy}, ', '),
having = args.having,
orderBy = args.orderBy,
limit = args.limit,
offset = args.offset,
}
-- Namespace condition
-- This is mainly to prevent items from user pages or other testing pages
-- from being returned in the query results.
if args.namespaces ~= 'any' then
local namespaces = m_util.cast.table(args.namespaces, {callback=m_util.cast.number})
if #namespaces > 0 then
namespaces = table.concat(namespaces, ',')
else
namespaces = m_item_util.get_item_namespaces{format = 'list'}
end
query.where = string.format('(%s) AND items._pageNamespace IN (%s)', query.where, namespaces)
end
-- Minimum required tables and fields, based on display options
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.field_options[index] = rowinfo.field_options[index] or {}
if rowinfo.field_options[index].skill_levels then
skill_levels[#skill_levels+1] = field
else
fields[#fields+1] = field
tables[#tables+1] = m_util.string.split(field, '.', true)[1]
end
end
end
if #skill_levels > 0 then
fields[#fields+1] = 'skill.max_level'
tables[#tables+1] = 'skill'
end
tables = m_util.table.remove_duplicates(tables)
-- Minimum required joins, based on display options
local joins = {}
for _, table_name in ipairs(tables) do
if table_name ~= 'items' then
joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name)
end
end
-- Append additional tables
args.tables = m_util.cast.table(args.tables)
if type(args.tables) == 'table' and #args.tables > 0 then
tables = m_util.table.merge(tables, args.tables)
end
-- Make join clause
if #joins > 0 or args.join then
-- m_util.table.merge rebuilds the table, which removes empty values
query.join = table.concat(m_util.table.merge(joins, {args.join}), ', ')
end
-- Query results
local results = m_cargo.query(tables, fields, query)
if #results == 0 and args.default ~= nil then
return 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 stat_results = m_cargo.query(
{'items', 'item_stats'},
{'item_stats._pageName', 'item_stats.id', 'item_stats.min', 'item_stats.max', 'item_stats.avg'},
{
join = 'items._pageID=item_stats._pageID',
where = string.format(
'item_stats._pageID IN (%s) AND item_stats.id IN ("%s")',
table.concat(m_util.table.column(results, 'items._pageID'), ','),
table.concat(m_util.table.keys(query_stats), '","')
),
groupBy = 'items._pageID, item_stats.id',
}
)
for _, row in ipairs(stat_results) 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')
if m_util.cast.boolean(args.responsive) then
tbl:addClass('responsive-table')
end
-- Headers:
local tr = tbl:tag('tr')
tr
:tag('th')
:wikitext(modes[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 args.no_html == nil then
il_args.html = row['items.html']
end
if args.large then
il_args.large = 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 options = rowinfo.field_options[index]
if options.optional ~= true and options.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:node(m_util.html.table_cell('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, {tables=tables, fields=fields, query=query}})
return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist=args.debug})
end
-------------------------------------------------------------------------------
-- Map item drops
-------------------------------------------------------------------------------
local function _map_item_drops(args)
--[[
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)'}
]]
local tables = {'maps'}
local fields = {
'maps.area_id',
}
local query = {
-- Only need each page name once
groupBy = 'maps._pageName',
}
if args.page then
-- Join with _pageData in order to check for page redirect
tables[#tables+1] = '_pageData'
fields[#fields+1] = '_pageData._pageNameOrRedirect'
query.where = string.format(
'_pageData._pageName="%s"',
args.page
)
query.join = 'maps._pageName = _pageData._pageNameOrRedirect'
else
query.where = string.format(
'maps._pageName="%s"',
tostring(mw.title.getCurrentTitle())
)
end
query.where = query.where .. ' AND maps.area_id > ""' -- area_id must not be empty or null
local results = m_cargo.query(tables, fields, query)
local id = ''
if #results > 0 then
id = results[1]['maps.area_id']
end
return mw.getCurrentFrame():expandTemplate{ title = 'Area item drops', args = {area_id=id} }
end
-------------------------------------------------------------------------------
-- Prophecy description
-------------------------------------------------------------------------------
local function _prophecy_description(args)
args.page = args.page or tostring(mw.title.getCurrentTitle())
local results = m_cargo.query(
{'prophecies'},
{'prophecies.objective', 'prophecies.reward'},
{
where=string.format('prophecies._pageName="%s"', 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
local function _simple_item_list(args)
--[[
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,
}
]]
local query = {}
for key, value in pairs(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 args.no_icon == nil then
fields[#fields+1] = 'items.inventory_icon'
end
if args.no_html == nil then
fields[#fields+1] = 'items.html'
end
local tables = m_util.cast.table(args.q_tables)
table.insert(tables, 1, '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 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 args.format == nil then
out[#out+1] = string.format('* %s', link)
elseif args.format == 'none' then
out[#out+1] = link
elseif args.format == 'li' then
out[#out+1] = string.format('<li>%s</li>', link)
else
error(string.format(i18n.errors.generic_argument_parameter, 'format', args.format))
end
end
if args.format == nil then
return table.concat(out, '\n')
elseif args.format == 'none' then
return table.concat(out, '\n')
elseif args.format == 'li' then
return table.concat(out)
end
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template:Item table
--
p.item_table = m_util.misc.invoker_factory(_item_table)
--
-- Template:Map item drops
--
p.map_item_drops = m_util.misc.invoker_factory(_map_item_drops, {
wrappers = cfg.wrappers.map_item_drops,
})
--
-- Template:Prophecy description
--
p.prophecy_description = m_util.misc.invoker_factory(_prophecy_description, {
wrappers = cfg.wrappers.prophecy_description,
})
--
-- Template:Simple item list
--
p.simple_item_list = m_util.misc.invoker_factory(_simple_item_list, {
wrappers = cfg.wrappers.simple_item_list,
})
-- ----------------------------------------------------------------------------
-- Debug
-- ----------------------------------------------------------------------------
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)
end
return p