Module:Item/core: Difference between revisions
Jump to navigation
Jump to search
(Rusted scarabs are in the core drop pool in 3.20.) |
(Parse harbinger glyphs in flavor text) |
||
Line 761: | Line 761: | ||
field = 'flavour_text', | field = 'flavour_text', | ||
type = 'Text', | type = 'Text', | ||
func = h.proc.text, | func = h.proc.factory.value{ | ||
cast = function (value) | |||
value = m_util.cast.text(value) | |||
-- Parse harbinger glyphs | |||
value = string.gsub(value, '<<([Hh][Bb][Gg]%w+)>>', '<span class="glyph %1"></span>') | |||
return value | |||
end, | |||
}, | |||
}, | }, | ||
flavour_text_id = { | flavour_text_id = { |
Revision as of 19:49, 8 December 2022
This submodule contains core configuration and functions for use in Module:Item and its other submodules.
The above documentation is transcluded from Module:Item/core/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.
-------------------------------------------------------------------------------
--
-- Core confirguation and functions for Module:Item2 and submodules
--
-------------------------------------------------------------------------------
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item2')
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game')
-- 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:Item2/config/sandbox') or mw.loadData('Module:Item2/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
-- ----------------------------------------------------------------------------
-- Core
-- ----------------------------------------------------------------------------
local core = {}
core.factory = {}
function core.factory.infobox_line(args)
--[[
args
type How to read data from tpl_args using the given keys. nil = Regular, gem = Skill progression, stat = Stats
parts
[n]
key key to use. If type = gem and table is given, parse for subfield along path
hide Hide part if this function returns true
hide_key Alternate key to use to retrieve the value
hide_default hide the value if this is set
hide_default_key key to use if it isn't equal to the key parameter
-- from m_util.html.format_value --
func Function to transform the value retrieved from the database
fmt Format string (or function that returns format string) to use for the value. Default: '%s'
fmt_range Format string to use for the value range. Default: '(%s-%s)'
color poe_color code to use for the value range. False for no color. Default: 'mod'
class Additional css class added to color tag
inline Format string to use for the output
inline_color poe_color code to use for the output. False for no color. Default: 'default'
inline_class Additional css class added to inline color tag
sep If specified, parts are joined with this separator before being formatted for output
fmt Format string to use for output. If not specified, parts are simply concatenated
color poe_color code to use for output. Default: no color
class Additional css class added to output
--]]
args.parts = args.parts or {}
return function (tpl_args)
local base_values = {}
local temp_values = {}
if args.type == 'gem' then
-- Skill progression. Look for keys in tpl_args.skill_levels
if not cfg.class_groups.gems.keys[tpl_args.class_id] then
-- Skip if this item is not actually a gem
return
end
for i, data in ipairs(args.parts) do
if data.key then
local path = type(data.key) == 'table' and data.key or {data.key}
-- Check for static value
local value = tpl_args.skill_levels[0]
for _, p in ipairs(path) do -- Parse for subfield along path
if value[p] == nil then
value = nil
break
else
value = value[p]
end
end
if value ~= nil then
base_values[i] = value
temp_values[#temp_values+1] = {value={min=value,max=value}, index=i}
else -- Check for leveled values
value = {
min = tpl_args.skill_levels[1],
max = tpl_args.skill_levels[tpl_args.max_level],
}
for k, _ in pairs(value) do
for _, p in ipairs(path) do -- Parse for subfield along path
if value[k][p] == nil then
value[k] = nil
break
else
value[k] = value[k][p]
end
end
end
if value.min ~= nil and value.max ~= nil then
base_values[i] = value.min
temp_values[#temp_values+1] = {value=value, index=i}
end
end
end
end
elseif args.type == 'stat' then
-- Stats. Look for key in tpl_args._stats
for i, data in ipairs(args.parts) do
local value = tpl_args._stats[data.key]
if value ~= nil then
base_values[i] = value.min
temp_values[#temp_values+1] = {value=value, index=i}
end
end
else
-- Regular. Look for key exactly as written in tpl_args
for i, data in ipairs(args.parts) do
local value = {}
if tpl_args[data.key .. '_range_minimum'] ~= nil then
value.min = tpl_args[data.key .. '_range_minimum']
value.max = tpl_args[data.key .. '_range_maximum']
elseif tpl_args[data.key] ~= nil then
value.min = tpl_args[data.key]
value.max = tpl_args[data.key]
end
if value.min ~= nil and value.max ~= nil then
base_values[i] = value.min
temp_values[#temp_values+1] = {value=value, index=i}
end
end
end
local final_values = {}
for i, data in ipairs(temp_values) do
local opt = args.parts[data.index]
local hide = false
if type(opt.hide) == 'function' then
local v = data.value
if opt.hide_key then
v = {
min = tpl_args[opt.hide_key .. '_range_minimum'],
max = tpl_args[opt.hide_key .. '_range_maximum'],
}
if v.min == nil or v.max == nil then
v = tpl_args[opt.hide_key]
end
end
hide = opt.hide(tpl_args, v)
elseif opt.hide_default ~= nil then
if opt.hide_default_key then
local v = {
min = tpl_args[opt.hide_default_key .. '_range_minimum'],
max = tpl_args[opt.hide_default_key .. '_range_maximum'],
}
if v.min == nil or v.max == nil then
if opt.hide_default == tpl_args[opt.hide_default_key] then
hide = true
end
elseif opt.hide_default == v.min and opt.hide_default == v.max then
hide = true
end
else
local v = data.value
if opt.hide_default == v.min and opt.hide_default == v.max then
hide = true
end
end
end
if not hide then
table.insert(final_values, data)
end
end
-- all zeros = dont display and return early
if #final_values == 0 then
return nil
end
local parts = {}
for i, data in ipairs(final_values) do
local value = data.value
value.base = base_values[data.index]
local options = args.parts[data.index]
if args.type == 'gem' and options.color == nil then
-- Display skill progression range values as unmodified (white)
options.color = 'value'
end
parts[#parts+1] = m_util.html.format_value(tpl_args, value, options)
end
if args.sep then
-- Join parts with separator before formatting
parts = {table.concat(parts, args.sep)}
end
-- Build output string
local out
if args.fmt then
out = string.format(args.fmt, unpack(parts))
else
out = table.concat(parts)
end
if args.color then
out = m_util.html.poe_color(args.color, out, args.class)
elseif args.class then
out = tostring(mw.html.create('em')
:attr('class', class)
:wikitext(out)
)
end
return out
end
end
function core.stats_update(tpl_args, id, value, modid, key)
if tpl_args[key][id] == nil then
tpl_args[key][id] = {
references = {modid},
min = value.min,
max = value.max,
avg = value.avg,
}
else
if modid ~= nil then
table.insert(tpl_args[key][id].references, modid)
end
tpl_args[key][id].min = tpl_args[key][id].min + value.min
tpl_args[key][id].max = tpl_args[key][id].max + value.max
tpl_args[key][id].avg = tpl_args[key][id].avg + value.avg
end
end
--
-- Functions for processing tpl_args
--
h.proc = {}
h.proc.factory = {}
function h.proc.factory.value(args)
args = args or {}
return function (tpl_args, value)
if value == nil then
return nil
end
if args.cast then
value = args.cast(value)
end
if args.validate then
value = args.validate(value)
end
return value
end
end
function h.proc.factory.list(args)
args = args or {}
return function (tpl_args, value)
return m_util.cast.table(value, {
pattern = args.pattern,
callback = args.callback,
})
end
end
function h.proc.factory.damage_html(args)
return function (tpl_args, value)
local keys = {
min = args.type .. '_damage_min',
max = args.type .. '_damage_max',
}
local range = {}
for ktype, key in pairs(keys) do
range[ktype] = core.factory.infobox_line{
parts = {
{
key = key,
color = false,
hide_default = 0,
}
}
}(tpl_args)
end
if range.min and range.max then
local color = args.type or false
local range_fmt
if tpl_args[keys.min .. '_range_minimum'] ~= tpl_args[keys.min .. '_range_maximum'] or tpl_args[keys.max .. '_range_minimum'] ~= tpl_args[keys.max .. '_range_maximum'] then
-- Variable damage range, based on modifier rolls
if args.type == 'physical' then
color = 'mod'
end
range_fmt = i18n.fmt.variable_damage_range
else
-- Standard damage range
if args.type == 'physical' then
color = 'value'
end
range_fmt = i18n.fmt.standard_damage_range
end
value = string.format(range_fmt, range.min, range.max)
if color then
value = m_util.html.poe_color(color, value)
end
end
return value
end
end
h.proc.text = h.proc.factory.value{cast = m_util.cast.text}
h.proc.boolean = h.proc.factory.value{cast = m_util.cast.boolean}
h.proc.number = h.proc.factory.value{cast = m_util.cast.number}
h.proc.percentage = h.proc.factory.value{
cast = m_util.cast.number,
validate = m_util.validate.factory.number_in_range{
min = 0,
max = 100,
},
}
h.proc.size = h.proc.factory.value{
cast = m_util.cast.number,
validate = m_util.validate.factory.number_in_range{
min = 1,
max = 4,
},
}
h.proc.character = h.proc.factory.value{
validate = m_util.validate.factory.string_length{
min = 1,
max = 1,
},
}
h.proc.list = h.proc.factory.list()
-- Process mod stats
function h.process_mod_stats(tpl_args, args)
local lines = {}
local skip = cfg.class_specifics[tpl_args.class_id]
if skip then
skip = skip.skip_stat_lines
end
local random_mods = {}
for _, modinfo in ipairs(tpl_args._mods) do
if modinfo.is_implicit == args.is_implicit then
if modinfo.is_random == true then
if random_mods[modinfo.stat_text] then
table.insert(random_mods[modinfo.stat_text], modinfo)
else
random_mods[modinfo.stat_text] = {modinfo}
end
else
if modinfo.id == nil then
table.insert(lines, modinfo.result)
-- Allows the override of the SMW fetched mod texts for this modifier via <modtype><id>_text parameter
elseif modinfo.text ~= nil then
table.insert(lines, modinfo.text)
else
for _, line in ipairs(m_util.string.split(modinfo.result['mods.stat_text'] or '', '<br>')) do
if line ~= '' then
if skip == nil then
table.insert(lines, line)
else
local skipped = false
for _, pattern in ipairs(skip) do
if string.match(line, pattern) then
skipped = true
break
end
end
if not skipped then
table.insert(lines, line)
end
end
end
end
end
end
end
end
for stat_text, modinfo_list in pairs(random_mods) do
local text = {}
for _, modinfo in ipairs(modinfo_list) do
table.insert(text, modinfo.result['mods.stat_text'])
end
local tbl = mw.html.create('table')
tbl
:attr('class', 'random-modifier-stats mw-collapsed')
:attr('style', 'text-align: left')
:tag('tr')
:tag('th')
:attr('class', 'mw-customtoggle-31')
:wikitext(stat_text)
:done()
:done()
:tag('tr')
:attr('class', 'mw-collapsible mw-collapsed')
:attr('id', 'mw-customcollapsible-31')
:tag('td')
:wikitext(table.concat(text, '<hr style="width: 20%">'))
:done()
:done()
table.insert(lines, tostring(tbl))
end
if #lines == 0 then
return
else
return table.concat(lines, '<br>')
end
end
--
-- Argument mapping
--
-- [<tpl_args key>] = {
-- inherit boolean Whether the item will inherit this key from its base item. Default: true
-- field string Cargo field name
-- type string Cargo field type
-- func function Function to unpack the argument into a native lua value and validate it
-- default varies Default value if parameter is not set
-- }
core.map = {
-- special params
html = {
inherit = false,
field = 'html',
type = 'Text',
func = nil,
},
infobox_html = {
inherit = false,
field = 'infobox_html',
type = 'Text',
func = nil,
},
metabox_html = {
inherit = false,
field = 'metabox_html',
type = 'Text',
func = nil,
},
implicit_stat_text = {
field = 'implicit_stat_text',
type = 'Text',
func = function (tpl_args, value)
return h.process_mod_stats(tpl_args, {is_implicit=true})
end,
},
explicit_stat_text = {
field = 'explicit_stat_text',
type = 'Text',
func = function (tpl_args, value)
return h.process_mod_stats(tpl_args, {is_implicit=false})
end,
},
stat_text = {
field = 'stat_text',
type = 'Text',
func = function (tpl_args, value)
if tpl_args.implicit_stat_text or tpl_args.explicit_stat_text then
local stats = {}
table.insert(stats, tpl_args.implicit_stat_text) -- No-op if value is nil
table.insert(stats, tpl_args.explicit_stat_text)
if tpl_args.is_corrupted then
table.insert(stats, m_util.html.poe_color('corrupted', i18n.tooltips.corrupted))
elseif tpl_args.is_mirrored then
table.insert(stats, i18n.tooltips.mirrored)
elseif tpl_args.is_unmodifiable then
table.insert(stats, i18n.tooltips.unmodifiable)
end
local sep = string.format('<span class="item-stat-separator -%s"></span>', tpl_args.frame_type)
value = table.concat(stats, sep)
end
return value
end,
},
class_id = {
inherit = false,
field = 'class_id',
type = 'String',
func = function (tpl_args, value)
if value == nil then
error(string.format(i18n.errors.generic_required_parameter, 'class_id'))
end
if not m_util.table.has_key(m_game.constants.item.classes, value) or m_game.constants.item.classes[value].disabled then
error(string.format(i18n.errors.invalid_class_id, tostring(value)))
end
return value
end,
},
class = {
inherit = false,
field = 'class',
type = 'String',
func = function (tpl_args, value)
local class = m_game.constants.item.classes[tpl_args.class_id].long_upper
-- Avoids errors with empty item class names later on
if class == '' then
class = nil
end
return class
end,
},
-- generic
is_in_game = {
inherit = false,
field = 'is_in_game',
type = 'Boolean',
func = h.proc.boolean,
default = true,
},
rarity_id = {
inherit = false,
field = 'rarity_id',
type = 'String',
func = h.proc.factory.value{
validate = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.rarities,
errmsg = i18n.errors.invalid_rarity_id,
},
},
},
rarity = {
inherit = false,
field = 'rarity',
type = 'String',
func = function (tpl_args, value)
return m_game.constants.rarities[tpl_args.rarity_id].long_upper
end,
},
name = {
inherit = false,
field = 'name',
type = 'String',
func = nil,
},
size_x = {
field = 'size_x',
type = 'Integer',
func = h.proc.size,
default = 1,
},
size_y = {
field = 'size_y',
type = 'Integer',
func = h.proc.size,
default = 1,
},
drop_rarities_ids = {
inherit = false,
field = 'drop_rarity_ids',
type = 'List (,) of String',
func = function (tpl_args, value)
-- Drop rarities only matter for base items.
if tpl_args.rarity_id ~= 'normal' then
return
end
return m_util.cast.table(value, {
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.rarities,
errmsg = i18n.errors.invalid_rarity_id,
errlvl = 4,
},
})
end,
default = {},
},
drop_rarities = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
tpl_args.drop_rarities = nil
if true then return end
-- Drop rarities only matter for base items.
if tpl_args.rarity_id ~= 'normal' then
return
end
local rarities = {}
for _, rarity_id in ipairs(tpl_args.drop_rarities_ids) do
rarities[#rarities+1] = m_game.constants.rarities[rarity_id].long_upper
end
tpl_args.drop_rarities = rarities
end,
},
drop_enabled = {
inherit = false,
field = 'drop_enabled',
type = 'Boolean',
func = function (tpl_args, value)
if value == nil then
return nil -- Use default
end
return tpl_args.is_in_game and m_util.cast.boolean(value)
end,
default = function (tpl_args)
return tpl_args.is_in_game
end,
},
drop_level = {
inherit = false,
field = 'drop_level',
type = 'Integer',
func = h.proc.number,
},
drop_level_maximum = {
inherit = false,
field = 'drop_level_maximum',
type = 'Integer',
func = h.proc.number,
},
acquisition_tags = {
inherit = false,
field = 'acquisition_tags',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = cfg.acquisition_tags,
errmsg = i18n.errors.invalid_acquisition_tag,
errlvl = 4,
},
},
default = {},
},
drop_areas = {
inherit = false,
field = 'drop_areas',
type = 'List (,) of String',
func = function (tpl_args, value)
value = m_util.cast.table(value)
if type(value) == 'table' and #value > 0 then
tpl_args._drop_areas_data = m_cargo.array_query{
tables = {'areas'},
fields = {
'areas._pageName=_pageName',
'areas.id=id',
'areas.name=name',
'areas.main_page=main_page',
'areas.is_legacy_map_area=is_legacy_map_area'
},
id_field = 'areas.id',
id_array = value,
query = {limit=5000},
}
end
return value
end,
default = {},
},
drop_monsters = {
inherit = false,
field = 'drop_monsters',
type = 'List (,) of Text',
func = h.proc.list,
default = {},
},
drop_text = {
inherit = false,
field = 'drop_text',
type = 'Text',
func = h.proc.text,
},
required_level = {
field = 'required_level_base',
type = 'Integer',
func = h.proc.number,
default = 1,
},
required_level_final = {
field = 'required_level',
type = 'Integer',
func = function (tpl_args, value)
value = tpl_args.required_level
if value < cfg.base_item_required_level_threshold and cfg.base_item_required_level_threshold_classes[tpl_args.class_id] then
value = 1
end
return value
end,
default = 1,
},
required_dexterity = {
field = 'required_dexterity',
type = 'Integer',
func = h.proc.number,
default = 0,
},
required_strength = {
field = 'required_strength',
type = 'Integer',
func = h.proc.number,
default = 0,
},
required_intelligence = {
field = 'required_intelligence',
type = 'Integer',
func = h.proc.number,
default = 0,
},
inventory_icon = {
inherit = false,
field = 'inventory_icon',
type = 'String',
func = function (tpl_args, value)
if not value then
-- Certain types of items have default inventory icons
if i18n.default_inventory_icons[tpl_args.class_id] then
value = i18n.default_inventory_icons[tpl_args.class_id]
elseif tpl_args.base_item_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy' then
value = i18n.default_inventory_icons['Prophecy']
end
end
tpl_args.inventory_icon_id = value or tpl_args.name
return string.format(i18n.files.inventory_icon, tpl_args.inventory_icon_id)
end,
},
-- note: this must be called after inventory_icon to work correctly as it depends on tpl_args.inventory_icon_id being set
alternate_art_inventory_icons = {
inherit = false,
field = 'alternate_art_inventory_icons',
type = 'List (,) of String',
func = function (tpl_args, value)
return m_util.cast.table(value, {
callback = function (value)
return string.format(i18n.files.inventory_icon, string.format('%s %s', tpl_args.inventory_icon_id, tostring(value)))
end,
})
end,
default = {},
},
cannot_be_traded_or_modified = {
inherit = false,
field = 'cannot_be_traded_or_modified',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
help_text = {
field = 'help_text',
type = 'Text',
func = h.proc.text,
},
is_account_bound = {
inherit = false,
field = 'is_account_bound',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
flavour_text = {
inherit = false,
field = 'flavour_text',
type = 'Text',
func = h.proc.factory.value{
cast = function (value)
value = m_util.cast.text(value)
-- Parse harbinger glyphs
value = string.gsub(value, '<<([Hh][Bb][Gg]%w+)>>', '<span class="glyph %1"></span>')
return value
end,
},
},
flavour_text_id = {
inherit = false,
field = 'flavour_text_id',
type = 'String',
func = nil,
},
tags = {
field = 'tags',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.tags,
errmsg = i18n.errors.invalid_tag,
errlvl = 4,
},
},
default = {},
},
metadata_id = {
inherit = false,
field = 'metadata_id',
type = 'String',
--type = 'String(unique; size=200)',
func = function (tpl_args, value)
if value == nil then
return nil
end
-- Unless we're in testing mode, validate that metadata_id is unique
if not tpl_args.test then
local results = m_cargo.query(
{'items'},
{'items._pageName'},
{
where=string.format(
'items.metadata_id = "%s" AND items._pageName != "%s"',
value,
m_cargo.addslashes(mw.title.getCurrentTitle().fullText)
)
}
)
if #results > 0 then
error(string.format(i18n.errors.duplicate_metadata, value, results[1]['items._pageName']))
end
end
return value
end,
},
influences = {
inherit = false,
field = 'influences',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.influences,
errmsg = i18n.errors.invalid_influence,
errlvl = 4,
},
},
default = {},
},
is_fractured = {
inherit = false,
field = 'is_fractured',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_synthesised = {
inherit = false,
field = 'is_synthesised',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_searing_exarch_item = {
inherit = false,
field = 'is_searing_exarch_item',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_eater_of_worlds_item = {
inherit = false,
field = 'is_eater_of_worlds_item',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_veiled = {
inherit = false,
field = 'is_veiled',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_replica = {
inherit = false,
field = 'is_replica',
type = 'Boolean',
func = function (tpl_args, value)
value = m_util.cast.boolean(value)
if value == true and tpl_args.rarity_id ~= 'unique' then
error(string.format(i18n.errors.non_unique_flag, 'is_replica'))
end
return value
end,
default = false,
},
is_corrupted = {
inherit = false,
field = 'is_corrupted',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_mirrored = {
inherit = false,
field = nil,
type = nil,
func = h.proc.boolean,
default = false,
},
is_unmodifiable = {
inherit = false,
field = 'is_unmodifiable',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
is_relic = {
inherit = false,
field = 'is_relic',
type = 'Boolean',
func = function (tpl_args, value)
value = m_util.cast.boolean(value)
if value == true and tpl_args.rarity_id ~= 'unique' then
error(string.format(i18n.errors.non_unique_flag, 'is_relic'))
end
return value
end,
default = false,
},
is_prophecy = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
tpl_args._flags.is_prophecy = tpl_args.metadata_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy' or tpl_args.base_item == cfg.prophecy_base_item or tpl_args.base_item_page == cfg.prophecy_base_item_page or tpl_args.base_item_id == 'Metadata/Items/Currency/CurrencyItemisedProphecy'
return value
end
},
is_blight_item = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
tpl_args._flags.is_blight_item = tpl_args.blight_item_tier ~= nil
return value
end
},
is_fossil = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
tpl_args._flags.is_fossil = tpl_args.metadata_id and string.find(tpl_args.metadata_id, 'CurrencyDelve', 1, true) ~= nil
return value
end
},
is_scarab = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
tpl_args._flags.is_scarab = tpl_args.metadata_id and string.find(tpl_args.metadata_id, 'Scarab', 1, true) ~= nil and string.find(tpl_args.name, i18n.misc.scarab, 1, true) ~= nil
return value
end
},
is_drop_restricted = {
inherit = false,
field = 'is_drop_restricted',
type = 'Boolean',
func = h.proc.boolean,
default = function (tpl_args)
-- Divination cards have drop restrictions by design.
if tpl_args.class_id == 'DivinationCard' then
return true
end
for _, key in ipairs({'is_talisman', 'is_essence', 'is_replica', 'is_relic', '_drop_areas_data', 'drop_monsters'}) do
-- arg must be truthy and NOT an empty table
if tpl_args[key] and not (type(tpl_args[key]) == 'table' and #tpl_args[key] == 0) then
return true
end
end
for _, flag in ipairs({'is_blight_item', 'is_fossil'}) do
if tpl_args._flags[flag] then
return true
end
end
return false
end,
},
purchase_costs = {
field = nil,
type = nil,
func = function (tpl_args, value)
local purchase_costs = {}
for _, rarity_id in ipairs(m_game.constants.rarity_order) do
local rtbl = {}
local prefix = string.format('purchase_cost_%s', rarity_id)
local i = 1
while i ~= -1 do
local iprefix = prefix .. i
local values = {
name = tpl_args[iprefix .. '_name'],
amount = tonumber(tpl_args[iprefix .. '_amount']),
rarity = rarity_id,
}
if values.name ~= nil and values.amount ~= nil then
rtbl[#rtbl+1] = values
i = i + 1
tpl_args._subobjects[#tpl_args._subobjects+1] = {
_table = 'item_purchase_costs',
amount = values.amount,
name = values.name,
rarity = values.rarity,
}
else
i = -1
end
end
purchase_costs[rarity_id] = rtbl
end
return purchase_costs
end,
func_fetch = function (tpl_args)
if tpl_args.rarity_id ~= 'unique' then
return
end
local results = m_cargo.query(
{'items' ,'item_purchase_costs'},
{'item_purchase_costs.amount', 'item_purchase_costs.name', 'item_purchase_costs.rarity'},
{
join = 'items._pageID=item_purchase_costs._pageID',
where = string.format('items._pageName="%s" AND item_purchase_costs.rarity="unique"', tpl_args.base_item_page),
}
)
for _, row in ipairs(results) do
local values = {
rarity = row['item_purchase_costs.rarity'],
name = row['item_purchase_costs.name'],
amount = tonumber(row['item_purchase_costs.amount']),
}
local datavar = tpl_args.purchase_costs[string.lower(values.rarity)]
datavar[#datavar+1] = values
tpl_args._subobjects[#tpl_args._subobjects+1] = {
_table = 'item_purchase_costs',
amount = values.amount,
name = values.name,
rarity = values.rarity,
}
end
end,
},
is_sellable = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
if value == nil then
return nil -- Use default
end
return tpl_args.is_in_game and m_util.cast.boolean(value)
end,
default = function (tpl_args)
return tpl_args.is_in_game
end,
},
sell_prices_override = {
inherit = false,
field = nil,
type = nil,
func = function (tpl_args, value)
-- these variables are also used by mods when setting automatic sell prices
tpl_args.sell_prices = {}
tpl_args.sell_price_order = {}
if not tpl_args.is_sellable then
return nil
end
local name
local amount
local i = 0
repeat
i = i + 1
name = tpl_args[string.format('sell_price%s_name', i)]
amount = tpl_args[string.format('sell_price%s_amount', i)]
if name ~= nil and amount ~= nil then
tpl_args.sell_price_order[#tpl_args.sell_price_order+1] = name
tpl_args.sell_prices[name] = amount
tpl_args._subobjects[#tpl_args._subobjects+1] = {
_table = 'item_sell_prices',
amount = amount,
name = name,
}
end
until name == nil or amount == nil
-- if sell prices are set, the override is active
for _, _ in pairs(tpl_args.sell_prices) do
tpl_args._flags.sell_prices_override = true
break
end
return value
end,
},
--
-- specific section
--
-- Most item classes
quality = {
inherit = false,
field = 'quality',
type = 'Integer',
-- Must copy to stat for the stat adjustments to work properly
func = function (tpl_args, value)
local quality = tonumber(value)
if quality then
local stat = {
min = quality,
max = quality,
avg = quality,
}
core.stats_update(tpl_args, 'quality', stat, nil, '_stats')
if tpl_args.class_id == 'UtilityFlask' or tpl_args.class_id == 'UtilityFlaskCritical' then
core.stats_update(tpl_args, 'quality_flask_duration', stat, nil, '_stats')
-- quality is added to quantity for maps
elseif tpl_args.class_id == 'Map' then
core.stats_update(tpl_args, 'map_item_drop_quantity_+%', stat, nil, '_stats')
end
end
return quality
end,
default = 0,
},
-- amulets
is_talisman = {
field = 'is_talisman',
type = 'Boolean',
func = h.proc.boolean,
default = false,
},
talisman_tier = {
field = 'talisman_tier',
type = 'Integer',
func = h.proc.number,
},
-- flasks
charges_max = {
field = 'charges_max',
type = 'Integer',
func = h.proc.number,
},
charges_per_use = {
field = 'charges_per_use',
type = 'Integer',
func = h.proc.number,
},
flask_mana = {
field = 'mana',
type = 'Integer',
func = h.proc.number,
},
flask_life = {
field = 'life',
type = 'Integer',
func = h.proc.number,
},
flask_duration = {
field = 'duration',
type = 'Float',
func = h.proc.number,
},
buff_id = {
field = 'id',
type = 'String',
func = nil,
},
buff_values = {
field = 'buff_values',
type = 'List (,) of Integer',
func = function (tpl_args, value)
local values = {}
local i = 0
repeat
i = i + 1
local key = 'buff_value' .. i
values[i] = tonumber(tpl_args[key])
tpl_args[key] = nil
until values[i] == nil
-- needed so the values copyied from unique item base isn't overriden
if #values >= 1 then
value = values
end
return value
end,
func_copy = function (tpl_args, value)
tpl_args.buff_values = m_util.string.split(value, ',%s*')
end,
default = {},
},
buff_stat_text = {
field = 'stat_text',
type = 'String',
func = nil,
},
buff_icon = {
field = 'icon',
type = 'String',
func = function (tpl_args, value)
return string.format(i18n.files.status_icon, tpl_args.name)
end,
},
-- weapons
critical_strike_chance = {
field = 'critical_strike_chance',
type = 'Float',
func = h.proc.number,
},
attack_speed = {
field = 'attack_speed',
type = 'Float',
func = h.proc.number,
},
weapon_range = {
field = 'weapon_range',
type = 'Integer',
func = h.proc.number,
},
physical_damage_min = {
field = 'physical_damage_min',
type = 'Integer',
func = h.proc.number,
},
physical_damage_max = {
field = 'physical_damage_max',
type = 'Integer',
func = h.proc.number,
},
fire_damage_min = {
field = 'fire_damage_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
fire_damage_max = {
field = 'fire_damage_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
cold_damage_min = {
field = 'cold_damage_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
cold_damage_max = {
field = 'cold_damage_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
lightning_damage_min = {
field = 'lightning_damage_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
lightning_damage_max = {
field = 'lightning_damage_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
chaos_damage_min = {
field = 'chaos_damage_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
chaos_damage_max = {
field = 'chaos_damage_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
-- armor-type stuff
armour = {
inherit = false,
field = 'armour',
type = nil,
func = nil,
},
armour_min = {
field = 'armour_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
armour_max = {
field = 'armour_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
evasion = {
inherit = false,
field = 'evasion',
type = nil,
func = nil,
},
evasion_min = {
field = 'evasion_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
evasion_max = {
field = 'evasion_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
energy_shield = {
inherit = false,
field = 'energy_shield',
type = nil,
func = nil,
},
energy_shield_min = {
field = 'energy_shield_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
energy_shield_max = {
field = 'energy_shield_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
ward = {
inherit = false,
field = 'ward',
type = nil,
func = nil,
},
ward_min = {
field = 'ward_min',
type = 'Integer',
func = h.proc.number,
default = 0,
},
ward_max = {
field = 'ward_max',
type = 'Integer',
func = h.proc.number,
default = 0,
},
-- This is the inherent penality from the armour piece if any
movement_speed = {
field = 'movement_speed',
type = 'Integer',
func = h.proc.number,
default = 0,
},
-- shields
block = {
field = 'block',
type = 'Integer',
func = h.proc.number,
},
-- skill gem stuff
gem_description = {
field = 'gem_description',
type = 'Text',
func = h.proc.text,
},
dexterity_percent = {
field = 'dexterity_percent',
type = 'Integer',
func = h.proc.percentage,
},
strength_percent = {
field = 'strength_percent',
type = 'Integer',
func = h.proc.percentage,
},
intelligence_percent = {
field = 'intelligence_percent',
type = 'Integer',
func = h.proc.percentage,
},
primary_attribute = {
field = 'primary_attribute',
type = 'String',
func = function (tpl_args, value)
for _, attr in ipairs(m_game.constants.attribute_order) do
local val = tpl_args[attr .. '_percent']
if val and val >= 60 then
return attr
end
end
return 'none'
end,
},
gem_tags = {
field = 'gem_tags',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.item.gem_tags_lookup,
errmsg = i18n.errors.invalid_gem_tag,
errlvl = 4,
},
},
default = {},
},
-- Support gems only
support_gem_letter = {
field = 'support_gem_letter',
type = 'String',
func = h.proc.character,
},
support_gem_letter_html = {
field = 'support_gem_letter_html',
type = 'Text',
func = function (tpl_args, value)
if tpl_args.support_gem_letter == nil then
return nil
end
for k, v in pairs(m_game.constants.attributes) do
local key = string.format('%s_percent', k)
if tpl_args[key] and tpl_args[key] > 50 then
value = tostring(
mw.html.create('span')
:attr('class', string.format('support-gem-id-%s', v.color))
:wikitext(tpl_args.support_gem_letter)
)
break
end
end
return value
end,
},
--
-- Maps
--
map_tier = {
field = 'tier',
type = 'Integer',
func = h.proc.number,
},
map_guild_character = {
field = 'guild_character',
type = 'String',
func = h.proc.character,
},
map_area_id = {
field = 'area_id',
type = 'String',
func = nil, -- TODO: Validate against a query?
},
map_area_level = {
field = 'area_level',
type = 'Integer',
func = h.proc.number,
},
unique_map_guild_character = {
field = 'unique_guild_character',
type = 'String',
func = h.proc.character,
func_copy = function (tpl_args, value)
tpl_args.map_guild_character = value
end,
},
unique_map_area_id = {
field = 'unique_area_id',
type = 'String',
func = nil, -- TODO: Validate against a query?
func_copy = function (tpl_args, value)
tpl_args.map_area_id = value
end,
},
unique_map_area_level = {
field = 'unique_area_level',
type = 'Integer',
func = h.proc.number,
func_copy = function (tpl_args, value)
tpl_args.map_area_level = value
end,
},
map_series = {
field = 'series',
type = 'String',
func = function (tpl_args, value)
if tpl_args.rarity == 'normal' and value == nil then
error(string.format(i18n.errors.generic_required_parameter, 'map_series'))
end
return value
end,
},
-- atlas info is only for the current map series
atlas_x = {
field = 'x',
type = 'Float',
func = h.proc.number,
},
atlas_y = {
field = 'y',
type = 'Float',
func = h.proc.number,
},
atlas_region_id = {
field = 'region_id',
type = 'String',
func = nil,
},
atlas_region_minimum = {
field = 'region_minimum',
type = 'Integer',
func = h.proc.number,
},
atlas_x0 = {
field = 'x0',
type = 'Float',
func = h.proc.number,
},
atlas_x1 = {
field = 'x1',
type = 'Float',
func = h.proc.number,
},
atlas_x2 = {
field = 'x2',
type = 'Float',
func = h.proc.number,
},
atlas_x3 = {
field = 'x3',
type = 'Float',
func = h.proc.number,
},
atlas_x4 = {
field = 'x4',
type = 'Float',
func = h.proc.number,
},
atlas_y0 = {
field = 'y0',
type = 'Float',
func = h.proc.number,
},
atlas_y1 = {
field = 'y1',
type = 'Float',
func = h.proc.number,
},
atlas_y2 = {
field = 'y2',
type = 'Float',
func = h.proc.number,
},
atlas_y3 = {
field = 'y3',
type = 'Float',
func = h.proc.number,
},
atlas_y4 = {
field = 'y4',
type = 'Float',
func = h.proc.number,
},
atlas_map_tier0 = {
field = 'map_tier0',
type = 'Integer',
func = h.proc.number,
},
atlas_map_tier1 = {
field = 'map_tier1',
type = 'Integer',
func = h.proc.number,
},
atlas_map_tier2 = {
field = 'map_tier2',
type = 'Integer',
func = h.proc.number,
},
atlas_map_tier3 = {
field = 'map_tier3',
type = 'Integer',
func = h.proc.number,
},
atlas_map_tier4 = {
field = 'map_tier4',
type = 'Integer',
func = h.proc.number,
},
atlas_connections = {
field = nil,
type = nil,
func = function (tpl_args, value)
value = {}
local cont = true
local i = 1
while cont do
local prefix = string.format('atlas_connection%s_', i)
local regions = tpl_args[prefix .. 'tier']
local data = {
_table = 'atlas_connections',
map1 = string.format('%s (%s)', tpl_args.name, tpl_args.map_series or ''),
map2 = tpl_args[prefix .. 'target'],
}
if regions and data.map2 then
regions = m_util.string.split(regions, ',%s*')
if #regions ~= 5 then
error(string.format(i18n.errors.invalid_region_upgrade_count, i, #regions))
end
for index, value in ipairs(regions) do
data['region' .. (index - 1)] = m_util.cast.boolean(value)
end
value[data.map2] = data
table.insert(tpl_args._subobjects, data)
else
cont = false
if i == 1 then
value = nil
end
end
i = i + 1
end
return value
end,
},
--
-- Currency-like items
--
stack_size = {
field = 'stack_size',
type = 'Integer',
func = h.proc.number,
},
stack_size_currency_tab = {
field = 'stack_size_currency_tab',
type = 'Integer',
func = h.proc.number,
},
description = {
field = 'description',
type = 'Text',
func = h.proc.text,
},
cosmetic_type = {
field = 'cosmetic_type',
type = 'String',
func = h.proc.factory.value{
validate = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.item.cosmetic_item_types,
errmsg = i18n.errors.invalid_cosmetic_type,
},
},
},
cosmetic_theme = {
field = 'theme',
type = 'String',
func = h.proc.text,
},
-- for essences
is_essence = {
field = nil,
type = nil,
func = h.proc.boolean,
default = false,
},
essence_level_restriction = {
field = 'level_restriction',
type = 'Integer',
func = h.proc.number,
},
essence_level = {
field = 'level',
type = 'Integer',
func = h.proc.number,
},
essence_type = {
field = 'type',
type = 'Integer',
func = h.proc.number,
},
essence_category = {
field = 'category',
type = 'String',
func = nil,
},
-- blight crafting items (i.e. oils)
blight_item_tier = {
field = 'tier',
type = 'Integer',
func = h.proc.number,
},
-- harvest seeds
seed_type_id = {
field = 'type_id',
type = 'String',
func = nil,
},
seed_type = {
field = 'type',
type = 'String',
func = function (tpl_args, value)
if tpl_args.seed_type_id ~= 'none' or tpl_args.seed_type_id ~= nil then
value = m_game.seed_types[tpl_args.seed_type_id]
end
return value
end
},
seed_type_html = {
field = nil,
type = nil,
func = function (tpl_args, value)
if tpl_args.seed_type ~= nil then
value = m_util.html.poe_color(tpl_args.seed_type_id, tpl_args.seed_type)
end
return value
end
},
seed_effect = {
field = 'effect',
type = 'Text',
func = nil,
},
seed_tier = {
field = 'tier',
type = 'Integer',
func = h.proc.number,
},
seed_growth_cycles = {
field = 'growth_cycles',
type = 'Integer',
func = h.proc.number,
},
seed_required_nearby_seed_tier = {
field = 'required_nearby_seed_tier',
type = 'Integer',
func = h.proc.number,
},
seed_required_nearby_seed_amount = {
field = 'required_nearby_seed_amount',
type = 'Integer',
func = h.proc.number,
},
seed_consumed_wild_lifeforce_percentage = {
field = 'consumed_wild_lifeforce_percentage',
type = 'Integer',
func = h.proc.number,
default = 0,
},
seed_consumed_vivid_lifeforce_percentage = {
field = 'consumed_vivid_lifeforce_percentage',
type = 'Integer',
func = h.proc.number,
default = 0,
},
seed_consumed_primal_lifeforce_percentage = {
field = 'consumed_primal_lifeforce_percentage',
type = 'Integer',
func = h.proc.number,
default = 0,
},
seed_granted_craft_option_ids = {
field = 'granted_craft_option_ids',
type = 'List (,) of String',
func = h.proc.list,
default = {},
},
--
-- harvest planet boosters
--
plant_booster_radius = {
field = 'radius',
type = 'Integer',
func = h.proc.number,
},
plant_booster_lifeforce = {
field = 'lifeforce',
type = 'Integer',
func = h.proc.number,
},
plant_booster_additional_crafting_options = {
field = 'additional_crafting_options',
type = 'Integer',
func = h.proc.number,
},
plant_booster_extra_chances = {
field = 'extra_chances',
type = 'Integer',
func = h.proc.number,
},
--
-- Heist properties
--
heist_required_job_id = {
field = 'required_job_id',
type = 'String',
func = h.proc.text,
},
heist_required_job_level = {
field = 'required_job_level',
type = 'Integer',
func = h.proc.number,
},
heist_data = {
field = nil,
type = nil,
func = function (tpl_args, value)
if tpl_args.heist_required_job_level then
if tpl_args.heist_required_job_id then
local results = m_cargo.query(
{'heist_jobs', 'heist_npcs', 'heist_npc_skills'},
{'heist_npcs.name', 'heist_jobs.name'},
{
join = 'heist_npc_skills.job_id=heist_jobs.id, heist_npc_skills.npc_id=heist_npcs.id',
where = string.format('heist_npc_skills.job_id = "%s" AND heist_npc_skills.level >= %s', tpl_args.heist_required_job_id, tpl_args.heist_required_job_level),
}
)
local npcs = {}
for _, row in ipairs(results) do
npcs[#npcs+1] = row['heist_npcs.name']
end
tpl_args.heist_required_npcs = table.concat(npcs, ', ')
tpl_args.heist_required_job = results[1]['heist_jobs.name']
else
tpl_args.heist_required_job = i18n.tooltips.heist_any_job
end
end
return value
end,
},
--
-- hideout doodads (HideoutDoodads.dat)
--
is_master_doodad = {
field = 'is_master_doodad',
type = 'Boolean',
func = h.proc.boolean,
},
variation_count = {
field = 'variation_count',
type = 'Integer',
func = h.proc.number,
},
-- Propehcy
prophecy_id = {
field = 'prophecy_id',
type = 'String',
func = nil,
},
prediction_text = {
field = 'prediction_text',
type = 'Text',
func = h.proc.text,
},
seal_cost = {
field = 'seal_cost',
type = 'Integer',
func = h.proc.number,
},
prophecy_reward = {
field = 'reward',
type = 'Text',
func = h.proc.text,
},
prophecy_objective = {
field = 'objective',
type = 'Text',
func = h.proc.text,
},
-- Divination cards
card_art = {
field = 'card_art',
type = 'Page',
func = function (tpl_args, value)
return string.format(i18n.files.divination_card_art, value or tpl_args.name)
end,
},
--
-- Sentinels
--
sentinel_duration = {
field = 'duration',
type = 'Integer',
func = h.proc.number,
},
sentinel_empowers = {
field = 'empowers',
type = 'Integer',
func = h.proc.number,
},
sentinel_empowerment = {
field = 'empowerment',
type = 'Integer',
func = h.proc.number,
},
sentinel_charge = {
field = 'charge',
type = 'Integer',
func = h.proc.number,
},
sentinel_monster = {
inherit = false,
field = 'monster',
type = 'String',
func = nil,
},
sentinel_monster_level = {
inherit = false,
field = 'monster_level',
type = 'Integer',
func = h.proc.number,
},
-- ------------------------------------------------------------------------
-- derived stats
-- ------------------------------------------------------------------------
-- For rarity != normal, rarity already verified
base_item = {
inherit = false,
field = 'base_item',
type = 'String',
func = function (tpl_args, value)
return tpl_args.base_item_data['items.name']
end,
},
base_item_id = {
inherit = false,
field = 'base_item_id',
type = 'String',
func = function (tpl_args, value)
return tpl_args.base_item_data['items.metadata_id']
end,
},
base_item_page = {
inherit = false,
field = 'base_item_page',
type = 'Page',
func = function (tpl_args, value)
return tpl_args.base_item_data['items._pageName']
end,
},
name_list = {
inherit = false,
field = 'name_list',
type = 'List (�) of String',
func = function (tpl_args, value)
value = m_util.cast.table(value)
value[#value+1] = tpl_args.name
return value
end,
default = {},
},
frame_type = {
inherit = false,
field = 'frame_type',
type = 'String',
func = function (tpl_args, value)
if value then
return value
end
if tpl_args._flags.is_prophecy then
return 'prophecy'
end
local var = cfg.class_specifics[tpl_args.class_id]
if var ~= nil and var.frame_type ~= nil then
return var.frame_type
end
if tpl_args.is_relic then
return 'relic'
end
return tpl_args.rarity_id
end,
},
--
-- args populated by mod validation
--
mods = {
field = nil,
type = nil,
func = nil,
default = {},
func_fetch = function (tpl_args)
-- Fetch implicit mods from base item
local results = m_cargo.query(
{'items' ,'item_mods'},
{'item_mods.id', 'item_mods.is_implicit', 'item_mods.is_random', 'item_mods.text'},
{
join = 'items._pageID=item_mods._pageID',
where = string.format('items._pageName="%s" AND item_mods.is_implicit=1', tpl_args.base_item_page),
}
)
for _, row in ipairs(results) do
-- Handle text-only mods
local result
if row['item_mods.id'] == nil then
result = row['item_mods.text']
end
tpl_args._base_implicit_mods[#tpl_args._base_implicit_mods+1] = {
result=result,
id=row['item_mods.id'],
stat_text=row['item_mods.text'],
is_implicit=m_util.cast.boolean(row['item_mods.is_implicit']),
is_random=m_util.cast.boolean(row['item_mods.is_random']),
}
end
end,
},
physical_damage_html = {
inherit = false,
field = 'physical_damage_html',
type = 'Text',
func = h.proc.factory.damage_html{type = 'physical'},
},
fire_damage_html = {
inherit = false,
field = 'fire_damage_html',
type = 'Text',
func = h.proc.factory.damage_html{type = 'fire'},
},
cold_damage_html = {
inherit = false,
field = 'cold_damage_html',
type = 'Text',
func = h.proc.factory.damage_html{type = 'cold'},
},
lightning_damage_html = {
inherit = false,
field = 'lightning_damage_html',
type = 'Text',
func = h.proc.factory.damage_html{type = 'lightning'},
},
chaos_damage_html = {
inherit = false,
field = 'chaos_damage_html',
type = 'Text',
func = h.proc.factory.damage_html{type = 'chaos'},
},
damage_avg = {
inherit = false,
field = 'damage_avg',
type = 'Text',
func = function (tpl_args, value)
local dmg = {min=0, max=0}
for key, _ in pairs(dmg) do
for _, dkey in ipairs(m_game.constants.damage_type_order) do
dmg[key] = dmg[key] + tpl_args[string.format('%s_damage_%s_range_average', dkey, key)]
end
end
dmg = (dmg.min + dmg.max) / 2
return dmg
end,
},
damage_html = {
inherit = false,
field = 'damage_html',
type = 'Text',
func = function (tpl_args, value)
local text = {}
for _, dkey in ipairs(m_game.constants.damage_type_order) do
local range = tpl_args[dkey .. '_damage_html']
if range ~= nil then
text[#text+1] = range
end
end
if #text > 0 then
value = table.concat(text, '<br>')
end
return value
end,
},
item_limit = {
inherit = false,
field = 'item_limit',
type = 'Integer',
func = h.proc.number,
},
jewel_radius_html = {
inherit = false,
field = 'radius_html',
type = 'Text',
func = function (tpl_args, value)
-- Get radius from stats
local radius = tpl_args._stats.local_jewel_effect_base_radius
if radius then
radius = radius.min
local size = m_game.constants.item.jewel_radius_to_size[radius] or radius
local color = radius == 0 and 'mod' or 'value'
value = m_util.html.poe_color(color, size)
end
return value
end,
},
drop_areas_html = {
inherit = false,
field = 'drop_areas_html',
type = 'Text',
func = function (tpl_args, value)
if tpl_args._drop_areas_data == nil then
return value
end
if value ~= nil then
return value
end
local areas = {}
for _, data in pairs(tpl_args._drop_areas_data) do
-- skip legacy maps in the drop html listing
if not string.match(data.id, '^Map.+') or string.match(data.id, '^MapWorlds.+') or string.match(data.id, '^MapAtziri.+') then
areas[#areas+1] = string.format('[[%s|%s]]', data.main_page or data._pageName, data.main_page or data.name)
end
end
return table.concat(areas, ' • ')
end,
},
release_version = {
inherit = false,
field = 'release_version',
type = 'String',
func = nil,
},
removal_version = {
inherit = false,
field = 'removal_version',
type = 'String',
func = nil,
},
--
-- args governing use of the template itself
--
suppress_improper_modifiers_category = {
inherit = false,
field = nil,
func = h.proc.boolean,
default = false,
},
disable_automatic_recipes = {
inherit = false,
field = nil,
func = h.proc.boolean,
default = false,
},
}
core.stat_map = {
required_level_final = {
field = 'required_level',
stats_add = {
'local_level_requirement_+',
},
stats_override = {
['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=1, max=1},
},
minimum = 1,
html_fmt_options = {
fmt = '%i',
},
},
weapon_range = {
field = 'weapon_range',
stats_add = {
'local_weapon_range_+',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
physical_damage_min = {
field = 'physical_damage_min',
stats_add = {
'local_minimum_added_physical_damage',
},
stats_increased = {
'local_physical_damage_+%',
'quality',
},
stats_override = {
['local_weapon_no_physical_damage'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
physical_damage_max = {
field = 'physical_damage_max',
stats_add = {
'local_maximum_added_physical_damage',
},
stats_increased = {
'local_physical_damage_+%',
'quality',
},
stats_override = {
['local_weapon_no_physical_damage'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
fire_damage_min = {
field = 'fire_damage_min',
stats_add = {
'local_minimum_added_fire_damage',
},
minimum = 0,
html_fmt_options = {
color = 'fire',
fmt = '%i',
},
},
fire_damage_max = {
field = 'fire_damage_max',
stats_add = {
'local_maximum_added_fire_damage',
},
minimum = 0,
html_fmt_options = {
color = 'fire',
fmt = '%i',
},
},
cold_damage_min = {
field = 'cold_damage_min',
stats_add = {
'local_minimum_added_cold_damage',
},
minimum = 0,
html_fmt_options = {
color = 'cold',
fmt = '%i',
},
},
cold_damage_max = {
field = 'cold_damage_max',
stats_add = {
'local_maximum_added_cold_damage',
},
minimum = 0,
html_fmt_options = {
color = 'cold',
fmt = '%i',
},
},
lightning_damage_min = {
field = 'lightning_damage_min',
stats_add = {
'local_minimum_added_lightning_damage',
},
minimum = 0,
html_fmt_options = {
color = 'lightning',
fmt = '%i',
},
},
lightning_damage_max = {
field = 'lightning_damage_max',
stats_add = {
'local_maximum_added_lightning_damage',
},
minimum = 0,
html_fmt_options = {
color = 'lightning',
fmt = '%i',
},
},
chaos_damage_min = {
field = 'chaos_damage_min',
stats_add = {
'local_minimum_added_chaos_damage',
},
minimum = 0,
html_fmt_options = {
color = 'chaos',
fmt = '%i',
},
},
chaos_damage_max = {
field = 'chaos_damage_max',
stats_add = {
'local_maximum_added_chaos_damage',
},
minimum = 0,
html_fmt_options = {
color = 'chaos',
fmt = '%i',
},
},
critical_strike_chance = {
field = 'critical_strike_chance',
stats_add = {
'local_critical_strike_chance',
},
stats_increased = {
'local_critical_strike_chance_+%',
},
stats_override = {
['local_weapon_crit_chance_is_100'] = {min=100, max=100},
},
minimum = 0,
html_fmt_options = {
fmt = '%.2f%%',
},
},
attack_speed = {
field = 'attack_speed',
stats_increased = {
'local_attack_speed_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%.2f',
},
},
flask_life = {
field = 'life',
stats_add = {
'local_flask_life_to_recover',
},
stats_increased = {
'local_flask_life_to_recover_+%',
'local_flask_amount_to_recover_+%',
'quality',
},
html_fmt_options = {
fmt = '%i',
},
},
flask_mana = {
field = 'mana',
stats_add = {
'local_flask_mana_to_recover',
},
stats_increased = {
'local_flask_mana_to_recover_+%',
'local_flask_amount_to_recover_+%',
'quality',
},
},
flask_duration = {
field = 'duration',
stats_increased = {
'local_flask_duration_+%',
-- regular quality isn't used here because it doesn't increase duration of life/mana/hybrid flasks
'quality_flask_duration',
},
stats_increased_inverse = {
'local_flask_recovery_speed_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%.2f',
},
},
charges_per_use = {
field = 'charges_per_use',
stats_increased = {
'local_charges_used_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
charges_max = {
field = 'charges_max',
stats_add = {
'local_extra_max_charges',
},
stats_increased = {
'local_max_charges_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
armour = {
arg = {
min = 'armour_min',
max = 'armour_max',
},
field = 'armour',
stats_add = {
'local_base_physical_damage_reduction_rating',
},
stats_increased = {
'local_physical_damage_reduction_rating_+%',
'local_armour_and_energy_shield_+%',
'local_armour_and_evasion_+%',
'local_armour_and_evasion_and_energy_shield_+%',
'quality',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
evasion = {
arg = {
min = 'evasion_min',
max = 'evasion_max',
},
field = 'evasion',
stats_add = {
'local_base_evasion_rating',
'local_evasion_rating_and_energy_shield',
},
stats_increased = {
'local_evasion_rating_+%',
'local_evasion_and_energy_shield_+%',
'local_armour_and_evasion_+%',
'local_armour_and_evasion_and_energy_shield_+%',
'quality',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
energy_shield = {
arg = {
min = 'energy_shield_min',
max = 'energy_shield_max',
},
field = 'energy_shield',
stats_add = {
'local_energy_shield',
'local_evasion_rating_and_energy_shield',
},
stats_increased = {
'local_energy_shield_+%',
'local_armour_and_energy_shield_+%',
'local_evasion_and_energy_shield_+%',
'local_armour_and_evasion_and_energy_shield_+%',
'quality',
},
stats_override = {
['local_no_energy_shield'] = {min=0, max=0},
['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
ward = {
arg = {
min = 'ward_min',
max = 'ward_max',
},
field = 'ward',
stats_add = {
'local_ward',
},
stats_increased = {
'local_ward_+%',
'quality',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
block = {
field = 'block',
stats_add = {
'local_additional_block_chance_%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i%%',
},
},
required_dexterity = {
field = 'required_dexterity',
stats_add = {
'local_dexterity_requirement_+'
},
stats_increased = {
'local_dexterity_requirement_+%',
'local_attribute_requirements_+%',
},
stats_override = {
['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
['local_no_attribute_requirements'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
required_intelligence = {
field = 'required_intelligence',
stats_add = {
'local_intelligence_requirement_+'
},
stats_increased = {
'local_intelligence_requirement_+%',
'local_attribute_requirements_+%',
},
stats_override = {
['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
['local_no_attribute_requirements'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
required_strength = {
field = 'required_strength',
stats_add = {
'local_strength_requirement_+'
},
stats_increased = {
'local_strength_requirement_+%',
'local_attribute_requirements_+%',
},
stats_override = {
['local_unique_tabula_rasa_no_requirement_or_energy_shield'] = {min=0, max=0},
['local_no_attribute_requirements'] = {min=0, max=0},
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
map_area_level = {
field = 'map_area_level',
stats_override = {
['map_item_level_override'] = true,
},
},
sentinel_duration = {
field = 'duration',
stats_add = {
'local_sentinel_duration_+',
},
stats_increased = {
'local_sentinel_drone_duration_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
sentinel_empowers = {
field = 'empowers',
stats_add = {
'local_sentinel_tag_limit_+',
},
stats_increased = {
'local_sentinel_tag_limit_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
sentinel_empowerment = {
field = 'empowerment',
stats_add = {
'local_sentinel_drone_difficulty_+',
},
stats_increased = {
'local_sentinel_drone_difficulty_+%',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
sentinel_charge = {
field = 'charge',
stats_add = {
'local_sentinel_drone_charge_+',
},
minimum = 0,
html_fmt_options = {
fmt = '%i',
},
},
}
core.dps_map = {
physical_dps = {
field = 'physical_dps',
damage_args = {'physical_damage'},
label_infobox = i18n.tooltips.physical_dps,
html_fmt_options = {
color = 'value',
fmt = '%.1f',
},
},
fire_dps = {
field = 'fire_dps',
damage_args = {'fire_damage'},
label_infobox = i18n.tooltips.fire_dps,
html_fmt_options = {
color = 'fire',
fmt = '%.1f',
},
},
cold_dps = {
field = 'cold_dps',
damage_args = {'cold_damage'},
label_infobox = i18n.tooltips.cold_dps,
html_fmt_options = {
color = 'cold',
fmt = '%.1f',
},
},
lightning_dps = {
field = 'lightning_dps',
damage_args = {'lightning_damage'},
label_infobox = i18n.tooltips.lightning_dps,
html_fmt_options = {
color = 'lightning',
fmt = '%.1f',
},
},
chaos_dps = {
field = 'chaos_dps',
damage_args = {'chaos_damage'},
label_infobox = i18n.tooltips.chaos_dps,
html_fmt_options = {
color = 'chaos',
fmt = '%.1f',
},
},
elemental_dps = {
field = 'elemental_dps',
damage_args = {'fire_damage', 'cold_damage', 'lightning_damage'},
label_infobox = i18n.tooltips.elemental_dps,
html_fmt_options = {
color = 'value',
fmt = '%.1f',
},
},
poison_dps = {
field = 'poison_dps',
damage_args = {'physical_damage', 'chaos_damage'},
label_infobox = i18n.tooltips.poison_dps,
html_fmt_options = {
color = 'value',
fmt = '%.1f',
},
},
dps = {
field = 'dps',
damage_args = {'physical_damage', 'fire_damage', 'cold_damage', 'lightning_damage', 'chaos_damage'},
label_infobox = i18n.tooltips.dps,
html_fmt_options = {
color = 'value',
fmt = '%.1f',
},
},
}
return core