Módulo:Item/sandbox
Aspeto
A documentação para este módulo pode ser criada em Módulo:Item/sandbox/doc
-------------------------------------------------------------------------------
--
-- Module:Item
--
-- This module implements Template:Item.
-------------------------------------------------------------------------------
_pageName='{{#replace:{{PAGENAME}}|'|\'}}'
require('Module:No globals')
local m_util = require('Module:Util')
local m_item_util -- Lazy load require('Module:Item util')
local m_cargo = require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Item')
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game')
-- Lazy loading
local f_process_skill_data -- require('Module:Skill').process_skill_data
local f_item_link -- require('Module:Item link').item_link
local f_query_area_info -- require('Module:Area').query_area_info
local f_process_recipes -- require('Module:Item/recipes').process_recipes
local f_append_schema -- require('Module:Item/cargo').append_schema
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Item/config/sandbox') or mw.loadData('Module:Item/config')
local i18n = cfg.i18n
local core = use_sandbox and require('Module:Item/core/sandbox') or require('Module:Item/core')
-- Declare early to avoid errors
local c = {}
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
-- Lazy loading for Module:Skill
function h.process_skill_data(tpl_args)
if not f_process_skill_data then
f_process_skill_data = use_sandbox and require('Module:Skill/sandbox').process_skill_data or require('Module:Skill').process_skill_data
end
return f_process_skill_data(tpl_args)
end
-- Lazy loading for Module:Item link
function h.item_link(args)
if not f_item_link then
f_item_link = require('Module:Item link').item_link
end
return f_item_link(args)
end
-- Lazy loading for Module:Area
function h.query_area_info(args)
if not f_query_area_info then
f_query_area_info = require('Module:Area').query_area_info
end
return f_query_area_info(args)
end
-- Lazy loading for Module:Item/cargo
function h.append_schema(tpl_args, tables)
if not f_append_schema then
f_append_schema = use_sandbox and require('Module:Item/cargo/sandbox').append_schema or require('Module:Item/cargo').append_schema
end
return f_append_schema(tpl_args, tables)
end
-- Lazy loading for Module:Item/recipes
function h.process_recipes(tpl_args)
if not f_process_recipes then
f_process_recipes = use_sandbox and require('Module:Item/recipes/sandbox').process_recipes or require('Module:Item/recipes').process_recipes
end
return f_process_recipes(tpl_args)
end
function h.handle_range_args(tpl_args, key, field, value, options)
local opt = {}
for k, v in pairs(options or {}) do
opt[k] = v
end
local html, val2 = m_util.html.format_value(tpl_args, value, opt)
tpl_args[key .. '_html'] = html
tpl_args[field .. '_html'] = html
tpl_args[field .. '_range_colour'] = val2.color
opt.color = false
tpl_args[field .. '_range_text'] = m_util.html.format_value(tpl_args, value, opt)
end
h.stat = {}
function h.stat.add(value, magnitude)
value.min = value.min + magnitude.min
value.max = value.max + magnitude.max
end
function h.stat.add_distance(value, magnitude)
-- Stats for distance are 10x the display value
value.min = value.min + (magnitude.min / 10)
value.max = value.max + (magnitude.max / 10)
end
function h.stat.increased(value, magnitude)
value.min = value.min * (1 + magnitude.min / 100)
value.max = value.max * (1 + magnitude.max / 100)
end
function h.stat.increased_inverse(value, magnitude)
value.min = value.min / (1 + magnitude.min / 100)
value.max = value.max / (1 + magnitude.max / 100)
end
--
-- Processing
--
function h.process_arguments(tpl_args, params)
tpl_args._processed_args = tpl_args._processed_args or {}
tpl_args._base_item_args = tpl_args._base_item_args or {}
for _, k in ipairs(params) do
local arg_def = core.map[k]
if arg_def == nil then
if tpl_args.debug then
error(string.format(i18n.debug.invalid_argument_key, k))
end
else
if arg_def.inherit ~= false then
table.insert(tpl_args._base_item_args, k)
end
if tpl_args[k] ~= nil and arg_def.deprecated == true then
tpl_args._flags.uses_deprecated_parameters = true
tpl_args._deprecated_args = tpl_args._deprecated_args or {}
table.insert(tpl_args._deprecated_args, k)
end
if arg_def.func ~= nil then
tpl_args[k] = arg_def.func(tpl_args, tpl_args[k])
end
if tpl_args[k] == nil then
if tpl_args.class_id and tpl_args._item_config.defaults[k] ~= nil and (not tpl_args._flags.is_derived or arg_def.inherit == false) then
-- Defaults based on item class
-- Ignore inherited fields if this is a derived item; these are set by the base item instead
tpl_args[k] = tpl_args._item_config.defaults[k]
elseif arg_def.default ~= nil then
-- General defaults
if type(arg_def.default) == 'function' then
tpl_args[k] = arg_def.default(tpl_args)
else
tpl_args[k] = arg_def.default
end
end
end
tpl_args._processed_args[k] = true
end
end
end
--
-- Display
--
function h.strip_random_stats(tpl_args, stat_text)
if tpl_args._flags.random_mods then
repeat
local text = string.match(stat_text, '<th class="mw%-customtoggle%-31">(.+)</th>')
if text ~= nil then
stat_text = string.gsub(stat_text, '<table class="random%-modifier%-stats.+</table>', text, 1)
end
until text == nil
end
return stat_text
end
function h.add_to_infobox_from_map(tpl_args, infobox, mapping)
local statcont = mw.html.create('span')
:addClass('item-stats')
local count = 0 -- Number of groups in infobox
for _, group in ipairs(mapping) do
local lines = {}
for _, line in ipairs(group) do
local show = true
if line.show == false then
show = false
elseif type(line.show) == 'function' then
show = line.show(tpl_args)
end
if show then
lines[#lines+1] = line.func(tpl_args)
end
end
if #lines > 0 then
count = count + 1
local heading = ''
if group.heading == nil then
elseif type(group.heading) == 'function' then
heading = group.heading()
else
heading = string.format('<em class="header">%s</em><br>', group.heading)
end
statcont
:tag('span')
:addClass('group ' .. (group.class or ''))
:wikitext(heading .. table.concat(lines, '<br>'))
:done()
end
end
-- Add groups to infobox
if count > 0 then
infobox:node(statcont)
end
end
function h.make_main_infobox(tpl_args)
local infobox = mw.html.create('span')
:addClass('item-box -' .. tpl_args.frame_type)
if tpl_args.class_id == 'DivinationCard' then
local divcard = infobox
:tag('span')
:addClass('divicard-wrapper')
local card_background = tpl_args.card_background or {}
if #card_background > 0 then
divcard
:tag('span')
:addClass('divicard-background')
:wikitext(string.format(
'[[%s|390px|gif]]',
string.format(
i18n.files.divination_card_background,
table.concat(card_background, '-')
)
))
end
divcard
:tag('span')
:addClass('divicard-art')
:wikitext(string.format('[[%s|link=|alt=]]', tpl_args.card_art))
:done()
:tag('span')
:addClass('divicard-frame')
:done()
:tag('span')
:addClass('divicard-header')
:wikitext(tpl_args.name)
:done()
:tag('span')
:addClass('divicard-artlink')
:wikitext(string.format('[[%s]]', tpl_args.card_art))
:done()
:tag('span')
:addClass('divicard-stack')
:wikitext(tpl_args.stack_size)
:done()
:tag('span')
:addClass('divicard-face')
:tag('span')
:addClass('divicard-reward')
:wikitext(tpl_args.description)
:done()
:tag('span')
:addClass('divicard-flavour')
:wikitext(tpl_args.flavour_text)
:done()
else
local line_type
local name_line
if tpl_args._flags.is_derived and tpl_args.rarity_id ~= 'normal' then
line_type = 'double'
name_line = tpl_args.name .. '<br>' .. tpl_args.base_item
else
line_type = 'single'
name_line = tpl_args.name
end
-- Symbols - These are displayed in the item box header to indicate certain flags and/or item influences
local symbols
local influences = tpl_args.influences or {}
if tpl_args.is_replica then
symbols = {'replica', 'replica'}
if #influences > 0 then
symbols[2] = influences[1]
end
elseif #influences > 0 then
symbols = {influences[1], influences[1]}
if #influences > 1 then
symbols[2] = influences[2]
end
elseif tpl_args.is_fractured then
symbols = {'fractured', 'fractured'}
elseif tpl_args.is_synthesised then
symbols = {'synthesised', 'synthesised'}
elseif tpl_args.is_searing_exarch_item or tpl_args.is_eater_of_worlds_item then
symbols = {
tpl_args.is_searing_exarch_item and 'searingexarch' or 'eaterofworlds',
tpl_args.is_eater_of_worlds_item and 'eaterofworlds' or 'searingexarch'
}
elseif tpl_args.is_veiled then
symbols = {'veiled', 'veiled'}
end
infobox
:tag('span')
:addClass('header -' .. line_type)
:tag('span')
:addClass('symbol')
:addClass(symbols and '-' .. symbols[1] or nil)
:done()
:wikitext(name_line)
:tag('span')
:addClass('symbol')
:addClass(symbols and '-' .. symbols[2] or nil)
:done()
:done()
h.add_to_infobox_from_map(tpl_args, infobox, c.item_infobox_groups)
end
if tpl_args.skill_icon ~= nil then
infobox:wikitext(string.format('[[%s]]', tpl_args.skill_icon))
end
return infobox
end
--
-- Factory
--
h.factory = {}
function h.factory.maybe_show_infobox_line(args)
-- Verifies that a list of keys are present in tpl_args and that the item
-- should use those keys. Returns true if all conditions are met, otherwise
-- returns false.
return function (tpl_args)
local keys = args.keys or {}
local conditions = args.conditions or {}
if #keys == 0 then
return false
end
for _, k in ipairs(keys) do
if tpl_args[k] == nil then
return false
end
local t = type(tpl_args[k])
if (t == 'table' or t == 'string') and #tpl_args[k] == 0 then
return false
end
if conditions.processed == true and tpl_args._processed_args[k] == nil then
return false
end
end
return true
end
end
function h.factory.display_raw_value(key)
return function(tpl_args)
return tpl_args[key]
end
end
function h.factory.descriptor_value(args)
-- Arguments:
-- key
-- tbl
args = args or {}
return function (tpl_args, value)
args.tbl = args.tbl or tpl_args
if args.tbl[args.key] then
value = m_util.html.abbr(value, args.tbl[args.key])
end
return value
end
end
-- ----------------------------------------------------------------------------
-- Additional configuration
-- ----------------------------------------------------------------------------
-- helper to loop over the range variables easier
c.range_map = {
min = '_range_minimum',
max = '_range_maximum',
avg = '_range_average',
}
--
-- Contents here are meant to resemble the ingame infobox of items
--
c.item_infobox_groups = {
-- [n]:
-- class: Additional css class added to group tag
-- heading: Group heading text (used for extras)
-- lines:
-- [n]:
-- show: Show line if this function returns true; Always show if boolean true. Default: Always show
-- func: Function that returns line text
{
-- Item type (weapons, hideout decorations, abyss jewels)
{
show = function (tpl_args)
return i18n.item_class_map[tpl_args.class_id] ~= nil
end,
func = function (tpl_args)
return i18n.item_class_map[tpl_args.class_id]
end,
},
-- Cosmetic item type
{
show = h.factory.maybe_show_infobox_line{
keys = {'cosmetic_type'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('cosmetic_type'),
},
-- Gems
{
show = h.factory.maybe_show_infobox_line{
keys = {'gem_tags'},
conditions = {processed = true},
},
func = function (tpl_args)
local out = {}
for i, tag in ipairs(tpl_args.gem_tags) do
out[#out+1] = string.format(i18n.gem_tag_category, tag, tag)
end
return table.concat(out, ', ')
end,
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'gem_tier'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'gem_tier',
hide_default = 0,
fmt = '%i',
},
},
fmt = i18n.tooltips.tier,
},
},
{
show = function (tpl_args)
return tpl_args.skill_levels and cfg.class_groups.gems.keys[tpl_args.class_id]
end,
func = function (tpl_args)
local value = {
base = 1,
min = 1,
max = tpl_args.max_level,
}
local options = {
fmt = '%i',
color = 'value',
}
return string.format(
i18n.tooltips.level,
m_util.html.format_value(tpl_args, value, options)
)
end,
},
{
show = function (tpl_args)
return tpl_args.skill_levels and cfg.class_groups.gems.keys[tpl_args.class_id]
end,
func = function (tpl_args)
local parts = {}
for k, v in pairs(m_game.constants.skill.cost_types) do
parts[#parts+1] = {
key = 'cost_' .. k,
func = function (tpl_args, value)
if string.find(k, 'PerMinute', 1, true) then
value = value / 60 -- Cost per second
end
return value
end,
fmt = '%i',
inline = string.format(
'%%s%s %s',
string.find(k, 'Percent', 1, true) and '%%' or '',
v.long_upper
),
}
end
return core.factory.infobox_line{
type = 'gem',
parts = parts,
sep = ', ',
fmt = i18n.tooltips.cost,
}(tpl_args)
end,
},
{
show = function (tpl_args)
return tpl_args.skill_levels and cfg.class_groups.gems.keys[tpl_args.class_id]
end,
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'mana_reservation_flat',
fmt = '%i',
inline = '%s ' .. m_game.constants.skill.cost_types['Mana'].long_upper,
},
{
key = 'mana_reservation_percent',
fmt = '%i',
inline = '%s%% ' .. m_game.constants.skill.cost_types['Mana'].long_upper,
},
{
key = 'life_reservation_flat',
fmt = '%i',
inline = '%s ' .. m_game.constants.skill.cost_types['Life'].long_upper,
},
{
key = 'life_reservation_percent',
fmt = '%i',
inline = '%s%% ' .. m_game.constants.skill.cost_types['Life'].long_upper,
},
{
key = 'spirit_reservation_flat',
fmt = '%i',
inline = '%s ' .. m_game.constants.skill.cost_types['Spirit'].long_upper,
},
},
sep = ', ',
fmt = i18n.tooltips.reservation,
},
},
{
show = true, -- TODO: Show only if has cost_multiplier
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'cost_multiplier',
hide_default = 100,
fmt = '%i',
inline = '%s%%',
},
},
fmt = i18n.tooltips.cost_multiplier,
},
},
{
show = true, -- TODO: Show only if has cooldown
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'cooldown',
hide_default = 0,
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
},
fmt = i18n.tooltips.cooldown_time,
},
},
{
-- TODO: Combine with cooldown. Multi-use non-vaal skills display uses together with cooldown time. E.g., Cooldown Time: 8.00 sec (3 uses)
show = true,
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'stored_uses',
hide_default = 0,
fmt = '%i',
},
},
fmt = i18n.tooltips.stored_uses,
},
},
{
show = true, -- TODO: Show only if has vaal_souls_requirement
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'vaal_souls_requirement',
hide_default = 0,
fmt = '%i',
},
},
fmt = i18n.tooltips.vaal_souls_per_use,
},
},
{
show = true, -- TODO: Show only if has vaal_stored_uses
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'vaal_stored_uses',
hide_default = 0,
fmt = '%i',
},
},
fmt = i18n.tooltips.stored_uses, -- TODO: Singular or plural based on number
},
},
{
show = true, -- TODO: Show only if has vaal_soul_gain_prevention_time
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'vaal_soul_gain_prevention_time',
hide_default = 0,
-- Technically it rounds to nearest, but it is given in milliseconds in the data,
fmt = '%i ' .. m_game.units.seconds.short_lower,
},
},
fmt = i18n.tooltips.vaal_soul_gain_prevention_time,
},
},
{
show = function (tpl_args)
return tpl_args.cast_time and tpl_args.gem_tags and (m_util.table.contains(tpl_args.gem_tags, m_game.constants.item.gem_tags.spell.tag) or m_util.table.contains(tpl_args.gem_tags, m_game.constants.item.gem_tags.warcry.tag))
end,
func = function (tpl_args)
return core.factory.infobox_line{
parts = {
{
key = 'cast_time',
fmt = function (tpl_args, value)
if value.min == 0 then
return i18n.tooltips.instant_cast_time
end
return '%.2f ' .. m_game.units.seconds.short_lower
end,
},
},
fmt = m_util.table.contains(tpl_args.gem_tags, m_game.constants.item.gem_tags.spell.tag) and i18n.tooltips.cast_time or i18n.tooltips.use_time,
}(tpl_args)
end,
},
{
show = true, -- TODO: Show only if has attack_time
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'attack_time',
hide_default = 0,
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
},
fmt = i18n.tooltips.attack_time,
},
},
{
show = true, -- TODO: Show only if has critical_strike_chance
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'critical_strike_chance',
hide_default = 0,
fmt = '%.2f%%',
},
},
fmt = i18n.tooltips.critical_strike_chance,
},
},
{
show = true, -- TODO: Show only if has attack_speed_multiplier
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'attack_speed_multiplier',
hide_default = 100,
fmt = '%i',
inline = '%s%% ' .. i18n.tooltips.of_base_stat,
},
},
fmt = i18n.tooltips.attack_speed_multiplier,
},
},
{
show = true, -- TODO: Show only if has damage_multiplier
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'damage_multiplier',
hide_default = 100,
fmt = '%i',
inline = '%s%% ' .. i18n.tooltips.of_base_stat,
},
},
fmt = i18n.tooltips.damage_multiplier,
},
},
{
show = true, -- TODO: Show only if has damage_effectiveness
func = core.factory.infobox_line{
type = 'gem',
parts = {
{
key = 'damage_effectiveness',
hide_default = 100,
fmt = '%i',
inline = '%s%%',
},
},
fmt = i18n.tooltips.damage_effectiveness,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'projectile_speed'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'projectile_speed',
},
},
fmt = i18n.tooltips.projectile_speed,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'radius'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'radius',
func = h.factory.descriptor_value{key='radius_description'},
},
{
key = 'radius_secondary',
func = h.factory.descriptor_value{key='radius_secondary_description'},
},
{
key = 'radius_tertiary',
func = h.factory.descriptor_value{key='radius_tertiary_description'},
},
},
sep = ' / ',
fmt = i18n.tooltips.aoe_radius,
},
},
-- Quality is before item stats, but after gem stuff and item class
{
show = h.factory.maybe_show_infobox_line{
keys = {'quality'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'quality',
fmt = '+%i%%',
color = 'mod',
hide_default = 0,
},
},
fmt = i18n.tooltips.quality,
},
},
-- Weapons
{
show = h.factory.maybe_show_infobox_line{
keys = {'physical_damage_html'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'physical_damage_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.physical_damage,
},
},
{
show = true, -- Elemental Damage
func = function (tpl_args)
local keys = {'fire_damage_html', 'cold_damage_html', 'lightning_damage_html'}
local elements = {}
for _, key in ipairs(keys) do
if tpl_args[key] then
elements[#elements+1] = tpl_args[key]
end
end
local text = table.concat(elements, ', ') -- returns empty string if elements is empty
if text ~= '' then
return string.format(i18n.tooltips.elemental_damage, text)
end
return
end,
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'chaos_damage_html'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'chaos_damage_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.chaos_damage,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'critical_strike_chance_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'critical_strike_chance_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.critical_strike_chance,
},
},
{
show = function (tpl_args)
-- Do not show for caster weapons
if tpl_args.tags and (m_util.table.contains(tpl_args.tags, 'staff')
or m_util.table.contains(tpl_args.tags, 'wand')
or m_util.table.contains(tpl_args.tags, 'sceptre')
or m_util.table.contains(tpl_args.tags, 'trap')) then
return false
end
return h.factory.maybe_show_infobox_line{
keys = {'attack_speed_html'},
}(tpl_args)
end,
func = core.factory.infobox_line{
parts = {
{
key = 'attack_speed_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.attacks_per_second,
},
},
{
show = function (tpl_args)
-- Do not show for ranged weapons
if tpl_args.tags and m_util.table.contains(tpl_args.tags, 'ranged') then
return false
end
return h.factory.maybe_show_infobox_line{
keys = {'weapon_range_html'},
}(tpl_args)
end,
func = core.factory.infobox_line{
parts = {
{
key = 'weapon_range_html',
color = false, -- html already has color
inline = '%s ' .. m_game.units.metres.long_lower,
},
},
fmt = i18n.tooltips.weapon_range,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'spirit_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'spirit_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'spirit',
},
},
fmt = i18n.tooltips.spirit,
},
},
-- Maps
{
show = h.factory.maybe_show_infobox_line{
keys = {'map_area_level'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'map_area_level',
fmt = '%i',
},
},
fmt = i18n.tooltips.map_level,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'map_tier'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'map_tier',
fmt = '%i',
},
},
fmt = i18n.tooltips.map_tier,
},
},
{
show = function (tpl_args)
return tpl_args.map_guild_character ~= nil and tpl_args.rarity_id == 'normal'
end,
func = core.factory.infobox_line{
parts = {
{
key = 'map_guild_character',
},
},
fmt = i18n.tooltips.map_guild_character,
},
},
{
show = function (tpl_args)
return tpl_args.unique_map_guild_character ~= nil and tpl_args.rarity_id == 'unique'
end,
func = core.factory.infobox_line{
parts = {
{
key = 'unique_map_guild_character',
},
},
fmt = i18n.tooltips.map_guild_character,
},
},
{
show = function (tpl_args)
return tpl_args.class_id == 'Map'
end,
func = core.factory.infobox_line{
type = 'stat',
parts = {
{
key = 'map_item_drop_quantity_+%',
fmt = '%i',
color = 'mod',
inline = '+%s%%',
hide_default = 0,
},
},
fmt = i18n.tooltips.item_quantity,
},
},
{
show = function (tpl_args)
return tpl_args.class_id == 'Map'
end,
func = core.factory.infobox_line{
type = 'stat',
parts = {
{
key = 'map_item_drop_rarity_+%',
fmt = '%i',
color = 'mod',
inline = '+%s%%',
hide_default = 0,
},
},
fmt = i18n.tooltips.item_rarity,
},
},
{
show = function (tpl_args)
return tpl_args.class_id == 'Map'
end,
func = core.factory.infobox_line{
type = 'stat',
parts = {
{
key = 'map_pack_size_+%',
fmt = '%i',
color = 'mod',
inline = '+%s%%',
hide_default = 0,
},
},
fmt = i18n.tooltips.monster_pack_size,
},
},
-- Jewels
{
show = h.factory.maybe_show_infobox_line{
keys = {'jewel_limit'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'jewel_limit',
},
},
fmt = i18n.tooltips.limited_to,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'jewel_radius_html'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'jewel_radius_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.radius,
},
},
-- Flasks
{
show = h.factory.maybe_show_infobox_line{
keys = {'flask_mana_html', 'flask_duration_html'},
},
--func = core.factory.display_flask('flask_mana'),
func = core.factory.infobox_line{
parts = {
{
key = 'flask_mana_html',
color = false, -- html already has color
},
{
key = 'flask_duration_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.flask_mana_recovery,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'flask_life_html', 'flask_duration_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'flask_life_html',
color = false, -- html already has color
},
{
key = 'flask_duration_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.flask_life_recovery,
},
},
{
-- don't display for mana/life flasks
show = function (tpl_args)
for _, k in ipairs({'flask_life_html', 'flask_mana_html'}) do
if tpl_args[k] ~= nil then
return false
end
end
return tpl_args['flask_duration_html'] ~= nil
end,
func = core.factory.infobox_line{
parts = {
{
key = 'flask_duration_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.flask_duration,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'charges_per_use_html', 'charges_max_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'charges_per_use_html',
color = false, -- html already has color
},
{
key = 'charges_max_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.flask_charges_per_use,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'buff_stat_text'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'buff_stat_text',
color = 'mod',
},
},
},
},
-- Armor
{
show = h.factory.maybe_show_infobox_line{
keys = {'block_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'block_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'block',
},
},
fmt = i18n.tooltips.chance_to_block,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'armour_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'armour_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'armour',
},
},
fmt = i18n.tooltips.armour,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'evasion_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'evasion_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'evasion',
},
},
fmt = i18n.tooltips.evasion,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'energy_shield_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'energy_shield_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'energy_shield',
},
},
fmt = i18n.tooltips.energy_shield,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'ward_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'ward_html',
color = false, -- html already has color
hide_default = 0,
hide_default_key = 'ward',
},
},
fmt = i18n.tooltips.ward,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'movement_speed'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'movement_speed',
fmt = '%i%%',
hide_default = 0,
},
},
fmt = i18n.tooltips.movement_speed,
},
},
-- Stackables
{
show = h.factory.maybe_show_infobox_line{
keys = {'stack_size'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'stack_size',
fmt = '%i',
hide_default = 1,
},
},
fmt = i18n.tooltips.stack_size,
},
},
-- Map fragments
{
show = function (tpl_args)
-- Only show for scarabs
if not tpl_args._flags.is_scarab then
return false
end
return h.factory.maybe_show_infobox_line{
keys = {'map_fragment_limit'},
conditions = {processed = true},
}(tpl_args)
end,
func = core.factory.infobox_line{
parts = {
{
key = 'map_fragment_limit',
fmt = '%i',
},
},
fmt = i18n.tooltips.limit,
},
},
-- Essences
{
show = h.factory.maybe_show_infobox_line{
keys = {'essence_level'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'essence_level',
fmt = '%i',
},
},
fmt = i18n.tooltips.essence_level,
},
},
-- Oils
{
show = h.factory.maybe_show_infobox_line{
keys = {'blight_item_tier'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'blight_item_tier',
fmt = '%i',
},
},
fmt = i18n.tooltips.blight_item_tier,
},
},
-- Tattoos
{
show = h.factory.maybe_show_infobox_line{
keys = {'tattoo_target'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'tattoo_target',
},
},
fmt = i18n.tooltips.tattoo_target,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'tattoo_limit'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'tattoo_limit',
},
},
fmt = i18n.tooltips.tattoo_limit,
},
},
-- Harvest seeds (upper section)
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_tier'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'seed_tier',
fmt = '%i',
},
},
fmt = i18n.tooltips.seed_tier,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_tier'},
conditions = {processed = true},
},
func = function (tpl_args)
return i18n.tooltips.seed_monster
end,
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_type_html'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'seed_type_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.seed_lifeforce_gained,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_growth_cycles'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'seed_growth_cycles',
},
},
fmt = i18n.tooltips.seed_growth_cycles,
},
},
-- Heist
{
show = h.factory.maybe_show_infobox_line{
keys = {'heist_required_npcs'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'heist_required_npcs',
},
},
fmt = i18n.tooltips.heist_required_npc,
},
},
-- Sentinels
{
show = h.factory.maybe_show_infobox_line{
keys = {'sentinel_duration'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'sentinel_duration',
fmt = '%i',
},
},
fmt = i18n.tooltips.sentinel_duration,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'sentinel_empowers'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'sentinel_empowers',
fmt = '%i',
},
},
fmt = i18n.tooltips.sentinel_empowers,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'sentinel_empowerment'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'sentinel_empowerment',
fmt = '%i',
},
},
fmt = i18n.tooltips.sentinel_empowerment,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'sentinel_monster', 'sentinel_monster_level'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'sentinel_monster',
},
{
key = 'sentinel_monster_level',
fmt = '%i',
},
},
fmt = i18n.tooltips.sentinel_can_only_empower,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'sentinel_charge'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'sentinel_charge',
fmt = '%i',
},
},
fmt = i18n.tooltips.sentinel_charge,
},
},
-- Corpse items
{
show = h.factory.maybe_show_infobox_line{
keys = {'monster_category_html'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'monster_category_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.monster_category,
},
},
-- Embers of the Allflame
{
show = h.factory.maybe_show_infobox_line{
keys = {'pack_size_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'pack_size_html',
color = false, -- html already has color
},
},
fmt = i18n.tooltips.pack_size,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'pack_leader_chance'},
conditions = {processed = true},
},
func = function (tpl_args)
return core.factory.infobox_line{
parts = {
{
key = 'pack_leader_chance',
hide_default = 0,
fmt = '%i%%',
},
},
fmt = tpl_args['pack_leader_chance'] < 100 and i18n.tooltips.pack_chance_to_contain_pack_leader or i18n.tooltips.pack_contains_pack_leader,
}(tpl_args)
end,
},
},
-- Requirements
{
-- Talismans display their tier right above requirements
{
show = h.factory.maybe_show_infobox_line{
keys = {'talisman_tier'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'talisman_tier',
fmt = '%i',
},
},
fmt = i18n.tooltips.talisman_tier,
},
},
-- Equipment requirements
{
show = true, -- Requires...
func = function (tpl_args)
local parts = {
{
key = 'required_level_final_html',
color = false, -- html already has color
inline = i18n.tooltips.level_inline,
hide_default = 1,
hide_default_key = 'required_level_final',
},
}
for _, attr in ipairs(m_game.constants.attribute_order) do
parts[#parts+1] = {
key = string.format('required_%s_html', attr),
color = false, -- html already has color
inline = '%s ' .. m_game.constants.attributes[attr].short_upper,
hide = function (tpl_args, value)
local min = m_game.constants.characters.minimum_attributes[m_game.constants.attributes[attr].short_lower]
return value.min <= min and value.max <= min
end,
hide_key = string.format('required_%s', attr),
}
end
return core.factory.infobox_line{
parts = parts,
sep = ', ',
fmt = i18n.tooltips.requires,
}(tpl_args)
end,
},
-- Tattoo requirements
{
show = h.factory.maybe_show_infobox_line{
keys = {'tattoo_min_adjacent'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'tattoo_min_adjacent',
fmt = '%i',
hide_default = 0,
},
},
fmt = i18n.tooltips.tattoo_min_adjacent,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'tattoo_max_adjacent'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'tattoo_max_adjacent',
fmt = '%i',
color = 'value',
inline = i18n.tooltips.tattoo_maximum .. ' %s',
hide_default = 0,
},
},
fmt = i18n.tooltips.tattoo_max_adjacent,
},
},
-- Heist gear requirements
{
show = h.factory.maybe_show_infobox_line{
keys = {'heist_required_job', 'heist_required_job_level'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'heist_required_job_level',
},
{
key = 'heist_required_job',
},
},
fmt = i18n.tooltips.heist_required_job,
},
},
},
-- Gem description
{
class = 'tc -gemdesc',
{
show = h.factory.maybe_show_infobox_line{
keys = {'gem_description'},
},
func = h.factory.display_raw_value('gem_description'),
},
},
-- Gem stats
{
class = 'tc -mod',
{
show = function (tpl_args)
return cfg.class_groups.gems.keys[tpl_args.class_id] and tpl_args.stat_text
end,
func = h.factory.display_raw_value('stat_text'),
},
{
-- Gem quality selector widget
-- Show in full infobox if gem has alternate quality
-- Never show in hover infobox
show = function (tpl_args)
return cfg.class_groups.gems.keys[tpl_args.class_id] and tpl_args.quality_type1_stat_text and tpl_args._flags.is_alt_quality_gem and tpl_args._infobox_container == 'infobox'
end,
func = function (tpl_args)
local html = mw.html.create()
:tag('br'):done()
local widget = html:tag('span')
:addClass('gemqual-widget')
:wikitext(m_util.html.poe_color('value', string.format(i18n.tooltips.gem_quality_effect_bonus, '')))
local qtypes = {}
for k, quality_data in ipairs(tpl_args.skill_quality) do
qtypes[#qtypes+1] = m_game.constants.item.gem_quality_types[k].long_upper
local stats = widget:tag('span')
:addClass('gemqual-widget__stats')
:attr('data-qid', k)
:wikitext(quality_data.stat_text)
if k == 1 then
stats:addClass('is-selected')
end
end
widget:attr('data-qtypes', table.concat(qtypes, ','))
return tostring(html)
end,
},
{
-- Superior quality bonus
-- Show in full infobox if gem does not have alternate quality
-- Always show in hover infobox
show = function (tpl_args)
return cfg.class_groups.gems.keys[tpl_args.class_id] and tpl_args.quality_type1_stat_text and (not tpl_args._flags.is_alt_quality_gem or tpl_args._infobox_container == 'inline')
end,
func = core.factory.infobox_line{
parts = {
{
key = 'quality_type1_stat_text',
color = 'mod',
inline = i18n.tooltips.gem_quality_effect_bonus,
inline_color = 'value',
},
},
fmt = ' <br> %s',
},
},
},
-- Implicit stats
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'implicit_stat_text'},
},
func = function (tpl_args)
if tpl_args._infobox_container == 'inline' then
return h.strip_random_stats(tpl_args, tpl_args.implicit_stat_text)
end
return tpl_args.implicit_stat_text
end,
},
},
-- Explicit stats
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'explicit_stat_text'},
},
func = function (tpl_args)
if tpl_args._infobox_container == 'inline' then
return h.strip_random_stats(tpl_args, tpl_args.explicit_stat_text)
end
return tpl_args.explicit_stat_text
end,
},
},
-- Experience
--[[{
{
show = h.factory.maybe_show_infobox_line{
keys = {'experience'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'experience',
fmt = '%i',
},
},
},
},
},]]--
-- Harvest seeds (lower section)
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_consumed_wild_lifeforce_percentage'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.seed_consumed_wild_lifeforce_percentage > 0 then
return string.format(i18n.tooltips.seed_lifeforce_consumed, tpl_args.seed_consumed_wild_lifeforce_percentage, m_util.html.poe_color('wild', m_game.seed_types.wild))
end
end
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_consumed_vivid_lifeforce_percentage'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.seed_consumed_vivid_lifeforce_percentage > 0 then
return string.format(i18n.tooltips.seed_lifeforce_consumed, tpl_args.seed_consumed_vivid_lifeforce_percentage, m_util.html.poe_color('vivid', m_game.seed_types.vivid))
end
end
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_consumed_primal_lifeforce_percentage'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.seed_consumed_primal_lifeforce_percentage > 0 then
return string.format(i18n.tooltips.seed_lifeforce_consumed, tpl_args.seed_consumed_primal_lifeforce_percentage, m_util.html.poe_color('primal', m_game.seed_types.primal))
end
end
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_required_nearby_seed_tier', 'seed_type_html', 'seed_required_nearby_seed_amount'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'seed_required_nearby_seed_amount',
},
{
key = 'seed_type_html',
},
{
key = 'seed_required_nearby_seed_tier',
},
},
fmt = i18n.tooltips.seed_required_seeds,
color = 'mod',
},
},
},
{
class = 'tc -crafted',
{
show = h.factory.maybe_show_infobox_line{
keys = {'seed_effect'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('seed_effect'),
},
},
-- Description
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'description'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('description'),
},
--[[{
show = h.factory.maybe_show_infobox_line{
keys = {'plant_booster_additional_crafting_options'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'plant_booster_additional_crafting_options',
},
},
fmt = i18n.tooltips.plant_booster_additional_crafting_options,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'plant_booster_extra_chances'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'plant_booster_extra_chances',
fmt = '%s%%',
},
},
fmt = i18n.tooltips.plant_booster_extra_chances,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'plant_booster_lifeforce'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'plant_booster_lifeforce',
fmt = '%s%%',
},
},
fmt = i18n.tooltips.plant_booster_lifeforce,
},
},]]
{
show = h.factory.maybe_show_infobox_line{
keys = {'monster_abilities'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('monster_abilities'),
},
},
-- Variations (for hideout decorations)
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'variation_count'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.variation_count > 1 then
return string.format('%i %s', tpl_args.variation_count, i18n.tooltips.variations)
end
return nil
end,
},
},
-- Corrupted
{
class = 'tc -corrupted',
{
show = h.factory.maybe_show_infobox_line{
keys = {'is_corrupted'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.is_corrupted then
return i18n.tooltips.corrupted
end
return nil
end,
},
},
-- Mirrored
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'is_mirrored'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.is_mirrored then
return i18n.tooltips.mirrored
end
return nil
end,
},
},
-- Unmodifiable
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'is_unmodifiable'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.is_unmodifiable then
return i18n.tooltips.unmodifiable
end
return nil
end,
},
},
-- Flavour text
{
class = 'tc -flavour',
{
show = h.factory.maybe_show_infobox_line{
keys = {'flavour_text'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('flavour_text'),
},
},
-- Prophecy text
{
class = 'tc -value',
{
show = h.factory.maybe_show_infobox_line{
keys = {'prediction_text'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('prediction_text'),
},
},
-- Can not be traded or modified
{
class = 'tc -canttradeormodify',
{
show = h.factory.maybe_show_infobox_line{
keys = {'cannot_be_traded_or_modified'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.cannot_be_traded_or_modified then
return i18n.tooltips.cannot_be_traded_or_modified
end
return nil
end,
},
},
-- Help text
{
class = 'tc -help',
{
show = h.factory.maybe_show_infobox_line{
keys = {'help_text'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('help_text'),
},
},
-- Account-bound
{
class = 'tc -mod',
{
show = h.factory.maybe_show_infobox_line{
keys = {'is_account_bound'},
conditions = {processed = true},
},
func = function (tpl_args)
if tpl_args.is_account_bound then
return i18n.tooltips.account_bound
end
return nil
end,
},
},
}
--
-- This is meant to show additional information about the item in a separate infobox
--
c.extra_display_groups = {
{
heading = i18n.tooltips.extra_info,
class = '',
{
show = h.factory.maybe_show_infobox_line{
keys = {'atlas_connections'},
conditions = {processed = true},
},
func = function (tpl_args)
local fields = {
[false] = {
value = '✗',
sort = 0,
class = 'table-cell-xmark',
},
[true] = {
value = '✓',
sort = 1,
class = 'table-cell-checkmark',
},
}
local tbl = mw.html.create('table')
tbl
:addClass('wikitable')
:attr('style', 'width:100%;')
:tag('tr')
:tag('th')
:attr('colspan', 6)
:attr('style', 'text-decoration: underline;')
:wikitext(i18n.tooltips.header_overall)
:done()
:done()
:tag('tr')
:tag('th')
:wikitext(i18n.tooltips.header_upgrades)
:done()
:tag('th')
:wikitext(0)
:done()
:tag('th')
:wikitext(1)
:done()
:tag('th')
:wikitext(2)
:done()
:tag('th')
:wikitext(3)
:done()
:tag('th')
:wikitext(4)
:done()
:done()
for _, vtype in ipairs({'tier', 'level'}) do
local tr = tbl:tag('tr')
tr
:tag('th')
:wikitext(i18n.tooltips['header_map_' .. vtype])
:done()
for i=0,4 do
local value = tpl_args['atlas_map_tier' .. i]
if value == 0 then
value = fields[false].value
elseif vtype == 'level' then
value = value + 67
end
tr
:tag('td')
:wikitext(value)
:done()
end
tr:done()
end
tbl
:tag('tr')
:tag('th')
:attr('colspan', 6)
:attr('style', 'text-decoration: underline;')
:wikitext(i18n.tooltips.header_connections)
:done()
:done()
-- sort alphabetically
local sorted = {}
for key, value in pairs(tpl_args.atlas_connections) do
sorted[#sorted+1] = key
end
table.sort(sorted)
for _, key in ipairs(sorted) do
local tr = tbl:tag('tr')
tr
:tag('th')
:wikitext(key)
:done()
for i=0,4 do
local field = fields[tpl_args.atlas_connections[key]['region' .. i]]
tr
:tag('td')
:attr('data-sort-value', field.sort)
:addClass(field.class)
:wikitext(field.value)
:done()
end
tr:done()
end
return tostring(tbl)
end
},
},
-- Acquisition
{
heading = i18n.tooltips.acquisition,
{
show = function (tpl_args)
if tpl_args.is_in_game == false then
return true
end
return false
end,
func = function (tpl_args)
local span = mw.html.create('span')
span
:addClass('infobox-disabled-drop')
:wikitext(i18n.tooltips.not_in_game)
:done()
return tostring(span)
end,
},
{
show = function (tpl_args)
if tpl_args.drop_enabled == false and tpl_args.is_in_game ~= false then
return true
end
return false
end,
func = function (tpl_args)
local span = mw.html.create('span')
span
:addClass('infobox-disabled-drop')
:wikitext(i18n.tooltips.drop_disabled)
:done()
return tostring(span)
end,
},
{
show = function (tpl_args)
if tpl_args.is_drop_restricted == true and tpl_args.drop_enabled ~= false then
return true
end
return false
end,
func = function (tpl_args)
local span = mw.html.create('span')
span
:addClass('infobox-restricted-drop')
:wikitext(i18n.tooltips.drop_restricted)
:done()
return tostring(span)
end,
},
{
show = function (tpl_args)
-- Do not show for gems (engraved only)
if tpl_args.class_id == 'Active Skill Gem' or tpl_args.class_id == 'Support Skill Gem' or tpl_args.class_id == 'Meta Skill Gem' then
return false
end
return h.factory.maybe_show_infobox_line{
keys = {'drop_level'},
conditions = {processed = true},
}
end,
func = core.factory.infobox_line{
parts = {
{
key = 'drop_level',
fmt = '%i',
},
{
key = 'drop_level_maximum',
fmt = '%i',
hide_default = 100,
},
},
sep = ' / ',
fmt = i18n.tooltips.drop_level,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'acquisition_tags'},
conditions = {processed = true},
},
func = function (tpl_args)
local tags = {}
for _, v in pairs(tpl_args.acquisition_tags) do
if v then
tags[#tags+1] = string.format('[[%s|%s]]', cfg.acquisition_tags[v].page, cfg.acquisition_tags[v].name)
end
end
return table.concat(tags, ' • ')
end
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'drop_areas_html'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('drop_areas_html'),
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'drop_text'},
conditions = {processed = true},
},
func = h.factory.display_raw_value('drop_text'),
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'seal_cost'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'seal_cost',
fmt = function (tpl_args, value)
return string.format('%s %s', i18n.fmt.item_count, h.item_link{metadata_id='Metadata/Items/Currency/CurrencySilverCoin', html=''})
end,
color = 'currency',
},
},
fmt = i18n.tooltips.seal_cost,
},
},
},
-- {
-- -- Vendor prices
-- heading = i18n.tooltips.purchase_costs,
-- {
-- show = function (tpl_args)
-- for rarity, data in pairs(tpl_args.purchase_costs) do
-- if #data > 0 then
-- return true
-- end
-- end
-- return false
-- end,
-- func = function (tpl_args)
-- local tbl = mw.html.create('table')
-- tbl
-- --:addClass('wikitable')
-- :attr('style', 'width: 100%; margin-top: 0px;')
-- for _, rarity_id in ipairs(m_game.constants.rarity_order) do
-- local data = tpl_args.purchase_costs[rarity_id]
-- if #data > 0 then
-- local tr = tbl:tag('tr')
-- tr
-- :tag('td')
-- :wikitext(m_game.constants.rarities[rarity_id].long_upper)
-- local td = tr:tag('td')
-- for _, purchase_data in ipairs(data) do
-- td:wikitext(string.format('%dx [[%s]]<br />', purchase_data.amount, purchase_data.name))
-- end
-- end
-- end
-- return tostring(tbl)
-- end,
-- },
-- },
{
-- Vendor offer
heading = i18n.tooltips.sell_price,
{
show = h.factory.maybe_show_infobox_line{
keys = {'sell_price_order'},
},
func = function (tpl_args)
local out = {}
if #tpl_args.sell_price_order > 0 then
for _, item_name in ipairs(tpl_args.sell_price_order) do
out[#out+1] = string.format('%dx [[%s]]', tpl_args.sell_prices[item_name], item_name)
end
end
return table.concat(out, '<br>')
end,
},
},
-- Damage per second
{
heading = i18n.tooltips.damage_per_second,
{
show = h.factory.maybe_show_infobox_line{
keys = {'physical_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'physical_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.physical_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'fire_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'fire_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.fire_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'cold_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'cold_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.cold_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'lightning_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'lightning_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.lightning_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'chaos_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'chaos_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.chaos_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'elemental_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'elemental_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.elemental_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'poison_dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'poison_dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.poison_dps,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'dps_html'},
},
func = core.factory.infobox_line{
parts = {
{
key = 'dps_html',
color = false, -- the html already contains the colour
},
},
fmt = i18n.tooltips.dps,
},
},
},
-- Metadata
{
heading = i18n.tooltips.metadata,
{
show = h.factory.maybe_show_infobox_line{
keys = {'class'},
conditions = {processed = true},
},
func = core.factory.infobox_line{
parts = {
{
key = 'class',
},
},
fmt = i18n.tooltips.item_class,
},
},
{
show = h.factory.maybe_show_infobox_line{
keys = {'metadata_id'},
conditions = {processed = true},
},
func = function (tpl_args)
return core.factory.infobox_line{
parts = {
{
key = 'metadata_id',
inline = m_util.html.abbr('%s', tpl_args.metadata_id),
},
},
fmt = tostring(
mw.html.create('span')
:addClass('u-truncate-line')
:wikitext(i18n.tooltips.metadata_id)
),
}(tpl_args)
end,
},
},
}
-- ----------------------------------------------------------------------------
-- Subroutines
-- ----------------------------------------------------------------------------
local s = {}
-- Subroutines for p.item. These exist to declutter the main function. Each one of these is only called once.
function s.get_item_config(tpl_args)
-- Returns primary item configuration, based on item class
local config = {
tables = {}, -- Table names that data may be stored to
args = {}, -- Args to process early
late_args = {}, -- Args to process late
defaults = {}, -- Defaults based on item class
}
for _, v in ipairs(cfg.tables) do
table.insert(config.tables, v)
end
for _, v in ipairs(cfg.default_args) do
table.insert(config.args, v)
end
for _, v in ipairs(cfg.late_args) do
table.insert(config.late_args, v)
end
-- Process class_id
h.process_arguments(tpl_args, {'class_id'})
local extend_keys = {'tables', 'args', 'late_args'} -- We will do defaults separately to preserve order of args
for _, row in pairs(cfg.class_groups) do
if row.keys[tpl_args.class_id] then
for _, k in ipairs(extend_keys) do
if row[k] then
for _, v in ipairs(row[k]) do
table.insert(config[k], v)
end
end
end
if row['defaults'] then
for k, v in pairs(row['defaults']) do
config['defaults'][k] = v
end
end
break
end
end
local class_specifics = cfg.class_specifics[tpl_args.class_id]
if class_specifics then
for _, k in ipairs(extend_keys) do
if class_specifics[k] then
for _, v in ipairs(class_specifics[k]) do
table.insert(config[k], v)
end
end
end
if class_specifics['defaults'] then
for k, v in pairs(class_specifics['defaults']) do
config['defaults'][k] = v
end
end
end
return config
end
function s.process_quest_rewards(tpl_args)
local rid = 1
local continue
tpl_args.quest_rewards = {}
tpl_args.vendor_rewards = {}
repeat
continue = true
local prefix = string.format('quest_reward%s_', rid)
local input_args = {
shared = {
['type'] = true,
['quest'] = false,
['quest_id'] = false,
['act'] = true,
['class_ids'] = false,
},
vendor = {
['npc'] = true,
},
quest = {
['sockets'] = false,
['item_level'] = false,
['rarity_id'] = false,
['notes'] = false,
},
}
local rdata = {}
for key, is_required in pairs(input_args.shared) do
rdata[key] = tpl_args[prefix .. key]
if is_required then
if rdata[key] == nil then
continue = false
break
end
end
end
if rdata.quest == nil or rdata.quest_id == nil then
continue = false
end
if continue and rdata.type == 'vendor' or rdata.type == 'quest' then
for key, is_required in pairs(input_args[rdata.type]) do
rdata[key] = tpl_args[prefix .. key]
if is_required then
if rdata[key] == nil then
continue = false
break
end
end
end
else
continue = false
end
if continue then
rdata.classes = {}
if rdata.class_ids ~= nil then
rdata.class_ids = m_util.string.split(rdata.class_ids, ',%s*')
for index, class_id in ipairs(rdata.class_ids) do
local class = m_game.constants.characters[class_id]
if class == nil then
error(string.format('Class id %s is invalid', class_id))
else
rdata.class_ids[index] = class.str_id
rdata.classes[index] = class.name
end
end
end
if rdata.item_level then
rdata.item_level = m_util.cast.number(rdata.item_level)
end
if rdata.rarity_id then
if m_game.constants.rarities[rdata.rarity_id] == nil then
error(string.format(i18n.errors.invalid_rarity_id, tostring(rdata.rarity_id)))
end
end
rdata._table = rdata.type .. '_rewards'
rdata.type = nil
tpl_args[rdata._table] = rdata
m_cargo.store(rdata)
-- TODO: Verify quests and quest ids?
end
rid = rid + 1
until continue == false
end
function s.process_base_item(tpl_args)
if not tpl_args._flags.is_derived then
if tpl_args.rarity_id ~= 'normal' then
error(i18n.errors.missing_base_item)
end
return
end
m_item_util = m_item_util or require('Module:Item util')
local qargs = {}
qargs.tables = tpl_args._item_config.tables
qargs.join = {}
for _, tbl in ipairs(tpl_args._item_config.tables) do
if tbl ~= 'items' then
qargs.join[#qargs.join+1] = string.format('items._pageID=%s._pageID', tbl)
end
end
qargs.join = #qargs.join > 0 and table.concat(qargs.join, ', ') or nil
qargs.fields = {
'items._pageName',
'items.name',
'items.metadata_id',
'items.class_id',
'items.rarity_id',
'items.inventory_icon',
}
for _, k in ipairs(tpl_args._base_item_args) do
local arg_def = core.map[k]
if arg_def.field ~= nil then
qargs.fields[#qargs.fields+1] = string.format('%s.%s', arg_def.table, arg_def.field)
end
end
qargs.where = string.format('items.class_id = "%s"', tpl_args.class_id)
local result = m_item_util.query_item({
metadata_id = tpl_args.base_item_id,
page = tpl_args.base_item_page,
item_name_exact = tpl_args.base_item,
debug = tpl_args.debug,
}, qargs)
if result.error then
result.error:throw(true)
end
if result['items.rarity_id'] ~= 'normal' then
error(i18n.errors.base_item_wrong_rarity)
end
tpl_args.base_item = result['items.name']
tpl_args.base_item_page = result['items._pageName']
tpl_args.base_item_id = result['items.metadata_id']
h.process_arguments(tpl_args, {'base_item', 'base_item_page', 'base_item_id'})
tpl_args.base_item_icon = result['items.inventory_icon']
-- Copy values
for _, k in ipairs(tpl_args._base_item_args) do
local arg_def = core.map[k]
if arg_def.func_fetch ~= nil then
arg_def.func_fetch(tpl_args)
elseif arg_def.field ~= nil then
local value = result[string.format('%s.%s', arg_def.table, arg_def.field)]
if value == nil then
if tpl_args.debug then
mw.log(string.format(i18n.debug.base_item_field_not_found, arg_def.table, arg_def.field))
end
else
local default = type(arg_def.default) == 'function' and arg_def.default(tpl_args) or arg_def.default
-- Inherit value from base item if derived item value is not equal to the default value.
-- This verbose comparison is needed since two empty tables are not considered equal to one another.
if tpl_args[k] == arg_def.default or (type(tpl_args[k]) == 'table' and #tpl_args[k] == 0 and type(arg_def.default) == 'table' and #arg_def.default == 0) then
tpl_args[k] = value
if arg_def.func ~= nil then
tpl_args[k] = arg_def.func(tpl_args, tpl_args[k])
end
if arg_def.func_copy ~= nil then
arg_def.func_copy(tpl_args, tpl_args[k])
end
elseif tpl_args.debug then
mw.log(string.format(i18n.debug.field_value_mismatch, k, tostring(tpl_args[k])))
end
end
end
end
-- Fetch implicit mods from base item
local mods = m_cargo.query(
{'items' ,'item_mods'},
{'item_mods.id', 'item_mods.is_random', 'item_mods.text'},
{
join = 'items._pageID=item_mods._pageID',
where = string.format(
'items._pageName="%s" AND item_mods.is_implicit=true',
tpl_args.base_item_page
),
}
)
tpl_args._base_implicit_mods = {}
for _, row in ipairs(mods) do
table.insert(tpl_args._base_implicit_mods, {
id = row['item_mods.id'],
stat_text = row['item_mods.text'],
is_implicit = true,
is_random = m_util.cast.boolean(row['item_mods.is_random']),
result = (function() if row['item_mods.id'] == nil then return false end end)()
})
end
end
function s.process_mods(tpl_args)
tpl_args._defined_implicit_mods = {}
tpl_args._mods = {}
local function parse_mod_arg(tpl_args, type, index)
local prefix = type .. index
local mod_data = {
is_implicit = type == 'implicit',
is_explicit = type == 'explicit',
is_map_fragment_bonus = type == 'map_fragment_bonus',
is_random = false,
}
local mods_table = mod_data.is_implicit and tpl_args._defined_implicit_mods or tpl_args._mods
-- By mod id
local value = tpl_args[prefix]
if value ~= nil then
mod_data.id = value
mod_data.stat_text = tpl_args[prefix .. '_text']
table.insert(mods_table, mod_data)
return true
end
-- By list of possible mod ids
value = tpl_args[prefix .. '_random_list']
if value ~= nil then
value = m_util.cast.table(value)
for _, id in ipairs(value) do
local mod_data_copy = {}
for k, v in pairs(mod_data) do
mod_data_copy[k] = v
end
mod_data_copy.id = id
mod_data_copy.is_random = true
mod_data_copy.stat_text = tpl_args[prefix .. '_text'] or string.format(i18n.tooltips.random_mod, index)
table.insert(mods_table, mod_data_copy)
end
tpl_args._flags.random_mods = true
return true
end
-- By stat text
value = tpl_args[prefix .. '_text']
if value ~= nil then
mod_data.result = false -- Not stored in Cargo table
mod_data.stat_text = value
table.insert(mods_table, mod_data)
tpl_args._flags.text_modifier = true
return true
end
return false
end -- parse_mod_arg
local types = {
'implicit',
'explicit',
'map_fragment_bonus',
}
for _, t in ipairs(types) do
local i = 1
while parse_mod_arg(tpl_args, t, i) do
i = i + 1
end
end
-- If the item does not have its own implicit mods, fall back to the implicit mods on the base item.
local implicit_mods = tpl_args._defined_implicit_mods or {}
if #implicit_mods == 0 then
implicit_mods = tpl_args._base_implicit_mods or {}
end
for _, v in ipairs(implicit_mods) do
table.insert(tpl_args._mods, v)
end
if #tpl_args._mods > 0 then
local mods = {}
local mod_ids = {}
local non_random_mod_ids = {}
for _, mod_data in ipairs(tpl_args._mods) do
if mod_data.result == nil then
mods[mod_data.id] = mod_data
table.insert(mod_ids, mod_data.id)
if not mod_data.is_random then
table.insert(non_random_mod_ids, mod_data.id)
end
end
table.insert(tpl_args._store_data, {
_table = 'item_mods',
id = mod_data.id,
text = mod_data.stat_text,
is_implicit = mod_data.is_implicit,
is_explicit = mod_data.is_explicit,
is_map_fragment_bonus = mod_data.is_map_fragment_bonus,
is_random = mod_data.is_random,
})
end
local results = m_cargo.query(
{'mods'},
{'mods._pageName', 'mods.id', 'mods.required_level', 'mods.stat_text'},
{
where = string.format(
'mods.id IN ("%s")',
table.concat(mod_ids, '","')
)
}
)
if #results < #mod_ids then
local found_mod_ids = m_util.table.column(results, 'mods.id')
local bogus_mod_ids = m_util.table.diff(mod_ids, found_mod_ids)
error(string.format(i18n.errors.mod_not_found, table.concat(bogus_mod_ids, ', ')))
end
for _, row in ipairs(results) do
local mod_data = mods[row['mods.id']]
mod_data.result = row
if mod_data.is_random == false then
-- Modifiers contribute to level requirement
-- Update base level requirement only if this is an implicit
local args = {
'required_level_final',
mod_data.is_implicit and 'required_level' or nil,
}
for _, v in ipairs(args) do
local req = math.floor(tonumber(row['mods.required_level']) * cfg.item_required_level_modifier_contribution)
if req > tpl_args[v] then
tpl_args[v] = req
end
end
end
end
-- fetch stats
results = m_cargo.query(
{'mods', 'mod_stats'},
{'mods.id', 'mod_stats.id', 'mod_stats.min', 'mod_stats.max'},
{
join = 'mods._pageID=mod_stats._pageID',
where = string.format(
'mods.id IN ("%s") AND mod_stats.id IS NOT NULL',
table.concat(mod_ids, '","')
),
}
)
for _, row in ipairs(results) do
-- Stat data
local mod_data = mods[row['mods.id']]
mod_data.result.stats = mod_data.result.stats or {}
table.insert(mod_data.result.stats, row)
local stat_id = row['mod_stats.id']
local value = {
min = tonumber(row['mod_stats.min']),
max = tonumber(row['mod_stats.max']),
}
local tbl = mod_data.is_random and '_random_stats' or '_stats'
core.add_stat(tpl_args, stat_id, value, {tbl=tbl, mod=mod_data})
end
if tpl_args.is_sellable == true and tpl_args._flags.sell_prices_override ~= true then
if tpl_args._flags.is_derived then -- Only derived items have explicit modifiers
-- fetch sell prices
results = m_cargo.query(
{'mods', 'mod_sell_prices'},
{'mods.id', 'mod_sell_prices.amount', 'mod_sell_prices.name'},
{
join = 'mods._pageID=mod_sell_prices._pageID',
-- must be non random mods to avoid accumulating sell prices of randomized modifiers
where = string.format(
'mod_sell_prices.amount IS NOT NULL AND mods.id IN ("%s")',
table.concat(non_random_mod_ids, '", "')
),
}
)
for _, data in ipairs(results) do
local mod_data = mods[data['mods.id']]
if not mod_data.is_implicit then
local values = {
name = data['mod_sell_prices.name'],
amount = tonumber(data['mod_sell_prices.amount']),
}
-- sell_prices is defined in core.map.sell_prices_override
tpl_args.sell_prices[values.name] = (tpl_args.sell_prices[values.name] or 0) + values.amount
end
end
end
end
end
if tpl_args.is_sellable == true and tpl_args._flags.sell_prices_override ~= true then
if m_util.table.length(tpl_args.sell_prices) > 0 then
-- Set sell price on page
for name, amount in pairs(tpl_args.sell_prices) do
-- sell_price_order is defined in core.map.sell_prices_override
tpl_args.sell_price_order[#tpl_args.sell_price_order+1] = name
tpl_args._store_data[#tpl_args._store_data+1] = {
_table = 'item_sell_prices',
amount = amount,
name = name,
}
end
table.sort(tpl_args.sell_price_order)
end
end
end
function s.process_stats(tpl_args)
tpl_args._stats = tpl_args._stats or {}
-- Extra stats - this is for when mods are not set, but we still need stats to calcuate new armour values etc
m_util.args.stats(tpl_args, {prefix='extra_'}) -- Populates tpl_args.extra_stats
for _, stat in ipairs(tpl_args.extra_stats) do
core.add_stat(tpl_args, stat.id, stat.value or stat)
end
-- Handle local item stat math - additions, increases, etc.
for key, stat_def in pairs(core.stat_map) do
local arg = stat_def.arg or key
local value = {}
if type(arg) == 'table' then
value.min = tpl_args[arg.min]
value.max = tpl_args[arg.max]
value.base_min = tpl_args[arg.min]
value.base_max = tpl_args[arg.max]
else
value.min = tpl_args[arg]
value.max = tpl_args[arg]
value.base = tpl_args[arg]
end
if value.min ~= nil and value.max ~= nil then
-- Determine whether stat is overridden; if so, other calculations can be ignored
local overridden = false
if stat_def.stats_override then
for id, v in pairs(stat_def.stats_override) do
if tpl_args._stats[id] then
-- If for some reason there is more than one of the same override stat, apply the final one.
local stat_value = tpl_args._stats[id][#tpl_args._stats[id]]
if stat_value then
if type(v) == 'table' then
value.min = v.min
value.max = v.max
overridden = true
elseif v then -- Use the stat value
value.min = stat_value.min
value.max = stat_value.max
overridden = true
end
end
end
end
end
if not overridden then
local operations = {'add', 'add_distance', 'increased', 'increased_inverse'}
for _, o in ipairs(operations) do
local stat_ids = stat_def['stats_' .. o]
if stat_ids then
local total = {min=0, max=0}
for _, id in ipairs(stat_ids) do
if tpl_args._stats[id] then
for _, s in ipairs(tpl_args._stats[id]) do
total.min = total.min + s.min
total.max = total.max + s.max
end
end
end
h.stat[o](value, total)
end
end
if stat_def.minimum ~= nil then
for _, k in ipairs({'min', 'max'}) do
if value[k] < stat_def.minimum then
value[k] = stat_def.minimum
end
end
end
end
if value.min == nil or value.max == nil then
value = nil
tpl_args[key] = nil
else
value.avg = (value.min + value.max) / 2
for short_key, range_def in pairs(c.range_map) do
tpl_args[stat_def.field .. range_def] = value[short_key]
end
-- process to HTML to use on list pages or other purposes
h.handle_range_args(tpl_args, key, stat_def.field, value, stat_def.html_fmt_options)
end
end
end
-- Transpose stats into cargo data
for _, tbl in ipairs({'_stats', '_random_stats'}) do
if tpl_args[tbl] then
for stat_id, stats in pairs(tpl_args[tbl]) do
for _, stat_data in ipairs(stats) do
table.insert(tpl_args._store_data, {
_table = 'item_stats',
id = stat_id,
min = stat_data.min,
max = stat_data.max,
avg = stat_data.avg,
mod_id = stat_data.mod and stat_data.mod.id or nil
})
end
end
end
end
end
function s.process_weapon_dps(tpl_args)
if tpl_args.tags and (m_util.table.contains(tpl_args.tags, 'staff')
or m_util.table.contains(tpl_args.tags, 'wand')
or m_util.table.contains(tpl_args.tags, 'sceptre')
or m_util.table.contains(tpl_args.tags, 'trap')) then
return false
end
for key, dps_def in pairs(core.dps_map) do
local damage = {
min = {},
max = {},
}
for var_type, value in pairs(damage) do
-- covers the min/max/avg range
for short_key, range_def in pairs(c.range_map) do
value[short_key] = 0
for _, damage_key in ipairs(dps_def.damage_args) do
value[short_key] = value[short_key] + (tpl_args[string.format('%s_%s%s', damage_key, var_type, range_def)] or 0)
end
end
end
local value = {}
for short_key, range_def in pairs(c.range_map) do
local result = (damage.min[short_key] + damage.max[short_key]) / 2 * tpl_args[string.format('attack_speed%s', range_def)]
value[short_key] = result
tpl_args[string.format('%s%s', dps_def.field, range_def)] = result
end
if value.avg > 0 then
h.handle_range_args(tpl_args, key, dps_def.field, value, dps_def.html_fmt_options)
end
end
end
function s.process_gem_variants(tpl_args)
if not tpl_args._flags.is_derived and tpl_args.metadata_id == nil then
-- If this is a base item, then it needs a metadata ID in order to find its variants.
return
end
local variants = {}
local title = mw.title.getCurrentTitle()
local current = {
page = title.prefixedText,
name = tpl_args.name,
id = tpl_args.metadata_id,
icon = tpl_args.inventory_icon,
current = true,
}
local results
local tables = {'items', 'skill_gems', 'skill'}
local fields = {
'items._pageName=page',
'items.name=name',
'items.metadata_id=id',
'items.inventory_icon=icon'
}
local qargs = {
join = 'items._pageID=skill_gems._pageID, items._pageID=skill._pageID',
orderBy = 'skill.skill_id ASC',
}
if tpl_args.class_id == 'Active Skill Gem' then
if tpl_args.is_vaal_skill_gem then
variants.vaal = current
-- Query base skill gem
qargs.where = string.format(
[[items.metadata_id IS NOT NULL
AND items.class_id = "Active Skill Gem"
AND skill_gems.vaal_variant_id = "%s"
AND skill_gems.is_vaal_skill_gem = false]],
tpl_args.metadata_id
)
if title.namespace == 0 then
qargs.where = qargs.where .. ' AND items._pageNamespace = 0'
end
results = m_cargo.query(tables, fields, qargs)
if #results > 0 then
variants.base = results[1]
end
else
-- Determine base skill gem
if tpl_args._flags.is_derived then
variants.base = {
page = tpl_args.base_item_page,
name = tpl_args.base_item,
id = tpl_args.base_item_id,
icon = tpl_args.base_item_icon,
}
else
variants.base = current
end
if tpl_args.vaal_variant_id ~= nil then
-- Query Vaal skill gem variant
qargs.where = string.format(
[[items.metadata_id = "%s"
AND items.class_id = "Active Skill Gem"
AND skill_gems.is_vaal_skill_gem = true]],
tpl_args.vaal_variant_id
)
if title.namespace == 0 then
qargs.where = qargs.where .. ' AND items._pageNamespace = 0'
end
results = m_cargo.query(tables, fields, qargs)
if #results > 0 then
variants.vaal = results[1]
end
end
end
if variants.base then
-- Query transfigured skill gem variants
qargs.where = string.format(
[[items.metadata_id IS NULL
AND items.base_item_id = "%s"
AND items.class_id = "Active Skill Gem"]],
variants.base.id
)
if title.namespace == 0 then
qargs.where = qargs.where .. ' AND items._pageNamespace = 0'
end
results = m_cargo.query(tables, fields, qargs)
if #results > 0 then
variants.transfigured = {}
for _, row in ipairs(results) do
row.current = row.page == title.prefixedText
table.insert(variants.transfigured, row)
end
end
end
elseif tpl_args.class_id == 'Support Skill Gem' then
if tpl_args.is_awakened_support_gem then
variants.awakened = current
-- Query base support gem
qargs.where = string.format(
[[items.metadata_id IS NOT NULL
AND items.class_id = "Support Skill Gem"
AND skill_gems.awakened_variant_id = "%s"
AND skill_gems.is_awakened_support_gem = false]],
tpl_args.metadata_id
)
if title.namespace == 0 then
qargs.where = qargs.where .. ' AND items._pageNamespace = 0'
end
results = m_cargo.query(tables, fields, qargs)
if #results > 0 then
variants.base = results[1]
end
else
variants.base = current
if tpl_args.awakened_variant_id ~= nil then
-- Query awakened support gem variant
qargs.where = string.format(
[[items.metadata_id = "%s"
AND items.class_id = "Support Skill Gem"
AND skill_gems.is_awakened_support_gem = true]],
tpl_args.awakened_variant_id
)
if title.namespace == 0 then
qargs.where = qargs.where .. ' AND items._pageNamespace = 0'
end
results = m_cargo.query(tables, fields, qargs)
if #results > 0 then
variants.awakened = results[1]
end
end
end
end
tpl_args._variants = {}
table.insert(tpl_args._variants, variants.base)
if variants.transfigured then
for _, v in ipairs(variants.transfigured) do
table.insert(tpl_args._variants, v)
end
end
table.insert(tpl_args._variants, variants.vaal)
table.insert(tpl_args._variants, variants.awakened)
end
function s.get_categories(tpl_args)
local cats = {}
if tpl_args._flags.is_prophecy then
table.insert(cats, i18n.categories.prophecies)
elseif tpl_args._flags.is_blight_item then
table.insert(cats, i18n.categories.oils)
elseif tpl_args._flags.is_talisman then
table.insert(cats, i18n.categories.talismans)
elseif tpl_args._flags.is_essence then
table.insert(cats, i18n.categories.essences)
elseif tpl_args._flags.is_fossil then
table.insert(cats, i18n.categories.fossils)
elseif tpl_args._flags.is_scarab then
table.insert(cats, i18n.categories.scarabs)
elseif tpl_args._flags.is_tattoo then
table.insert(cats, i18n.categories.tattoos)
elseif tpl_args._flags.is_delirium_orb then
table.insert(cats, i18n.categories.delirium_orbs)
elseif tpl_args._flags.is_catalyst then
table.insert(cats, i18n.categories.catalysts)
elseif tpl_args.class_id == 'Map' and tpl_args.rarity_id == 'normal' then
table.insert(cats, string.format(i18n.categories.maps_by_series, tpl_args.map_series))
elseif tpl_args.class_id == 'Microtransaction' and tpl_args.cosmetic_type and m_game.constants.item.cosmetic_item_types[tpl_args.cosmetic_type].cats then
for _, v in ipairs(m_game.constants.item.cosmetic_item_types[tpl_args.cosmetic_type].cats) do
table.insert(cats, v)
end
elseif m_game.constants.item.classes[tpl_args.class_id].cats then
for _, v in ipairs(m_game.constants.item.classes[tpl_args.class_id].cats) do
if tpl_args.rarity_id == 'unique' then
table.insert(cats, string.format(i18n.categories.unique_affix, v))
else
table.insert(cats, v)
end
end
else
table.insert(cats, tpl_args.class)
end
if tpl_args._flags.is_derived then
table.insert(cats, i18n.categories.derived_items)
else
table.insert(cats, i18n.categories.base_items)
end
for _, tag in ipairs(tpl_args.acquisition_tags) do
if cfg.acquisition_tags[tag] and cfg.acquisition_tags[tag].cat then
table.insert(cats, cfg.acquisition_tags[tag].cat)
end
end
for _, attr in ipairs(m_game.constants.attribute_order) do
if tpl_args[attr .. '_percent'] and tpl_args[attr .. '_percent'] ~= 0 then
table.insert(cats, string.format('%s %s', m_game.constants.attributes[attr].long_upper, tpl_args.class))
end
end
if cfg.class_groups.gems.keys[tpl_args.class_id] then
for _, tag in ipairs(tpl_args.gem_tags) do
table.insert(cats, string.format(i18n.categories.gem_tag_affix, tag))
end
end
if tpl_args._flags.is_derived and #tpl_args._defined_implicit_mods > 0 then
table.insert(cats, i18n.categories.implicit_modifier_override)
end
if #tpl_args.alternate_art_inventory_icons > 0 then
table.insert(cats, i18n.categories.alternate_artwork)
end
if tpl_args.release_version == nil then
table.insert(cats, i18n.categories.missing_release_version)
end
if tpl_args._flags.text_modifier and not tpl_args.suppress_improper_modifiers_category then
table.insert(cats, i18n.categories.improper_modifiers)
end
for _, k in ipairs({'invalid_recipe_parts', 'duplicate_recipes', 'duplicate_query_area_ids', 'sell_prices_override'}) do
if tpl_args._flags[k] then
table.insert(cats, i18n.categories[k])
end
end
if tpl_args.disable_automatic_recipes == true then
table.insert(cats, i18n.categories.automatic_recipes_disabled)
end
if tpl_args._flags.uses_deprecated_parameters == true then
mw.log('Deprecated parameters used:')
mw.logObject(tpl_args._deprecated_args)
table.insert(cats, i18n.categories.deprecated_parameters)
end
if tpl_args._flags.has_deprecated_skill_parameters then
table.insert(cats, i18n.categories.deprecated_skill_parameters)
end
return cats
end
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _main(tpl_args)
local t = os.clock() -- Start time (for logging)
tpl_args._flags = {}
tpl_args._store_data = {}
tpl_args._errors = {}
-- Item configuration
tpl_args._item_config = s.get_item_config(tpl_args)
-- Add table names to core.map and append range fields.
h.append_schema(tpl_args, tpl_args._item_config.tables)
-- Using general purpose function to handle release and removal versions
m_util.args.version(tpl_args)
-- Assume this is meant to be a derived item if any base item parameters are set
tpl_args._flags.is_derived = m_util.table.has_any_key(tpl_args, {'base_item_id', 'base_item_page', 'base_item'})
-- Must validate some argument early. It is required for future things
h.process_arguments(tpl_args, tpl_args._item_config.args)
-- Base item
s.process_base_item(tpl_args)
-- Process arguments that require a particular flag to be set
-- Used for subsets of item classes that have additional fields
for k, v in pairs(cfg.flagged_args) do
if tpl_args._flags[k] then
h.process_arguments(tpl_args, v.args)
end
end
-- Mods
s.process_mods(tpl_args)
-- Stats
s.process_stats(tpl_args)
-- Calculate and handle weapon dps
if cfg.class_groups.weapons.keys[tpl_args.class_id] then
s.process_weapon_dps(tpl_args)
end
-- Skill gems
if cfg.class_groups.gems.keys[tpl_args.class_id] then
h.process_skill_data(tpl_args)
end
-- Late argument processing
h.process_arguments(tpl_args, tpl_args._item_config.late_args)
-- Recipes
h.process_recipes(tpl_args)
-- Quest reward info
s.process_quest_rewards(tpl_args)
-- Gem variants
if cfg.class_groups.gems.keys[tpl_args.class_id] then
s.process_gem_variants(tpl_args)
end
-- ------------------------------------------------------------------------
-- Infobox handling
-- ------------------------------------------------------------------------
--
-- Tabs
--
local tabs
if type(tpl_args._variants) == 'table' and #tpl_args._variants > 1 then
tabs = mw.html.create('div')
:addClass('itembox-tabs')
for _, v in ipairs(tpl_args._variants) do
local tab = tabs:tag('span')
tab:addClass('itembox-tab')
if v.current then
tab:addClass('-selected')
end
if v.icon then
tab:wikitext(string.format('[[%s|34x34px|link=%s|alt=%s]]', v.icon, v.page, v.name))
else
tab:wikitext(m_util.html.wikilink(v.page, v.name))
end
end
end
--
-- Primary infobox
--
tpl_args._infobox_container = 'infobox'
local infobox = h.make_main_infobox(tpl_args)
if tpl_args.inventory_icon ~= nil and tpl_args.class_id ~= 'DivinationCard' then
infobox:tag('span')
:addClass('images')
:wikitext(string.format(
'[[%s|%sx%spx]]',
tpl_args.inventory_icon,
cfg.image_size_full * tpl_args.size_x,
cfg.image_size_full * tpl_args.size_y
))
end
tpl_args.infobox_html = tostring(infobox)
--
-- Secondary infobox
--
local extra_infobox = mw.html.create('div')
:addClass('item-box -' .. tpl_args.frame_type)
h.add_to_infobox_from_map(tpl_args, extra_infobox, c.extra_display_groups)
tpl_args.metabox_html = tostring(extra_infobox)
--
-- Output
--
local container = mw.html.create('div')
:addClass('infobox-page-container')
if tabs then
container:node(tabs)
end
container
:node(infobox)
:node(extra_infobox)
-- skill_screenshot is set in skill module
if tpl_args.skill_screenshot then
container:wikitext(string.format('<br>[[%s|300px]]', tpl_args.skill_screenshot))
end
local out = tostring(container)
local cats = s.get_categories(tpl_args)
out = out .. m_util.misc.add_category(cats, {ignore_blacklist=tpl_args.debug})
--
-- Misc
--
-- Also show the infobox for areas right away for maps, since they're both on the same page
local query_id
if tpl_args.rarity_id == 'normal' and tpl_args.map_area_id ~= nil then
query_id = tpl_args.map_area_id
elseif tpl_args.rarity_id == 'unique' and tpl_args.unique_map_area_id ~= nil then
query_id = tpl_args.unique_map_area_id
end
if query_id then
out = out .. h.query_area_info{cats=true, where=string.format('areas.id="%s"', query_id)}
end
-- ------------------------------------------------------------------------
-- Store cargo data
-- ------------------------------------------------------------------------
-- Store the infobox so it can be accessed with ease on other pages
tpl_args._infobox_container = 'inline'
tpl_args.html = tostring(h.make_main_infobox(tpl_args))
-- Map argument values for cargo storage
for _, table_name in ipairs(tpl_args._item_config.tables) do
tpl_args._store_data[table_name] = {
_table = table_name,
}
end
for k, v in pairs(tpl_args) do
local arg_def = core.map[k]
if arg_def ~= nil then
if arg_def.table ~= nil and arg_def.field ~= nil then
if arg_def.type == 'Integer' then
v = tonumber(string.format("%.0f", v))
if v ~= tpl_args[k] then
mw.log(string.format('Float value "%s" for integer field "%s.%s"', tpl_args[k], arg_def.table, arg_def.field))
end
end
if tpl_args._store_data[arg_def.table] == nil then
error(string.format('Missing data for table "%s", key "%s", \nvalue:\n "%s" \narg_def:\n%s', arg_def.table, k, mw.dumpObject(v), mw.dumpObject(arg_def)))
end
if type(tpl_args._store_data[arg_def.table]) ~= 'table' then
error(string.format('Unexpected format of data for table "%s", key "%s", \nvalue:\n "%s" \narg_def:\n%s', arg_def.table, k, mw.dumpObject(v), mw.dumpObject(arg_def)))
end
tpl_args._store_data[arg_def.table][arg_def.field] = v
elseif arg_def.table ~= nil and arg_def.field == nil then
error(string.format('Missing field for table "%s", key "%s", \nvalue:\n "%s" \narg_def:\n%s', arg_def.table, k, mw.dumpObject(v), mw.dumpObject(arg_def)))
elseif arg_def.table == nil and arg_def.field ~= nil then
mw.log(string.format('Possibly redundant argument "%s", value:\n "%s"', k, mw.dumpObject(v)))
end
end
end
-- Don't store cargo data in testing mode
if not tpl_args.test then
local attach = {}
for _, data in pairs(tpl_args._store_data) do
if attach[data._table] == nil then
local i = 0
for _, _ in pairs(data) do
i = i + 1
-- Don't attach to tables we don't store data to. _table is always present so we need to check for 2 or more entries.
if i > 1 then
attach[data._table] = true
-- One of the purposes of attaching is to facilitate table recreation.
-- Unfortunately, the Cargo extension's table recreation tool is
-- very slow and often fails to rebuild the entire table.
mw.getCurrentFrame():expandTemplate{
title = string.format(i18n.templates.cargo_attach, data._table),
args = {}
}
break
end
end
end
m_cargo.store(data, {
debug = tpl_args.debug,
sep = {
name_list = '�',
},
})
end
end
-- Show additional error messages in console to help fixing them
if #tpl_args._errors > 0 then
mw.log(table.concat(tpl_args._errors, '\n'))
end
if tpl_args.debug then
mw.log(os.clock() - t)
mw.log('Start logging tpl_args.')
mw.logObject(tpl_args)
mw.log('Stop logging tpl_args.')
mw.log('Start logging core.map.')
mw.logObject(core.map)
mw.log('Stop logging core.map.')
end
if tpl_args.test then
tpl_args.out = out
return tpl_args
end
return out
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template:Item
--
p.main = m_util.misc.invoker_factory(_main, {
parentFirst = true,
})
p.item = p.main
return p