Module:Item table: Difference between revisions
		
		
		
		Jump to navigation
		Jump to search
		
| >Illviljan m (Protected "Module:Item table": High traffic page ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite))) | Elderofwoe (talk | contribs)   (Undo revision 58709 by Elderofwoe (talk)) Tag: Undo | ||
| (59 intermediate revisions by 10 users not shown) | |||
| Line 1: | Line 1: | ||
| -- Item table | ------------------------------------------------------------------------------- | ||
| -- | --  | ||
| --  | --                             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') | ||
| --  | |||
| --  | -- Should we use the sandbox version of our submodules? | ||
| --  | local use_sandbox = m_util.misc.maybe_sandbox('Item table') | ||
| local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo') | |||
| 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 | |||
| local f_item_link  | |||
| local f_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  | local i18n = cfg.i18n | ||
| -- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
| --  | -- Helper functions | ||
| -- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
| local  | 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 = use_sandbox and require('Module:Item link/sandbox').item_link or require('Module:Item link').item_link | |||
|      end | |||
|     return f_item_link(args) | |||
| end | |||
| function h. | -- Lazy loading for Module:Skill link | ||
|      if  | function h.skill_link(args) | ||
|      if not f_skill_link then | |||
|          f_skill_link = use_sandbox and require('Module:Skill link/sandbox').skill_link or require('Module:Skill link').skill_link | |||
|      end |      end | ||
|     return f_skill_link(args) | |||
| end | end | ||
| function h.range_fields_factory(args) | |||
|     -- Returns a function that gets the range fields for the given keys | |||
| function h. | |||
|      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, suffix) | |||
|                  fields[#fields+1] = string.format('%s_range_%s', field,  | |||
|              end |              end | ||
|          end |          end | ||
|          return fields |          return fields | ||
| Line 229: | 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 = {} | |||
| function h. | |||
|      args. | |||
|      return function(tr, data, fields, data2) | |||
|          local values = {} | |||
|          local fmt_values = {} | |||
|          for index, field in ipairs(fields) do |          for index, field in ipairs(fields) do | ||
|              local value = { |              local value = { | ||
| Line 267: | Line 111: | ||
|                  base=data[field], |                  base=data[field], | ||
|              } |              } | ||
|              if sdata then |             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] |                  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] |                  value.max = value.max or sdata['0'][field] or sdata[data['skill.max_level']][field] | ||
|              end |              end | ||
|              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 295: | Line 142: | ||
| end | end | ||
| function h. | function h.display_range_factory(args) | ||
|      -- args: table |      -- args: table | ||
|      --  property |      --  property | ||
| Line 308: | 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 359: | 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. |              value = m_util.html.tooltip(value, args.tbl[args.key]) | ||
|          end |          end | ||
|          return value |          return value | ||
| Line 373: | 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 399: | 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) | ||
|     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 | -- 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 418: | 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.tooltip( | |||
|             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.tooltip( | |||
|             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.tooltip( | |||
|             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.tooltip( | ||
|          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_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, |          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'}, | |||
|          header = i18n.item_table.damage, | |||
|          order =  | |||
|          header = i18n.item_table.damage, | |||
|          fields = {'weapons.damage_html', 'weapons.damage_avg'}, |          fields = {'weapons.damage_html', 'weapons.damage_avg'}, | ||
|          display = function (tr, data) |          display = function (tr, data) | ||
| Line 542: | 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'}, | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 1611, | |||
|         args = {'reload_time'}, | |||
|         header = i18n.item_table.reload_time, | |||
|         fields = h.range_fields_factory{fields={'weapons.reload_time'}}, | |||
|         display = h.display_range_factory{field='weapons.reload_time'}, | |||
|     }, | |||
|     -- 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 = {'jewels. |          header = i18n.item_table.jewel_limit, | ||
|          display = h. |          fields = {'jewels.jewel_limit'}, | ||
|          display = h.display_value_factory{}, | |||
|      }, |      }, | ||
|      { |      { | ||
|          order = 1801, | |||
|         args = {'jewel_radius'}, | |||
|          header = i18n.item_table.jewel_radius, |          header = i18n.item_table.jewel_radius, | ||
|          fields = {'jewels.radius_html'}, |          fields = {'jewels.radius_html'}, | ||
| Line 693: | Line 633: | ||
|                      :wikitext(data['jewels.radius_html']) |                      :wikitext(data['jewels.radius_html']) | ||
|          end, |          end, | ||
|      }, |      }, | ||
|     -- Maps 19xx | |||
|      { |      { | ||
|          order = 1900, | |||
|         args = {'map_tier'}, | |||
|          header = i18n.item_table.map_tier, |          header = i18n.item_table.map_tier, | ||
|          fields = {'maps.tier'}, |          fields = {'maps.tier'}, | ||
|          display = h. |          display = h.display_value_factory{}, | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 1901, | |||
|         args = {'map_level'}, | |||
|          header = i18n.item_table.map_level, |          header = i18n.item_table.map_level, | ||
|          fields = {'maps.area_level'}, |          fields = {'maps.area_level'}, | ||
|          display = h. |          display = h.display_value_factory{}, | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 1902, | |||
|         args = {'map_guild_character'}, | |||
|          header = i18n.item_table.map_guild_character, |          header = i18n.item_table.map_guild_character, | ||
|          fields = {'maps.guild_character'}, |          fields = {'maps.guild_character'}, | ||
|          display = h. |          display = h.display_value_factory{}, | ||
|          sort_type = 'text', |          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, |          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, |          colspan = 5, | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 1905, | |||
|         args = {'atlas_level'}, | |||
|          header = i18n.item_table.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, |          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 = {'hideout_doodads. |          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, | ||
|          display =  |          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, | |||
|          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', | |||
|      }, |      }, | ||
|     -- Seed data 91xx, | |||
|      { |      { | ||
|          order = 9100, | |||
|          header = i18n.item_table. |         args = {'seed', 'seed_type'}, | ||
|          fields = {' |          header = i18n.item_table.seed_type, | ||
|          display =  |          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'}, | ||
|          fields = {' |          header = i18n.item_table.seed_tier, | ||
|          display = h. |          fields = {'harvest_seeds.tier'}, | ||
|          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, | |||
|          header = i18n.item_table. |         args = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_amount'}, | ||
|          fields = {' |          header = i18n.item_table.seed_required_nearby_seed_amount, | ||
|          display = h. |          fields = {'harvest_seeds.required_nearby_seed_amount'}, | ||
|          display = h.display_value_factory{}, | |||
|      }, |      }, | ||
|     { | |||
|          order = 9114, | |||
|          header = i18n.item_table. |          args = {'seed', 'seed_required_nearby_seeds', 'seed_required_nearby_seed_tier'}, | ||
|          fields = {' |          header = i18n.item_table.seed_required_nearby_seed_tier, | ||
|          display = h. |          fields = {'harvest_seeds.required_nearby_seed_tier'}, | ||
|          display = h.display_value_factory{}, | |||
|      }, |      }, | ||
|      { |      { | ||
|          order = 12000, | |||
|          header = i18n.item_table. |         args = {'buff'}, | ||
|          fields = {' |          header = i18n.item_table.buff_effects, | ||
|          display = h. |          fields = {'item_buffs.stat_text'}, | ||
|          display = h.display_value_factory{ | |||
|             color = 'mod', | |||
|         }, | |||
|          sort_type = 'text', | |||
|      }, |      }, | ||
|      { |      { | ||
|          order = 12001, | |||
|          header = i18n.item_table. |         args = {'stat'}, | ||
|          fields = {' |          header = i18n.item_table.stats, | ||
|          display = h. |          fields = {'items.stat_text'}, | ||
|          display = h.display_value_factory{ | |||
|             color = 'mod', | |||
|          }, | |||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 12002, | |||
|          header = i18n.item_table. |         args = {'inherent_skills'}, | ||
|          fields = {' |          header = i18n.item_table.inherent_skills, | ||
|          display = h. |          fields = {'inherent_skills.inherent_skills_text'}, | ||
|          display = h.display_value_factory{ | |||
|              color = 'mod', | |||
|          }, | |||
|          } | |||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 12003, | |||
|          header = i18n.item_table. |         args = {'description'}, | ||
|          fields = {'items. |          header = i18n.item_table.description, | ||
|          display =  |          fields = {'items.description'}, | ||
|          display = h.display_value_factory{ | |||
|             color = 'mod', | |||
|         }, | |||
|         sort_type = 'text', | |||
|     }, | |||
|     { | |||
|         order = 12004, | |||
|         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, | |||
|          header = i18n.item_table. |         args = {'flavour_text'}, | ||
|          fields = {'items. |          header = i18n.item_table.flavour_text, | ||
|          display =  |          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'}, | |||
|          order =  |          display = h.display_value_factory{ | ||
|              color = 'help', | |||
|          }, | |||
|          header = i18n.item_table. | |||
|          fields = {'items. | |||
|          display =  | |||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 12300, | |||
|          header = i18n.item_table. |         args = {'buff_icon'}, | ||
|          fields = {' |          header = i18n.item_table.buff_icon, | ||
|          display = h. |          fields = {'item_buffs.icon'}, | ||
|          display = h.display_value_factory{ | |||
|             fmt_options = { | |||
|                 [1] = { | |||
|                     fmt = '[[%s]]', | |||
|                 }, | |||
|             }, | |||
|          }, | |||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 13000, | |||
|          header = i18n.item_table. |         args = {'prophecy', 'objective'}, | ||
|          fields = {' |          header = i18n.item_table.objective, | ||
|          display =  |          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, |          end, | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 15000, | |||
|          header = i18n.item_table. |         args = {'drop', 'drop_level'}, | ||
|          fields = {'items. |          header = i18n.item_table.drop_level, | ||
|          display = h. |          fields = {'items.drop_level'}, | ||
|          order =  |          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, | |||
|          header = i18n.item_table. |         args = {'version', 'removal_version'}, | ||
|          fields = {'items. |          header = i18n.item_table.removal_version, | ||
|          display = function(tr, data,  |          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'}, | |||
|                      { |                      { | ||
|                          join= |                         'items._pageName', | ||
|                          where=string.format( |                         '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, ', ') |                              table.concat(results2.pageIDs, ', ') | ||
|                          ), |                          ), | ||
|                          orderBy=' |                          orderBy='items.drop_monsters', | ||
|                      } |                      } | ||
|                  ) |                  ) | ||
|                  results2[' | |||
|                      results=results2[' |                  results2['drop_monsters_query'] = m_cargo.map_results_to_id{ | ||
|                      field=' |                      results=results2['drop_monsters_query'], | ||
|                      field='items._pageName', | |||
|                  } |                  } | ||
|              end |              end | ||
|              local results = results2['drop_monsters_query'][data['items._pageName']] or {} | |||
|              local results = results2[' | |||
|              local tbl = {} |              local tbl = {} | ||
|              for _, v in ipairs(results) do |              for _,v in ipairs(results) do | ||
|                  local  |                  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) | |||
|                  tbl[#tbl+1] = string.format( | |||
|              end |              end | ||
|             h.na_or_val(tr, table.concat(tbl, '<br>')) | |||
|          end, |          end, | ||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 15006, | |||
|          header = i18n.item_table. |         args = {'drop', 'drop_text'}, | ||
|          fields = {'items._pageName'}, |         header = i18n.item_table.drop_text, | ||
|          display = function(tr, data, na, results2) |         fields = {'items.drop_text'}, | ||
|              if results2[' |         display = h.display_value_factory{}, | ||
|                  results2[' |         sort_type = 'text', | ||
|                      {'items', ' |     }, | ||
|     { | |||
|         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= |                          join='items._pageName=quest_rewards._pageName', | ||
|                          where=string.format( |                          where=string.format( | ||
|                              'items._pageID IN (%s) AND  |                              'items._pageID IN (%s) AND quest_rewards._pageName IS NOT NULL', | ||
|                              table.concat(results2.pageIDs, ', ') |                              table.concat(results2.pageIDs, ', ') | ||
|                          ), |                          ), | ||
|                          orderBy=' |                          orderBy='quest_rewards.act, quest_rewards.quest', | ||
|                      } |                      } | ||
|                  ) |                  ) | ||
|                  results2[' |                  results2['quest_query'] = m_cargo.map_results_to_id{ | ||
|                      results=results2[' |                      results=results2['quest_query'], | ||
|                      field=' |                      field='quest_rewards._pageName', | ||
|                  } |                  } | ||
|              end |              end | ||
|             local results = results2['quest_query'][data['items._pageName']] or {} | |||
|              local tbl = {} |              local tbl = {} | ||
|              for _, v in ipairs(results) do |              for _, v in ipairs(results) do | ||
|                  local classes = table.concat(m_util.string.split(v[' |                  local classes = table.concat(m_util.string.split(v['quest_rewards.classes'] or '', ',%s*'), ', ') | ||
|                  if classes == '' or classes == nil then |                  if classes == '' or classes == nil then | ||
|                      classes = i18n.item_table. |                      classes = i18n.item_table.quest_rewards_any_classes | ||
|                  end |                  end | ||
|                  tbl[#tbl+1] = string.format( |                  tbl[#tbl+1] = string.format( | ||
|                      i18n.item_table. |                      i18n.item_table.quest_rewards_row_format, | ||
|                      v[' |                      v['quest_rewards.act'], | ||
|                      v[' |                      v['quest_rewards.quest'], | ||
|                      classes |                      classes | ||
|                  ) |                  ) | ||
|              end |              end | ||
|              value = table.concat(tbl, '<br>') |              local value = table.concat(tbl, '<br>') | ||
|              if value == nil or value == '' then |              if value == nil or value == '' then | ||
|                  tr: |                  tr:node(m_util.html.table_cell('na')) | ||
|              else |              else | ||
|                  tr |                  tr | ||
| Line 1,085: | Line 1,084: | ||
|              end |              end | ||
|          end, |          end, | ||
|          sort_type = 'text', |          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', | |||
|          order =  | |||
|          header = i18n.item_table. | |||
|          fields = {' | |||
|          display = function(tr, data, na, results2) | |||
|              if results2[' | |||
|                  results2[' | |||
|                      {'items', ' | |||
|                      { | |||
|                          ' | |||
|                          ' | |||
|                          ' | |||
|                      }, |                      }, | ||
|                      { |                      { | ||
|                          join='items. |                          join='items._pageName=vendor_rewards._pageName', | ||
|                          where=string.format( |                          where=string.format( | ||
|                              'items._pageID IN (%s) AND  |                              'items._pageID IN (%s) AND vendor_rewards._pageName IS NOT NULL', | ||
|                              table.concat(results2.pageIDs, ', ') |                              table.concat(results2.pageIDs, ', ') | ||
|                          ), |                          ), | ||
|                          orderBy=' |                          orderBy='vendor_rewards.act, vendor_rewards.quest', | ||
|                      } |                      } | ||
|                  ) |                  ) | ||
|                  results2[' |                  results2['vendor_query'] = m_cargo.map_results_to_id{ | ||
|                      results=results2[' |                      results=results2['vendor_query'], | ||
|                      field=' |                      field='vendor_rewards._pageName', | ||
|                  } |                  } | ||
|              end |              end | ||
|              local results = results2[' |              local results = results2['vendor_query'][data['items._pageName']] or {} | ||
|              local tbl = {} |              local tbl = {} | ||
|              for _,v in ipairs(results) do |              for _, v in ipairs(results) do | ||
|                 local classes = table.concat(m_util.string.split(v['vendor_rewards.classes'] or '', ',%s*'), ', ') | |||
|                 if classes == '' or classes == nil then | |||
|                     classes = i18n.item_table.vendor_rewards_any_classes | |||
|                 end | |||
|                  tbl[#tbl+1] = string.format( |                  tbl[#tbl+1] = string.format( | ||
|                      ' |                      i18n.item_table.vendor_rewards_row_format, | ||
|                      v[' |                     v['vendor_rewards.act'], | ||
|                      v['vendor_rewards.quest'], | |||
|                      v['vendor_rewards.npc'], | |||
|                     classes | |||
|                  ) |                  ) | ||
|              end |              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, |          end, | ||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 18000, | |||
|          header = i18n.item_table. |         args = {'price', 'purchase_cost'}, | ||
|          fields = {' |         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) |          display = function(tr, data, na, results2) | ||
|              if results2[' |              if results2['sell_price_query'] == nil then | ||
|                  results2[' |                  results2['sell_price_query'] = m_cargo.query( | ||
|                      {'items', ' |                      {'items', 'item_sell_prices'}, | ||
|                      { |                      { | ||
|                          ' |                          'item_sell_prices.name', | ||
|                          'item_sell_prices.amount', | |||
|                          ' |                          'item_sell_prices._pageID' | ||
|                          ' | |||
|                      }, |                      }, | ||
|                      { |                      { | ||
|                          join= |                          join='items._pageID=item_sell_prices._pageID', | ||
|                          where=string.format( | |||
|                              'items._pageID IN (%s) AND item_sell_prices._pageID IS NOT NULL', | |||
|                          where=string.format( | |||
|                              items._pageID IN (%s) | |||
|                              table.concat(results2.pageIDs, ', ') |                              table.concat(results2.pageIDs, ', ') | ||
|                          ), |                          ), | ||
|                          orderBy=' |                          orderBy='item_sell_prices.name', | ||
|                      } |                      } | ||
|                  ) |                  ) | ||
|                  results2['sell_price_query'] = m_cargo.map_results_to_id{ | |||
|                  results2[' |                      results=results2['sell_price_query'], | ||
|                      results=results2[' |                      field='item_sell_prices._pageID', | ||
|                      field=' | |||
|                  } |                  } | ||
|              end |              end | ||
|              local results = results2[' |              local results = results2['sell_price_query'][data['items._pageID']] or {} | ||
|              local tbl = {} |              local tbl = {} | ||
|              for _,v in ipairs(results) do |              for _,v in ipairs(results) do | ||
|                  tbl[#tbl+1] = string.format( | |||
|                     '%sx %s', | |||
|                     v['item_sell_prices.amount'], | |||
|                     h.item_link{v['item_sell_prices.name']} | |||
|                  ) | |||
|              end |              end | ||
|              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, | |||
|          header = i18n.item_table. |         args = {'boss', 'boss_name'}, | ||
|          header = i18n.item_table.boss_name, | |||
|          fields = {'maps.area_id'}, |          fields = {'maps.area_id'}, | ||
|          display = function(tr, data, na, results2) |          display = function(tr, data, na, results2) | ||
| Line 1,245: | Line 1,252: | ||
|              local tbl = {} |              local tbl = {} | ||
|              for _,v in ipairs(results) do |              for _,v in ipairs(results) do | ||
|                  tbl[#tbl+1] =  |                 local page = v['main_pages._pageName'] or v['monsters._pageName'] or '' | ||
|                 local name = v['monsters.name'] or v['areas.boss_monster_ids'] or '' | |||
|                  tbl[#tbl+1] = string.format('[[%s|%s]]', page, name) | |||
|              end |              end | ||
|              h.na_or_val(tr,  |              h.na_or_val(tr, table.concat(tbl, '<br>')) | ||
|          end, |          end, | ||
|          sort_type = 'text', | |||
|      }, |      }, | ||
|      { |      { | ||
|          order = 19001, | |||
|          header = i18n.item_table. |         args = {'boss', 'boss_number'}, | ||
|          fields = { |          header = i18n.item_table.boss_number, | ||
|          fields = {'maps.area_id'}, | |||
|          display = function(tr, data, na, results2) |          display = function(tr, data, na, results2) | ||
|              if results2[' |              if results2['boss_query'] == nil then | ||
|                  results2[' |                  results2['boss_query'] = m_cargo.query( | ||
|                      {'items', 'maps', ' |                      {'items', 'maps', 'areas', 'monsters', 'main_pages'}, | ||
|                      { |                      { | ||
|                          'items._pageName', |                          'items._pageName', | ||
|                          ' |                          'maps.area_id', | ||
|                          ' |                         'areas.id', | ||
|                          ' |                          'areas.boss_monster_ids', | ||
|                          ' |                          'monsters._pageName', | ||
|                          ' |                          'monsters.name', | ||
|                          'main_pages._pageName', | |||
|                      }, |                      }, | ||
|                      { |                      { | ||
|                          join= |                          join=[[ | ||
|                          where= |                             items._pageID=maps._pageID, | ||
|                          orderBy=' |                             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 |              end | ||
|             local results = results2['boss_query'][data['items._pageName']] or {} | |||
|              local tbl = {} |              local tbl = {} | ||
|              for _, v in ipairs(results) do |              for _,v in ipairs(results) do | ||
|                  tbl[#tbl+1] = v['areas.boss_monster_ids'] | |||
|              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,381: | Line 1,396: | ||
|                      :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,439: | Line 1,454: | ||
|                  local level_number = string.match( |                  local level_number = string.match( | ||
|                      stat_text:lower(), |                      stat_text:lower(), | ||
|                      h.string.format( | |||
|                          i18n.item_table.granted_skills_level_pattern, |                          i18n.item_table.granted_skills_level_pattern, | ||
|                          { |                          { | ||
| Line 1,450: | Line 1,465: | ||
|                  -- then add it to the cell: |                  -- then add it to the cell: | ||
|                  if level_number then |                  if level_number then | ||
|                      level =  |                      level = h.string.format( | ||
|                          i18n.item_table.granted_skills_level_format, |                          i18n.item_table.granted_skills_level_format, | ||
|                          { |                          { | ||
| Line 1,462: | Line 1,477: | ||
|                  -- not: |                  -- not: | ||
|                  if v['items2.class'] == nil  then |                  if v['items2.class'] == nil  then | ||
|                      tbl[#tbl+1] =  |                      tbl[#tbl+1] = h.string.format( | ||
|                          i18n.item_table.granted_skills_skill_output_format, |                          i18n.item_table.granted_skills_skill_output_format, | ||
|                          { |                          { | ||
|                              level = level, |                              level = level, | ||
|                              sl =  |                              sl = h.skill_link{ | ||
|                                  skip_query=true, |                                  skip_query=true, | ||
|                                  page = v['skill.active_skill_name'] |                                  page = v['skill.active_skill_name'] | ||
| Line 1,493: | Line 1,508: | ||
|                          il_args.html = v['items2.html'] |                          il_args.html = v['items2.html'] | ||
|                      end |                      end | ||
|                      tbl[#tbl+1] =  |                      tbl[#tbl+1] = h.string.format( | ||
|                          i18n.item_table.granted_skills_gem_output_format, |                          i18n.item_table.granted_skills_gem_output_format, | ||
|                          { |                          { | ||
|                              level = level, |                              level = level, | ||
|                              il =  |                              il = h.item_link(il_args), | ||
|                          } |                          } | ||
|                      ) |                      ) | ||
| Line 1,504: | Line 1,519: | ||
|              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,532: | Line 1,547: | ||
|                      :wikitext(table.concat(out, '')) |                      :wikitext(table.concat(out, '')) | ||
|          end, |          end, | ||
|          sort_type = 'text', |          sort_type = 'text', | ||
|      }, |      }, | ||
| } | } | ||
| data_map. | data_map.skill_gem = { | ||
|      { |      { | ||
|          order = 1000, |          order = 1000, | ||
|          args = {'gem_tier'}, | |||
|         header = i18n.item_table.tier, | |||
|         fields = {'skill_gems.gem_tier'}, | |||
|         display = h.display_value_factory{}, | |||
|      }, |      }, | ||
|      { |      { | ||
|          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.tooltip( | ||
|          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. |         args = {'str'}, | ||
|          fields = {' |         header = m_util.html.tooltip( | ||
|          display = h. |             string.format( | ||
|              [1] = { |                 '[[%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.tooltip( | |||
|             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.tooltip( | |||
|             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] = { |              [1] = { | ||
|                  skill_levels = true, |                  skill_levels = true, | ||
| Line 1,607: | Line 1,684: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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,634: | Line 1,709: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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,651: | Line 1,727: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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,668: | Line 1,745: | ||
|      }, |      }, | ||
|      { |      { | ||
|          order = 5001, | |||
|         args = {'mana'}, | |||
|          header = i18n.item_table.mana_cost, |          header = i18n.item_table.mana_cost, | ||
|          fields = {'skill_levels. |          fields = {'skill_levels.cost_amounts', 'skill_levels.mana_reservation_percent', 'skill_levels.mana_reservation_flat'}, | ||
|          display = function (tr, data, fields, data2) |          display = function (tr, data, fields, data2) | ||
|              local appendix = '' |              local appendix = '' | ||
|              if  |              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 .. '%%' |                  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 |                  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 |              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. | |||
|          field_options = { | |||
|              [1] = { |              [1] = { | ||
|                 skill_levels = true, | |||
|             }, | |||
|             [2] = { | |||
|                 skill_levels = true, | |||
|             }, | |||
|             [3] = { | |||
|                  skill_levels = true, |                  skill_levels = true, | ||
|              }, |              }, | ||
| Line 1,695: | Line 1,787: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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,711: | Line 1,799: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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,727: | Line 1,811: | ||
|      }, |      }, | ||
|      { |      { | ||
|          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, | ||
|          order =  |     }, | ||
|     { | |||
|          order = 8000, | |||
|         args = {'drop', 'drop_text'}, | |||
|         header = i18n.item_table.drop_text, | |||
|         fields = {'items.drop_text'}, | |||
|         display = h.display_value_factory{}, | |||
|         sort_type = 'text', | |||
|      }, |      }, | ||
| } | } | ||
| -- ---------------------------------------------------------------------------- | |||
| -- Main functions | |||
| -- ---------------------------------------------------------------------------- | |||
| local function _item_table(args) | |||
| -- ---------------------------------------------------------------------------- | |||
| --  | |||
| -- ---------------------------------------------------------------------------- | |||
| local  | |||
| function  | |||
|      --[[ |      --[[ | ||
|      Creates a generic table for items. |      Creates a generic table for items. | ||
| Line 1,808: | Line 1,882: | ||
|      -------- |      -------- | ||
|      = p.item_table{ |      = p.item_table{ | ||
|          tables='vendor_rewards, quest_rewards, skill_gems', | |||
|          join='items._pageID=vendor_rewards._pageID, items._pageID=quest_rewards._pageID, items._pageID=skill_gems._pageID', | |||
|          where='(items.class="Active Skill Gems" OR items.class = "Support Skill Gems") AND (vendor_rewards.quest_id IS NOT NULL OR quest_rewards.quest_id IS NOT NULL)', | |||
|          vendor=1, |          vendor=1, | ||
|      } |      } | ||
|     ]] | |||
|      m_item_util = m_item_util or (use_sandbox and require('Module:Item util/sandbox') or require('Module:Item util')) | |||
|      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,835: | Line 1,904: | ||
|          }, |          }, | ||
|      } |      } | ||
|      if modes[args.mode] == nil then | |||
|      if  |          error(i18n.errors.invalid_item_table_mode) | ||
|      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,851: | Line 1,920: | ||
|      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,875: | Line 1,941: | ||
|      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 | |||
|              end | |||
|              local  |             table.insert(col_info.stats, {id=stat_id}) | ||
|              query_stats[stat_id] = true | |||
|              if  | |||
|              end | |||
|          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,931: | Line 1,979: | ||
|                          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,937: | Line 1,985: | ||
|                      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,973: | Line 2,021: | ||
|                          :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,988: | Line 2,036: | ||
|      end) |      end) | ||
|      --  |      -- Build Cargo query | ||
|      local  |      local tables = {'items'} | ||
|      local fields = { |      local fields = { | ||
|          'items._pageID', |          'items._pageID', | ||
| Line 1,999: | Line 2,047: | ||
|          '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 2,023: | Line 2,076: | ||
|          end |          end | ||
|          for index, field in ipairs(rowinfo.fields) do |          for index, field in ipairs(rowinfo.fields) do | ||
|              rowinfo. |              rowinfo.field_options[index] = rowinfo.field_options[index] or {} | ||
|              if rowinfo. |              if rowinfo.field_options[index].skill_levels then | ||
|                  skill_levels[#skill_levels+1] = field |                  skill_levels[#skill_levels+1] = field | ||
|              else |              else | ||
|                  fields[#fields+1] = field |                  fields[#fields+1] = field | ||
|                  tables[#tables+1] = m_util.string.split(field, '.', true)[1] | |||
|              end |              end | ||
|          end |          end | ||
|      end |      end | ||
|      if #skill_levels > 0 then |      if #skill_levels > 0 then | ||
|          fields[#fields+1] = 'skill.max_level' |          fields[#fields+1] = 'skill.max_level' | ||
|          tables[#tables+1] = 'skill' | |||
|          tables[#tables+1] =  | |||
|      end |      end | ||
|      tables = m_util.table.remove_duplicates(tables) | |||
|      --  |      -- Minimum required joins, based on display options | ||
|      local joins = {} |      local joins = {} | ||
|      for  |      for _, table_name in ipairs(tables) do | ||
|          if table_name ~= 'items' then |          if table_name ~= 'items' then | ||
|              joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name) |              joins[#joins+1] = string.format('items._pageID=%s._pageID', table_name) | ||
|          end |          end | ||
|      end |      end | ||
|      -- Append additional tables | |||
|     args.tables = m_util.cast.table(args.tables) | |||
|     if type(args.tables) == 'table' and #args.tables > 0 then | |||
|          tables = m_util.table.merge(tables, args.tables) | |||
|      end |      end | ||
|      --  |      -- Make join clause | ||
|     if #joins > 0 or args.join then | |||
|         -- m_util.table.merge rebuilds the table, which removes empty values | |||
|         query.join = table.concat(m_util.table.merge(joins, {args.join}), ', ') | |||
|     end | |||
|      -- Query results |      -- Query results | ||
|      local results = m_cargo.query( |      local results = m_cargo.query(tables, fields, query) | ||
|      if #results == 0 and  |      if #results == 0 and args.default ~= nil then | ||
|          return  |          return args.default | ||
|      end |      end | ||
| Line 2,111: | Line 2,151: | ||
|          if #stat_columns > 0 then |          if #stat_columns > 0 then | ||
|              local  |              local stat_results = m_cargo.query( | ||
|                  {'items', 'item_stats'}, | |||
|                  {'item_stats._pageName', 'item_stats.id', 'item_stats.min', 'item_stats.max', 'item_stats.avg'}, | |||
|                  {'items', 'item_stats'}, | |||
|                  {'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,153: | Line 2,179: | ||
|          end |          end | ||
|      end |      end | ||
|      -- |      -- | ||
| Line 2,161: | Line 2,186: | ||
|      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(args.responsive) then | |||
|         tbl:addClass('responsive-table') | |||
|     end | |||
|      -- Headers: |      -- Headers: | ||
| Line 2,166: | Line 2,194: | ||
|      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,193: | Line 2,221: | ||
|          } |          } | ||
|          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 | ||
|          tr |          tr | ||
|              :tag('td') |              :tag('td') | ||
|                  :wikitext( |                  :wikitext(h.item_link(il_args)) | ||
|                  :done() |                  :done() | ||
| Line 2,209: | Line 2,237: | ||
|              -- this has been cast from a function in an earlier step |              -- this has been cast from a function in an earlier step | ||
|              local display = true |              local display = true | ||
|              for index, field in ipairs(rowinfo.fields) do |              for index, field in ipairs(rowinfo.fields) do | ||
|                  -- this will bet set to an empty value not nil confusingly |                  -- this will bet set to an empty value not nil confusingly | ||
|                  if row[field] == 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,224: | Line 2,253: | ||
|                  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 | ||
|      end |      end | ||
|      cats = {} |      local cats = {} | ||
|      if #results == query.limit then |      if #results == query.limit then | ||
|          cats[#cats+1] = i18n.categories.query_limit |          cats[#cats+1] = i18n.categories.query_limit | ||
|      end |      end | ||
|      if #results == 0 then |      if #results == 0 then | ||
|          cats[#cats+1] = i18n.categories.no_results |          cats[#cats+1] = i18n.categories.no_results | ||
|      end |      end | ||
|      mw.logObject({os.clock() - t, query}) |      mw.logObject({os.clock() - t, {tables=tables, fields=fields, query=query}}) | ||
|      return tostring(tbl) .. m_util.misc.add_category(cats, { |      return tostring(tbl) .. m_util.misc.add_category(cats, {ignore_blacklist=args.debug}) | ||
| end | end | ||
| Line 2,248: | Line 2,276: | ||
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
| 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,257: | Line 2,285: | ||
|      ]] |      ]] | ||
|      local tables = {'maps'} | |||
|      local  |      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' | ||
|      local id = '' |     else | ||
|      if #results > 0 then |          query.where = string.format( | ||
|          id = results[1]['maps.area_id'] |             'maps._pageName="%s"', | ||
|      end |             tostring(mw.title.getCurrentTitle()) | ||
|      return  |         ) | ||
| end |     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 | |||
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
| Line 2,285: | Line 2,321: | ||
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
| 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,298: | Line 2,328: | ||
|          {'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,321: | Line 2,351: | ||
| 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 | |||
|      local  |         if string.sub(key, 0, 2) == 'q_' then | ||
|             query[string.sub(key, 3)] = value | |||
|          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  |      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  | -- Debug | ||
| -- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
| p.debug = {} | p.debug = {} | ||
| Line 2,621: | Line 2,498: | ||
| function p.debug.skill_gem_all() | function p.debug.skill_gem_all() | ||
|      return p.debug._tbl_data(data_map. |      return p.debug._tbl_data(data_map.skill_gem) | ||
| end | end | ||
| return p | return p | ||
Latest revision as of 19:20, 29 September 2025
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')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item table')
local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')
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 = use_sandbox and require('Module:Item link/sandbox').item_link or 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 = use_sandbox and require('Module:Skill link/sandbox').skill_link or 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.tooltip(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.tooltip(
            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.tooltip(
            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.tooltip(
            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.tooltip(
            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'},
    },
    {
        order = 1611,
        args = {'reload_time'},
        header = i18n.item_table.reload_time,
        fields = h.range_fields_factory{fields={'weapons.reload_time'}},
        display = h.display_range_factory{field='weapons.reload_time'},
    },
    -- 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',
    },
    -- 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 = {'inherent_skills'},
        header = i18n.item_table.inherent_skills,
        fields = {'inherent_skills.inherent_skills_text'},
        display = h.display_value_factory{
            color = 'mod',
        },
        sort_type = 'text',
    },
    {
        order = 12003,
        args = {'description'},
        header = i18n.item_table.description,
        fields = {'items.description'},
        display = h.display_value_factory{
            color = 'mod',
        },
        sort_type = 'text',
    },
    {
        order = 12004,
        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 = {'gem_tier'},
        header = i18n.item_table.tier,
        fields = {'skill_gems.gem_tier'},
        display = h.display_value_factory{},
    },
    {
        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.tooltip(
            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.tooltip(
            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.tooltip(
            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.tooltip(
            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,
    },
    {
        order = 8000,
        args = {'drop', 'drop_text'},
        header = i18n.item_table.drop_text,
        fields = {'items.drop_text'},
        display = h.display_value_factory{},
        sort_type = 'text',
    },
}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _item_table(args)
    --[[
    Creates a generic table for items.
    Examples
    --------
    = p.item_table{
        tables='vendor_rewards, quest_rewards, skill_gems',
        join='items._pageID=vendor_rewards._pageID, items._pageID=quest_rewards._pageID, items._pageID=skill_gems._pageID',
        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 (use_sandbox and require('Module:Item util/sandbox') 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
    -- 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

