Module:Item table: Difference between revisions
Jump to navigation
Jump to search
(Cargo's HOLDS seems to work just fine unless you try to give it an empty string, in which case it throws an error. The solution should be to implement proper error handling for empty values.) |
(Minor refactoring in preparation to drop the "q_" from the query arguments. The error message when `where` param is missing is now more informative.) |
||
(24 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,790: | 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,806: | 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,830: | 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,886: | 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,892: | 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,928: | 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,943: | Line 2,042: | ||
end) | end) | ||
-- | -- Build Cargo query | ||
local | local tables = {'items'} | ||
local fields = { | local fields = { | ||
'items._pageID', | 'items._pageID', | ||
Line 1,954: | 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,986: | 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' | |||
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,073: | 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,115: | Line 2,185: | ||
end | end | ||
end | end | ||
-- | -- | ||
Line 2,123: | 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,131: | 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,158: | 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,178: | 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,190: | 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,199: | 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,214: | 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,222: | 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,237: | 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,243: | 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,258: | 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,265: | 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,278: | 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,301: | 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,604: | 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 |
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