Module:Item2
This module is used on 12,000+ pages.
To avoid major disruption and server load, do not make unnecessary edits to this module. Test changes to this module first using its /sandbox and /testcases subpages . All of the changes can then be applied to this module in a single edit.
Consider discussing changes on the talk page or on Discord before implementing them.
The item module provides functionality for various item-related templates.
Overview
This module is responsible for creating item boxes, various item lists, item links and other item-related tasks. In the process a lot of the input data is verified and also added as semantic property to pages; as such, any templates deriving from this module should not be used on user pages other then for temporary testing purposes.
This template is also backed by an export script in PyPoE which can be used to export item data from the game files which then can be used on the wiki. Use the export when possible,
Item templates
Module:Item2
All templates defined in Module:Item2:
Module:Item table
All templates defined in Module:Item table:
- {{Item table}}
- {{Query base items}}
- {{Query unique items}}
- {{Item unique versions}}
- {{Area item drops}}
- {{Skill gem table}}
- {{Map item drops}}
- {{Prophecy description}}
- {{Simple item list}}
Module:Item link
All templates defined in Module:Item link:
- {{Item link}}
- {{Il}}
- {{Item icon link}}
Module:Item acquisition
- {{Item acquisition}}
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-- SMW reworked item module
-- TODO:
-- included mods and base items should override the internal values and then set them as semantic properties so items can be queried for
-- > subobjects/property in display code
-- drop location (+drop difficulty) support
-- divinatation card support
-- ----------------------------------------------------------------------------
-- Imports
-- ----------------------------------------------------------------------------
local xtable = require('Module:Table')
local util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local game = require('Module:Game')
local p = {}
local v = {}
local g_frame, g_args
-- ----------------------------------------------------------------------------
-- Other stuff
-- ----------------------------------------------------------------------------
local h = {}
function h.new_color(label, text)
if text == nil or text == '' then
return nil
end
return tostring(mw.html.create('span')
:attr('class', 'text-' .. label)
:wikitext(text))
end
-- ----------------------------------------------------------------------------
-- core
-- ----------------------------------------------------------------------------
local core = {}
function core.validate_table(args)
-- args: Table containing:
-- arg: value of the argument
-- tbl: table of valid options
-- key: key or table of key of in tbl
-- rtrkey: if key is table, return this key instead of the value instead
if type(args.key) == 'table' then
for _, item in ipairs(args.tbl) do
for _, k in ipairs(args.key) do
if item[k] == args.arg then
if args.rtrkey ~= nil then
return item[args.rtrkey]
else
return k
end
end
end
end
elseif args.key == nil then
for _, item in ipairs(args.tbl) do
if item == args.arg then
return item
end
end
else
for _, item in ipairs(args.tbl) do
if item[args.key] == args.arg then
return args.arg
end
end
end
return nil
end
function core.validate_mod(args)
local value = g_args[args.key .. args.i]
local out = {
result=nil,
type=args.key,
is_text=false,
}
if value ~= nil then
--semantic search
local query = {
string.format('[[Is mod::%s]]', value),
'?#=Page',
'?Is Mod#',
'?Has mod group#',
'?Has mod type#',
'?Has stat text#',
}
out.result = util.smw.query(query, g_frame)
query = {
string.format('[[-Has subobject::%s]]', result['?Page']),
'?Is stat number#',
'?Has stat id#',
'?Has minimum stat value#',
'?Has maximum stat value#',
}
out.result.stats = util.smw.query(query, g_frame)
table.insert(g_args._subobjects, {
['Is mod number'] = args.i,
['Has mod id'] = g_args[value],
})
-- process and cache stats
local id, min, max
for _, stat in ipairs(out.result.stats) do
id = stat['?Has stat id']
min = tonumber(stat['?Has minimum stat value'])
max = tonumber(stat['?Has maximum stat value'])
if g_args._stats[id] == nil then
g_args._stats[id] = {
references = #out+1,
min = min,
max = max,
}
else
table.insert(g_args._stats[id].references, #out+1)
g_args._stats[id].min = g_args._stats[id].min + min
g_args._stats[id].max = g_args._stats[id].max + max
end
end
else
value = g_args[args.key .. args.i .. '_text']
if value ~= nil then
table.insert(g_args._subobjects, {
['Is mod number'] = args.i,
['Has mod text'] = value,
})
out.is_text = true
out.result = value
end
end
if out.result ~= nil then
table.insert(g_args._mods, out)
return true
else
return false
end
end
function core.err(args)
local err = mw.html.create('div')
err
:attr('style', 'font-color: red; font-weight: bold;')
:wikitext(args.msg or ('Argument ' .. args.key .. ' to item template is invalid. Please check the documention for acceptable values.'))
:done()
return tostring(err)
end
--
-- function factory
--
core.factory = {}
function core.factory.table_cast(k, args)
return function ()
args.arg = g_args[k]
g_args[k] = core.validate_table(args)
if g_args[k] == nil then
error(core.err{key=k})
end
end
end
function core.factory.number_cast(k, default)
return function ()
g_args[k] = tonumber(g_args[k]) or default
end
end
function core.factory.percentage(k)
return function ()
local v = tonumber(g_args[k])
if v == nil then
return core.err{key=k}
end
if v < 0 or v > 100 then
return core.err{msg=k .. ' must be in range 0-100.'}
end
g_args[k] = v
end
end
function core.factory.display_mods(args)
return function ()
lines = {}
if args.type == 'quality' then
table.insert(lines, h.new_color('default', 'Per 1% Quality:'))
end
for _, modinfo in ipairs(g_args._mods) do
if modinfo.type == args.type then
if modinfo.is_text then
table.insert(lines, modinfo.result)
else
table.insert(lines, modinfo.result['?Has stat text'])
end
end
end
return lines
end
end
function core.factory.display_value(args)
-- TODO: sane defaults for the prepend string "before" & after arguments
--
-- args:
-- args<Array>: Array of arguments to use for displaying
-- values<Array>: Array of base values to use for displaying; useful if no args are present
-- options<Array>:
-- fmt: formatter to use for the value instead of valfmt
-- allow_zero: allow zero values
-- before: add this string before the coloured string with colour gray
-- after: add this string after the coloured string with colour gray
-- stats_add: Array of stats to add
-- stats_multiplay: Array of stats to multiply
local _map = {
add = function (value, stat_cached)
value.min = value.min + stat_cached.min
value.max = value.max + stat_cached.max
end,
multiply = function (value, stat_cached)
value.min = value.min * stat_cached.min / 100
value.max = value.max * stat_cached.max / 100
end,
}
return function ()
for k, default in pairs({stats = {}, options = {}}) do
if args[k] == nil then
args[k] = default
end
end
local values = {}
if args.values == nil then
values = {}
for i, k in ipairs(args.keys) do
local value = g_args[k]
if g_args.base_item then
value = tonumber(g_args.base_item['?' .. core.map[k].property]) or value
end
values[i] = value
end
else
values = args.values
end
local final_values = {}
for i, base_value in ipairs(values) do
local value = {min=base_value, max=base_value}
for _, operator in ipairs({'add', 'multiply'}) do
local st = args.options[i]['stats_' .. operator]
if st ~= nil then
for _, statid in ipairs(st) do
if g_args._stats[statid] ~= nil then
_map[operator](value, g_args._stats[statid])
end
end
end
end
local prop = args.options[i].property
if prop ~= nil then
g_args._properties[prop .. ' range minimum'] = value.min
g_args._properties[prop .. ' range maximum'] = value.max
g_args._properties[prop .. ' range avarage'] = (value.min + value.max) / 2
end
if not (value.min == 0 and value.max == 0 and args.options[i].allow_zero == nil) then
table.insert(final_values, {value=value, index=i})
end
end
-- all zeros = dont display and return early
if #final_values == 0 then
return nil
end
local out = {}
if args.before then
out[#out+1] = h.new_color('default', args.before)
end
for i, data in ipairs(final_values) do
local value = data.value
local base_value = values[data.index]
local fmt = args.options[data.index] or {}
if base_value ~= value.min or base_value ~= value.max then
value.color = 'mod'
else
value.color = 'value'
end
if value.min == value.max then
value.out = string.format(fmt.fmt or '%s', value.min)
else
value.out = string.format(string.format("(%s to %s)", fmt.fmt or '%s', fmt.fmt or '%s'), value.min, value.max)
end
value.out = h.new_color(value.color, value.out)
local before = ''
if fmt.before ~= nil then
before = h.new_color('default', fmt.before)
end
local after = ''
if fmt.after ~= nil then
after = h.new_color('default', fmt.after)
end
out[#out+1] = before .. value.out .. after
end
if args.after then
out[#out+1] = h.new_color('default', args.after)
end
return table.concat(out, ' ')
end
end
function core.factory.display_value_only(key)
return function()
return g_args[key]
end
end
--
-- argument mapping
--
core.map = {
-- generic
class = {
property = 'Has item class',
func = core.factory.table_cast('class', {key='full', tbl=game.constants.item.class}),
},
rarity = {
property = 'Has rarity',
func = core.factory.table_cast('rarity', {key={'full', 'long_lower'}, tbl=game.constants.item.rarity, rtrkey='full'}),
},
name = {
property = 'Has name',
func = nil,
},
size_x = {
property = 'Has inventory width',
func = core.factory.number_cast('size_x'),
},
size_y = {
property = 'Has inventory height',
func = core.factory.number_cast('size_y'),
},
drop_level = {
property = 'Has drop level',
func = core.factory.number_cast('drop_level'),
},
required_level = {
property = 'Has level requirement',
func = core.factory.number_cast('required_level'),
},
required_dexterity = {
property = 'Has base dexterity requirement',
func = core.factory.number_cast('required_dexterity'),
},
required_strength = {
property = 'Has base strength requirement',
func = core.factory.number_cast('required_strength'),
},
required_intelligence = {
property = 'Has base intelligence requirement',
func = core.factory.number_cast('required_intelligence'),
},
inventory_icon = {
property = 'Has inventory icon',
func = function ()
-- TODO readd support if needed.
g_args['inventory_icon'] = string.format('File:%s inventory icon.png', g_args.name)
end,
},
help_text = {
property = 'Has help text',
func = nil,
},
flavour_text = {
property = 'Has flavour text',
func = nil,
},
-- For rarity != normal, rarity already verified
base_item = {
property = 'Has base item',
func = function ()
if g_args['rarity'] ~= 'Normal' then
local query = {
string.format('[[Has name::%s]]', g_args['base_item']),
string.format('[[Has item class::%s]]', g_args['class']),
'[[Has rarity::normal]]',
}
for _, k in ipairs(g_args['total_args']) do
query[#query+1] = string.format('?%s#', core.map[k].property)
end
local result = util.smw.query(query, g_frame)
if #result > 1 then
error(core.err{msg='More then one result found for the specified base item.'})
-- TODO be more explicit in the error?
end
g_args['base_item'] = result
-- TODO Semantic search - verify ?
elseif g_args['base_item'] ~= nil then
error(core.err{msg='Base item requries item with rarity above normal.'})
end
end,
},
--
-- specific section
--
-- flasks
charges_max = {
property = 'Has base maximum flask charges',
func = core.factory.number_cast('charges_max'),
},
charges_per_use = {
property = 'Has base flask charges per use',
func = core.factory.number_cast('changes_per_use'),
},
flask_mana = {
property = 'Has base flask mana recovery',
func = core.factory.number_cast('flask_mana'),
},
flask_life = {
property = 'Has base flask life recovery',
func = core.factory.number_cast('flask_life'),
},
flask_duration = {
property = 'Has base flask duration',
func = core.factory.number_cast('flask_duration'),
},
-- weapons & active skills
critical_strike_chance = {
property = 'Has base critical strike chance',
func = core.factory.number_cast('critical_strike_chance'),
},
-- weapons
attack_speed = {
property = 'Has base attack speed',
func = core.factory.number_cast('attack_speed'),
},
damage_min = {
property = 'Has base minimum damage',
func = core.factory.number_cast('damage_min'),
},
damage_max = {
property = 'Has base maximum damage',
func = core.factory.number_cast('damage_max'),
},
range = {
property = 'Has base weapon range',
func = core.factory.number_cast('range'),
},
-- armor-type stuff
armour = {
property = 'Has base armour',
func = core.factory.number_cast('armour', 0),
},
energy_shield = {
property = 'Has base energy shield',
func = core.factory.number_cast('energy shield', 0),
},
evasion = {
property = 'Has base evasion',
func = core.factory.number_cast('evasion', 0),
},
-- shields
block = {
property = 'Has base block chance',
func = core.factory.number_cast('block'),
},
-- skill gem stuff
gem_description = {
property = 'Has description',
func = nil,
},
dex_percent = {
property = 'Has dexterity percentage',
func = core.factory.percentage('dex_percent'),
},
str_percent = {
property = 'Has strength percentage',
func = core.factory.percentage('str_percent'),
},
int_percent = {
property = 'Has intelligence percentage',
func = core.factory.percentage('int_percent'),
},
gem_tags = {
property = 'Has gem tag',
func = function ()
local tags = {}
if g_args.gem_tags ~= nil then
for s in mw.text.gsplit(g_args.gem_tags, ', ') do
local r = core.validate_table{arg=s, tbl=game.constants.item.gem_tags, key='full'}
if r == nil then
error(core.err{msg=s .. ' is not a valid gem tag.'})
end
tags[#tags+1] = r
end
end
g_args.gem_tags = tags
end,
},
experience = {
property = 'Has maximum experience',
func = core.factory.percentage('experience'),
},
-- Active gems only
gem_icon = {
property = 'Has skill gem icon',
func = function ()
-- TODO readd support if needed.
g_args['gem_icon'] = string.format('File:%s skill icon.png', g_args.name)
end,
},
mana_reserved = {
property = 'Has mana reserved',
func = core.factory.number_cast('mana_reserved'),
},
cooldown_time = {
property = 'Has cooldown time',
func = core.factory.number_cast('cooldown_time'),
},
cast_time = {
property = 'Has cast time',
func = core.factory.number_cast('cast_time'),
},
damage_effectiveness = {
property = 'Has damage effectiveness',
func = core.factory.number_cast('damage_effectiveness', 100),
},
mana_cost = {
property = 'Has mana cost',
func = core.factory.number_cast('mana_cost'),
},
souls_normal = {
property = 'Has normal souls requirement',
func = core.factory.number_cast('souls_normal'),
},
souls_cruel = {
property = 'Has cruel souls requirement',
func = core.factory.number_cast('souls_cruel'),
},
souls_merciless = {
property = 'Has merciless souls requirement',
func = core.factory.number_cast('souls_merciless'),
},
stored_uses = {
property = 'Has stored uses',
func = core.factory.number_cast('stored_uses'),
},
-- Support gems only
mana_cost_multiplier = {
property = 'Has mana cost multiplier',
func = core.factory.number_cast('mana_cost_multiplier'),
},
support_gem_letter = {
property = 'Has support gem letter',
func = nil,
},
-- Maps
map_level = {
property = 'Has map level',
func = core.factory.number_cast('map_level'),
},
-- TODO: aren't these given by mods ... removeable?
map_item_quantity = {
property = 'Has base map item quantity',
func = core.factory.number_cast('map_item_quantity'),
},
map_item_rarity = {
property = 'Has base map item rarity',
func = core.factory.number_cast('map_item_rarity'),
},
map_pack_size = {
property = 'Has base map pack size',
func = core.factory.number_cast('map_pack_size'),
},
-- Misc
stack_size = {
property = 'Has stack size',
func = core.factory.number_cast('stack_size'),
},
}
-- base item is default, but will be validated later
core.default_args = {'class', 'rarity', 'name', 'size_x', 'size_y', 'drop_level', 'required_level', 'required_dexterity', 'required_intelligence', 'required_strength', 'inventory_icon', 'flavour_text', 'help_text'}
core.class_groups = {
flasks = {
keys = xtable:new({'Life Flasks', 'Mana Flasks', 'Hybrid Flasks', 'Utility Flasks', 'Critical Utility Flasks'}),
args = {'flask_duration', 'charges_max', 'charges_per_use'},
},
weapons = {
keys = xtable:new({'Claws', 'Daggers', 'Wands', 'One Hand Swords', 'Thrusting One Hand Swords', 'One Hand Axes', 'One Hand Maces', 'Bows', 'Staves', 'Two Hand Swords', 'Two Hand Axes', 'Two Hand Maces', 'Sceptres'}),
args = {'critical_strike_chance', 'attack_speed', 'damage_min', 'damage_max', 'range'},
},
gems = {
keys = xtable:new({'Active Skill Gems', 'Support Skill Gems'}),
args = {'gem_description', 'dex_percent', 'str_percent', 'int_percent', 'gem_tags', 'experience'},
},
armor = {
keys = xtable:new({'Gloves', 'Boots', 'Body Armours', 'Helmets', 'Shields'}),
args = {'armour', 'energy_shield', 'evasion'},
},
stackable = {
keys = xtable:new({'Currency', 'Stackable Currency', 'Hideout Doodads', 'Microtransactions', 'Divination Card'}),
args = {'stack_size'},
},
}
core.class_specifics = {
['Life Flasks'] = {
args = {'flask_life'},
},
['Mana Flasks'] = {
args = {'flask_mana'},
},
['Hybrid Flasks'] = {
args = {'flask_life', 'flask_mana'},
},
['Active Skill Gems'] = {
args = {'gem_icon', 'mana_cost', 'mana_reserved', 'souls_normal', 'souls_cruel', 'souls_merciless', 'cooldown_time', 'cast_time', 'critical_strike_chance', 'damage_effectiveness', 'stored_uses'},
defaults = {
help_text = 'Place into an item socket of the right colour to gain this skill. Right click to remove from a socket.',
size_x = 1,
size_y = 1,
},
},
['Support Skill Gems'] = {
args = {'mana_cost_multiplier', 'support_gem_letter'},
defaults = {
help_text = 'This is a Support Gem. It does not grant a bonus to your character, but skills in sockets connected to it. Place into an item socket connected to a socket contianing the Active Skill Gem you wish to agument. Right click to remove from a socket.',
size_x = 1,
size_y = 1,
},
},
['Shields'] = {
args = {'block'},
},
['Hideout Doodads'] = {
defaults = {
help_text = 'Right click on this item then left click on a location on the ground to create the object.',
},
},
['Jewel'] = {
defaults = {
help_text = 'Place into an allocated Jewel Socket on the Passive Skill Tree. Right click to remove from the Socket.',
},
},
}
-- add defaults from class specifics and class groups
core.item_classes = {}
for _, data in ipairs(game.constants.item.class) do
core.item_classes[data['full']] = {
args = xtable:new(),
defaults = {},
}
end
for _, row in pairs(core.class_groups) do
for _, k in ipairs(row.keys) do
core.item_classes[k].args:insertT(row.args)
end
end
for k, row in pairs(core.class_specifics) do
if row.args ~= nil then
core.item_classes[k].args:insertT(row.args)
end
if row.defaults ~= nil then
for key, value in pairs(row.defaults) do
core.item_classes[k].defaults[key] = value
end
end
end
-- GroupTable -> RowTable -> formatter function
--
--
core.display_groups = {
-- Tags, stats, level, etc
{
{
args = {'gem_tags'},
func = function ()
local out = {}
for i, tag in ipairs(g_args.gem_tags) do
out[#out+1] = '[[' .. tag .. ']]'
end
return table.concat(out, ', ')
end,
},
-- TODO: gem level here. Maybe put max level here?
{
args = {'mana_cost'},
func = core.factory.display_value{
keys={'mana_cost'},
options = {
[1] = {
--property = 'Has mana cost',
fmt = '%i',
before = 'Mana Cost: ',
},
},
},
},
{
args = {'mana_reserved'},
func = core.factory.display_value{
keys={'mana_reserved'},
options = {
[1] = {
--property = 'Has mana reserved',
fmt = '%i',
before = 'Mana Reserved: ',
},
},
},
},
{
args = {'mana_multiplier'},
func = core.factory.display_value{
keys={'mana_multiplier'},
options = {
[1] = {
--property = 'Has mana multiplier',
fmt = '%i',
before = 'Mana Multiplier: ',
},
},
},
},
{
args = {'souls_normal', 'souls_cruel', 'souls_merciless'},
func = core.factory.display_value{
keys={'souls_normal', 'souls_cruel', 'souls_merciless'},
options = {
[1] = {
--property = 'Has normal souls requirement',
fmt = '%i (N) / ',
before = 'Souls per use: ',
},
[2] = {
--property = 'Has cruel souls requirement',
fmt = '%i (C) / ',
},
[3] = {
--property = 'Has merciless souls requirement',
fmt = '%i (M)',
},
},
},
},
{
args = {'stored_uses'},
func = core.factory.display_value{
keys={'stored_uses'},
options = {
[1] = {
--property = 'Has stored uses',
fmt = '%i',
before = 'Can store ',
after = ' Use',
},
},
},
},
{
args = {'cooldown_time'},
func = core.factory.display_value{
keys={'cooldown_time'},
options = {
[1] = {
--property = 'Has cooldown time',
fmt = '%.2f sec',
before = 'Cooldown Time: ',
},
},
},
},
{
args = {'cast_time'},
func = core.factory.display_value{
keys={'cast_time'},
options = {
[1] = {
--property = 'Has cast time',
fmt = '%.2f sec',
before = 'Cast Time: ',
},
},
},
},
{
args = {'critical_strike_chance'},
func = core.factory.display_value{
keys={'critical_strike_chance'},
options = {
[1] = {
property = 'Has critical strike chance',
fmt = '%.2f%%',
before = 'Critical Strike Chance: ',
stats_add = {
'local_critical_strike_chance',
},
stats_multiply = {
'local_critical_strike_chance_+%',
},
},
},
},
},
{
args = {'damage_effectiveness'},
func = core.factory.display_value{
keys={'damage_effectiveness'},
options = {
[1] = {
--property = 'Has damage effectiveness',
fmt = '%i%%',
before = 'Damage Effectiveness: ',
},
},
},
},
-- Weapon only
{
args = {'damage_min', 'damage_max'},
func = core.factory.display_value{
keys={'damage_min', 'damage_max'},
options = {
[1] = {
property = 'Has minimum physical damage',
fmt = '%i',
stats_add = {
'local_minimum_added_physical_damage',
},
stats_multiply = {
'local_physical_damage_+%',
},
},
[2] = {
property = 'Has maximum physical damage',
fmt = '%i',
stats_add = {
'local_maximum_added_physical_damage',
},
stats_multiply = {
'local_physical_damage_+%',
},
},
},
},
},
{
args = nil,
func = core.factory.display_value{
before='Elemental Damage: ',
values={0, 0, 0, 0, 0, 0},
options = {
[1] = {
property = 'Has minimum fire damage',
fmt = '%i',
before = '<span class="text-fire">',
stats_add = {
'local_minimum_added_fire_damage',
},
},
[2] = {
property = 'Has maximum fire damage',
fmt = '%i',
after = '</span>',
stats_add = {
'local_maximum_added_fire_damage',
},
},
[3] = {
property = 'Has minimum cold damage',
fmt = '%i',
before = '<span class="text-cold">',
stats_add = {
'local_minimum_added_cold_damage',
},
},
[4] = {
property = 'Has maximum cold damage',
fmt = '%i',
after = '</span>',
stats_add = {
'local_maximum_added_cold_damage',
},
},
[5] = {
property = 'Has minimum lightning damage',
fmt = '%i',
before = '<span class="text-lightning">',
stats_add = {
'local_minimum_added_lightning_damage',
},
},
[6] = {
property = 'Has maximum lightning damage',
fmt = '%i',
after = '</span>',
stats_add = {
'local_maximum_added_lightning_damage',
},
},
},
},
},
{
args = nil,
func = core.factory.display_value{
before='Chaos Damage: ',
values={0,0},
options = {
[1] = {
property = 'Has minimum chaos damage',
fmt = '%i',
stats_add = {
'local_minimum_added_chaos_damage',
},
},
[2] = {
property = 'Has maximum chaos damage',
fmt='%i',
stats_add = {
'local_maximum_added_chaos_damage',
},
},
},
},
},
-- Map only
{
args = {'map_level'},
func = core.factory.display_value{
keys={'map_level'},
options = {
[1] = {
--property = 'Has map level',
fmt = '%i',
before = 'Map Level: ',
},
},
},
},
{
args = {'map_item_quantity'},
func = core.factory.display_value{
keys={'map_item_quantity'},
options = {
[1] = {
property = 'Has map item quantity',
fmt = '%i',
before = 'Item Quantity: ',
stats_add = {
'map_item_drop_quantity_+%',
},
},
},
},
},
{
args = {'map_item_rarity'},
func = core.factory.display_value{
keys={'map_item_rarity'},
options = {
[1] = {
property = 'Has map item rarity',
fmt = '%i',
before = 'Item Rarity: ',
stats_add = {
'map_item_drop_rarity_+%',
},
},
},
},
},
{
args = {'map_pack_size'},
func = core.factory.display_value{
keys={'map_pack_size'},
options = {
[1] = {
property = 'Has map pack size',
fmt = '%i',
before = 'Pack Size: ',
stats_add = {
'map_pack_size_+%',
},
},
},
},
},
-- Jewel Only
{
args = {'limit'},
func = core.factory.display_value{
keys={'limit'},
options = {
[1] = {
--property = 'Has limit',
fmt = '%i',
before = 'Limit: ',
},
},
},
},
{
args = {'jewel_radius'},
func = core.factory.display_value{
keys={'jewel_radius'},
options = {
[1] = {
--property = 'Has jewel radius',
fmt = '%i',
before = 'Radius: ',
},
},
},
},
-- Flask only
{
args = {'flask_mana', 'flask_duration'},
--func = core.factory.display_flask('flask_mana'),
func = core.factory.display_value{
keys = {'flask_mana', 'flask_duration'},
options = {
[1] = {
property = 'Has flask mana recovery',
fmt = '%i Mana',
before = 'Recovers',
stats_add = {
'local_flask_mana_to_recover',
},
stats_multiply = {
'local_flask_mana_to_recover_+%',
},
},
[2] = {
property = 'Has flask duration',
fmt = '%.2f',
before = ' over ',
after = ' seconds',
stats_multiply = {
'local_flask_recovery_speed_+%',
},
},
}
},
},
{
args = {'flask_life', 'flask_duration'},
func = core.factory.display_value{
keys = {'flask_life', 'flask_duration'},
options = {
[1] = {
property = 'Has flask mana recovery',
fmt = '%i Life',
before = 'Recovers',
stats_add = {
'local_flask_life_to_recover',
},
stats_multiply = {
'local_flask_life_to_recover_+%',
},
},
[2] = {
property = 'Has flask duration',
fmt = '%.2f',
before = ' over ',
after = ' seconds',
stats_multiply = {
'local_flask_recovery_speed_+%',
},
},
}
},
},
{
-- don't display for mana/life flasks
args = function ()
for _, k in ipairs({'flask_life', 'flask_mana'}) do
if g_args[k] ~= nil then
return false
end
end
return g_args['flask_duration'] ~= nil
end,
func = core.factory.display_value{
keys = {'flask_duration'},
options = {
[1] = {
property = 'Has flask duration',
before = 'Lasts ',
after = ' Seconds',
fmt = '%.2f',
stats_multiply = {
'local_flask_recovery_speed_+%',
},
},
},
},
},
{
args = {'charges_per_use', 'charges_cap'},
func = core.factory.display_value{
keys = {'charges_per_use', 'charges_cap'},
options = {
[1] = {
property = 'Has flask charges per use',
before = 'Consumes ',
fmt = '%i',
stats_multiply = {
'local_charges_used_+%',
},
},
[2] = {
property = 'Has flask maximum charges',
before = ' of ',
after = ' Charges on use',
fmt = '%i',
stats_add = {
'local_extra_max_charges',
},
stats_multiply = {
'local_max_charges_+%',
'local_charges_added_+%',
},
},
},
},
},
-- armor
{
args = {'block'},
func = core.factory.display_value{
keys={'block'},
options = {
[1] = {
property = 'Has block',
fmt = '%i',
stats_add = {
'local_additional_block_chance_%',
},
},
},
},
},
{
args = {'armour'},
func = core.factory.display_value{
keys={'armour'},
options = {
[1] = {
property = 'Has armour',
fmt = '%i',
stats_add = {
'local_base_physical_damage_reduction_rating',
},
stats_multiply = {
'local_physical_damage_reduction_rating_+%',
'local_armour_and_energy_shield_+%',
'local_armour_and_evasion_+%',
'local_armour_and_evasion_and_energy_shield_+%',
},
},
},
},
},
{
args = {'evasion'},
func = core.factory.display_value{
keys={'evasion'},
options = {
[1] = {
property = 'Has evasion',
fmt = '%i',
stats_add = {
'local_base_evasion_rating',
},
stats_multiply = {
'local_evasion_rating_+%',
'local_evasion_and_energy_shield_+%',
'local_armour_and_evasion_+%',
'local_armour_and_evasion_and_energy_shield_+%',
},
},
},
},
},
{
args = {'energy_shield'},
func = core.factory.display_value{
keys={'energy_shield'},
options = {
[1] = {
property = 'Has energy shield',
fmt = '%i',
stats_add = {
'local_energy_shield'
},
stats_multiply = {
'local_energy_shield_+%',
'local_armour_and_energy_shield_+%',
'local_evasion_and_energy_shield_+%',
'local_armour_and_evasion_and_energy_shield_+%',
},
},
},
},
},
-- Misc
{
args = {'stack_size'},
func = core.factory.display_value{
keys={'stack_size'},
options = {
[1] = {
--property = 'Has stack size',
fmt = '%i',
before = 'Stack Size: ',
},
},
},
},
},
-- Requirements
{
-- Instead of item level, show drop level if any
{
args = {'drop_level'},
func = core.factory.display_value{
keys={'drop_level'},
options = {
[1] = {
fmt = '%i',
before = 'Drop Level: ',
},
},
},
},
{
args = nil,
func = function ()
local requirements = {}
local attr_label
local use_short_label
if g_args.required_level then
table.insert( requirements, 'Level ' .. tostring( h.new_color('value', g_args.required_level) ) )
end
for _, attr in ipairs(game.constants.attributes) do
local val = g_args['required_' .. attr['long_lower']]
if val then
use_short_label = false or g_args.required_level
for _, attr2 in ipairs(game.constants.attributes) do
if attr ~= attr2 then
use_short_label = use_short_label or args[attr2['long_lower']]
end
end
if use_short_label then
attr_label = attr['short_upper']
else
attr_label = attr['long_upper']
end
table.insert( requirements, h.new_color('value', val) .. ' ' .. attr_label )
end
end
-- return early
if #requirements == 0 then
return
end
return h.new_color('default', 'Requires ') .. table.concat(requirements, ', ')
end,
},
},
-- Gem description
{
css_class = 'textwrap text-gemdesc',
{
args = {'gem_description'},
func = core.factory.display_value_only('gem_description'),
},
},
-- Quality Stats
{
css_class = 'text-mod',
func = core.factory.display_mods{type='quality'},
},
-- Implicit Stats
{
css_class = 'text-mod',
func = core.factory.display_mods{type='implicit'},
},
-- Stats
{
css_class = 'text-mod',
func = core.factory.display_mods{type='mod'},
},
-- Experience
{
{
args = {'experience'},
func = core.factory.display_value{
keys={'experience'},
options = {
[1] = {
fmt = '%i',
},
},
},
},
},
-- Flavour Text
{
css_class = 'textwrap text-flavour',
{
args = {'flavour_text'},
func = core.factory.display_value_only('flavour_text'),
},
},
-- Help text
{
css_class = 'textwrap text-help',
{
args = {'help_text'},
func = core.factory.display_value_only('help_text'),
},
},
-- Cost (i.e. vendor costs)
{
},
}
-- ----------------------------------------------------------------------------
-- Page views
-- ----------------------------------------------------------------------------
--
-- Frame args:
-- class
-- rarity
--[[ =p.itembox{
name='Vaal Ground Slam',
class='Active Skill Gems',
rarity='Normal',
gem_tags='Vaal, Spell, Duration',
str_percent='100',
required_level='34',
souls_normal='48',
souls_cruel='72',
souls_merciless='96',
stored_uses='1',
cast_time='0.85',
gem_description='Discharges Endurance Charges, making the character unable to die for a short time, proportional to how many endurance charges were expended.',
quality1_text='2% Increased cast speed',
mod1_text='Base Duration is 0.4 seconds',
mod2_text='Additional x seconds Base Duration per Endurance Charge',
}
]]--
function p.itembox (frame)
--
-- Args/Frame
--
g_args = getArgs(frame, {
parentFirst = true
})
if frame == nil or type(frame) == 'table' then
frame = mw.getCurrentFrame()
end
g_frame = frame
--
-- Shared args
--
g_args._total_args = {}
local properties = {}
-- Must validate some argument early. It is required for future things
for _, k in ipairs(core.default_args) do
table.insert(g_args._total_args, k)
local data = core.map[k]
if data.func ~= nil then
local status, err = pcall(data.func)
-- an error was raised, return the error string instead of the template
if not status then
return err
end
end
end
for _, k in ipairs(core.item_classes[g_args.class].args) do
table.insert(g_args._total_args, k)
local data = core.map[k]
if data.func ~= nil then
local status, err = pcall(data.func)
-- an error was raised, return the error string instead of the template
if not status then
return err
end
end
end
-- set defaults
for k, v in pairs(core.item_classes[g_args.class].defaults) do
if g_args[k] == nil then
g_args[k] = v
end
end
-- Mods
g_args._mods = {}
g_args._stats = {}
g_args._subobjects = {}
for _, k in ipairs({'implicit', 'mod', 'quality'}) do
local success = true
local i = 1
while success do
success = core.validate_mod{key=k, i=i}
i = i + 1
end
end
-- Handle extra stats (for gems)
if core.class_groups.gems.keys:contains(g_args.class) then
local success = true
local i = 1
while success do
local value = {
id = g_args['stat' .. i .. '_id'],
min = g_args['stat' .. i .. '_min'],
max = g_args['stat' .. i .. '_max'],
}
if not (value.id == nil and value.min == nil and value.max == nil) then
local onenil = nil
for _, k in ipairs({'id', 'min', 'max'}) do
if value[k] == nil then
onenil = k
break
end
end
if onenil ~= nil then
error('"' .. id[onenil] .. '" is not set')
end
value.min = util.number.cast(value.min)
value.max = util.number.cast(value.max)
properties = {}
properties['Is stat number'] = i
properties['Has stat id'] = value.id
properties['Has minimum stat value'] = util.number.cast(value.min)
properties['Has maximum stat value'] = util.number.cast(value.max)
g_frame:callParserFunction('#subobject:', properties)
else
success = false
end
end
end
-- base_item
core.map.base_item.func()
table.insert(g_args._total_args, 'base_item')
-- Setting semantic properties Part 1 (base values)
local val
for _, k in ipairs(g_args._total_args) do
val = g_args[k]
if val == nil then
elseif type(val) == 'table' then
g_frame:callParserFunction('#set:', {
[core.map[k].property] = table.concat(g_args[k], ';'),
['+sep'] = ';',
})
else
properties[core.map[k].property] = g_args[k]
end
end
g_frame:callParserFunction('#set:', properties)
-- Subobjects
for _, properties in ipairs(g_args._subobjects) do
g_frame:callParserFunction('#subobject:', properties)
end
--
-- Validate arguments
--
return v.itembox()
end
function p.new_func (frame)
--
-- Args/Frame
--
g_args = getArgs(frame, {
parentFirst = true
})
if frame == nil or type(frame) == 'table' then
frame = mw.getCurrentFrame()
end
end
-- ----------------------------------------------------------------------------
-- Property views..
-- ----------------------------------------------------------------------------
function v.itembox ()
--todo: remove g_args.frame ?
local container = v._itembox_core()
for _, k in ipairs({'inventory_icon', 'gem_icon'}) do
container:wikitext(string.format('[[%s]]', g_args[k]))
end
return tostring(container) .. v._itembox_categories()
end
function v._itembox_core()
-- needed later for setting caculated properties
g_args._properties = {}
local frame_css = {
['Currency'] = 'currency',
['Microtransactions'] = 'currency',
['Active Skill Gems'] = 'gem',
['Support Skill Gems'] = 'gem',
['Quest'] = 'quest',
['Divination Card'] = 'divicard'
}
local container = mw.html.create('div')
:attr( 'class', 'itembox-' .. ( string.lower(g_args.frame or frame_css[g_args.class] or game.constants.item.rarity[g_args.rarity]['lower'] or 'normal') ) .. ' ' .. (g_args.class or '') )
if g_args.class == 'Divination Card' then
--TODO div card code
else
container
:tag('span')
:attr( 'class', 'itemboxheader-' .. (g_args.base_item and 'double' or 'single') )
:tag('span')
:attr('class', 'itemboxheaderleft')
:done()
:tag('span')
:attr('class', 'itemboxheaderright')
:done()
:tag('span')
:attr('class', 'itemboxheadertext')
:wikitext( g_args.name .. (g_args.base_item and '<br>' .. g_args.base_item or '') )
:done()
:done()
local grpcont
local valid
local statcont = container:tag('span')
statcont
:attr('class', 'itemboxstats')
:done()
for _, group in ipairs(core.display_groups) do
grpcont = {}
if group.func == nil then
for _, disp in ipairs(group) do
valid = true
-- No args to verify which means always valid
if disp.args == nil then
elseif type(disp.args) == 'table' then
for _, key in ipairs(disp.args) do
if g_args[key] == nil then
valid = false
break
end
end
elseif type(disp.args) == 'function' then
valid = disp.args()
end
if valid then
--mw.logObject(disp)
grpcont[#grpcont+1] = disp.func()
end
end
else
grpcont = group.func()
end
if #grpcont > 0 then
statcont
:tag('span')
:attr('class', 'itemboxstatsgroup ' .. (group.css_class or ''))
:wikitext(table.concat(grpcont, '<br>'))
:done()
end
end
end
g_frame:callParserFunction('#set:', g_args._properties)
return container
end
function v._itembox_categories()
cats = {}
cats[#cats+1] = g_args.class
if g_args.rarity == 'Unique' then
cats[#cats+1] = 'Unique ' .. g_args.class
end
if g_args.class == 'Active Skill Gems' then
for _, tag in ipairs(g_args.gem_tags) do
cats[#cats+1] = tag .. ' Skills'
end
elseif g_args.class == 'Support Skill Gems' then
for _, tag in ipairs(g_args.gem_tags) do
cats[#cats+1] = tag .. ' Supports'
end
end
-- TODO: add maintenance categories
--
-- Output formatting
--
return util.misc.add_category(cats)
end
return p