[dismiss]
The wiki is currently a work in progress. If you'd like to help out, please check the Community Portal and our getting started guide. Also, check out our sister project on poewiki.net.
Module:Skill: Difference between revisions
Jump to navigation
Jump to search
(Module:Game should actually be included using mw.loadData()) |
Mefisto1029 (talk | contribs) No edit summary |
||
(29 intermediate revisions by 4 users not shown) | |||
Line 6: | Line 6: | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
require('Module:No globals') | |||
local m_util = require('Module:Util') | local m_util = require('Module:Util') | ||
local m_cargo = require('Module:Cargo') | local m_cargo = require('Module:Cargo') | ||
local m_game = mw.loadData('Module:Game') | -- Should we use the sandbox version of our submodules? | ||
local use_sandbox = m_util.misc.maybe_sandbox('Skill') | |||
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 | -- The cfg table contains all localisable strings and configuration, to make it | ||
-- easier to port this module to another wiki. | -- easier to port this module to another wiki. | ||
local cfg = mw.loadData('Module:Skill/config') | local cfg = use_sandbox and mw.loadData('Module:Skill/config/sandbox') or mw.loadData('Module:Skill/config') | ||
local mwlanguage = mw.language.getContentLanguage() | local mwlanguage = mw.language.getContentLanguage() | ||
Line 31: | Line 30: | ||
local h = {} | local h = {} | ||
function h.map_to_arg(tpl_args | function h.map_to_arg(tpl_args, properties, prefix_in, map, level, set_name, set_id) | ||
if map.fields then | if map.fields then | ||
for key, row in pairs(map.fields) do | for key, row in pairs(map.fields) do | ||
Line 37: | Line 36: | ||
local val = tpl_args[prefix_in .. row.name] | local val = tpl_args[prefix_in .. row.name] | ||
if row.func ~= nil then | if row.func ~= nil then | ||
val = row.func(tpl_args | val = row.func(tpl_args, val) | ||
end | end | ||
if val == nil and row.default ~= nil then | if val == nil and row.default ~= nil then | ||
Line 70: | Line 69: | ||
-- Deprecated parameters | -- Deprecated parameters | ||
if val and row.deprecated then | if val and row.deprecated then | ||
tpl_args. | tpl_args._flags.has_deprecated_skill_parameters = true | ||
if tpl_args.test then -- Log when testing | if tpl_args.test then -- Log when testing | ||
tpl_args.deprecated_parameters = tpl_args.deprecated_parameters or {} | tpl_args.deprecated_parameters = tpl_args.deprecated_parameters or {} | ||
Line 82: | Line 81: | ||
end | end | ||
function h. | function h.expand_costs_data(tpl_args, skill_levels) | ||
--[[ | |||
Expand costs data so that each cost type has its own column with amounts | |||
local | Assumptions: | ||
Cost types are always static | |||
Cost amounts can either be static or leveled, but not both | |||
--]] | |||
if skill_levels[0] then | |||
local cost_types = m_util.cast.table(skill_levels[0].cost_types) | |||
if #cost_types > 0 then | |||
for _, level_data in pairs(skill_levels) do | |||
if type(level_data) == 'table' and level_data.cost_amounts then | |||
local cost_amounts = m_util.cast.table(level_data.cost_amounts, {callback = m_util.cast.number}) | |||
for i=1, #cost_types do | |||
local type = cost_types[i] | |||
local amount = cost_amounts[i] | |||
if amount then | |||
level_data['cost_' .. type] = amount | |||
end | |||
end | |||
end | |||
end | end | ||
end | end | ||
Line 103: | Line 107: | ||
end | end | ||
function h.stats(tpl_args | function h.stats(tpl_args, prefix_in, level) | ||
for i=1, | for i=1, math.huge do -- repeat until no more stats are found | ||
local stat_prefix = string.format('%s%s%d_', prefix_in, i18n.parameters.skill.stat, i) -- level<level>_stat<i>_ | local stat_prefix = string.format('%s%s%d_', prefix_in, i18n.parameters.skill.stat, i) -- level<level>_stat<i>_ | ||
local stat = { | local stat = { | ||
Line 110: | Line 114: | ||
value = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.value.name], --level<level>_stat<i>_value | value = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.value.name], --level<level>_stat<i>_value | ||
} | } | ||
if stat.id | if stat.id == nil or stat.value == nil then | ||
local properties = { | break | ||
end | |||
local properties = { | |||
_table = tables.skill_stats_per_level.table, | |||
[tables.skill_stats_per_level.fields.level.field] = level, | |||
} | |||
h.map_to_arg(tpl_args, properties, stat_prefix, tables.skill_stats_per_level, level, 'stats', i) | |||
tpl_args.skill_levels.has_stats = true | |||
if not tpl_args.test then | |||
m_cargo.store(properties) | |||
end | end | ||
end | end | ||
end | end | ||
function h.int_value_or_na(tpl_args | function h.int_value_or_na(tpl_args, tblrow, value, tmap) | ||
value = tonumber(value) | value = tonumber(value) | ||
if value == nil then | if value == nil then | ||
tblrow:node(m_util.html. | tblrow:node(m_util.html.table_cell('na')) | ||
else | else | ||
-- value = mwlanguage:formatNum(value) -- Removed for now. lang:formatNum() returns a string, which causes issues for formatting | -- value = mwlanguage:formatNum(value) -- Removed for now. lang:formatNum() returns a string, which causes issues for formatting | ||
Line 134: | Line 139: | ||
value = string.format(tmap.fmt, value) | value = string.format(tmap.fmt, value) | ||
elseif type(tmap.fmt) == 'function' then | elseif type(tmap.fmt) == 'function' then | ||
value = string.format(tmap.fmt(tpl_args | value = string.format(tmap.fmt(tpl_args) or '%s', value) | ||
end | end | ||
end | end | ||
Line 145: | Line 150: | ||
h.cast = {} | h.cast = {} | ||
function h.cast.wrap ( | function h.cast.wrap(func) | ||
return function(tpl_args | return function(tpl_args, value) | ||
if value == nil then | if value == nil then | ||
return nil | return nil | ||
end | end | ||
return func(value) | |||
end | end | ||
end | end | ||
Line 158: | Line 162: | ||
h.display.factory = {} | h.display.factory = {} | ||
function h.display.factory.value(args) | function h.display.factory.value(args) | ||
return function (tpl_args | return function (tpl_args) | ||
args.fmt = args.fmt or tables.static.fields[args.key].fmt | args.fmt = args.fmt or tables.static.fields[args.key].fmt | ||
local value = tpl_args[args.key] | local value = tpl_args[args.key] | ||
Line 170: | Line 174: | ||
function h.display.factory.range_value(args) | function h.display.factory.range_value(args) | ||
return function (tpl_args | return function (tpl_args) | ||
local value = {} | local value = {} | ||
if args.set_name and args.set_id then | if args.set_name and args.set_id then | ||
Line 184: | Line 188: | ||
value.max = tpl_args.skill_levels[0][args.set_name][args.set_id][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.set_name][args.set_id][args.key] | value.max = tpl_args.skill_levels[0][args.set_name][args.set_id][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.set_name][args.set_id][args.key] | ||
else | else | ||
value.min = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[1][args.key] | value.min = tpl_args.skill_levels[0][args.key] | ||
value.max = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.key] | if value.min == nil or type(value.min) == 'table' and #value.min == 0 then | ||
value.min = tpl_args.skill_levels[1][args.key] | |||
end | |||
value.max = tpl_args.skill_levels[0][args.key] | |||
if value.max == nil or type(value.max) == 'table' and #value.max == 0 then | |||
value.max = tpl_args.skill_levels[tpl_args.max_level][args.key] | |||
end | |||
if type(value.min) == 'table' and type(value.max) == 'table' and args.key_index then | |||
value.min = value.min[args.key_index] | |||
value.max = value.max[args.key_index] | |||
end | |||
end | end | ||
if value.min == nil or value.max == nil then | if value.min == nil or value.max == nil then | ||
return | -- property not set for this skill | ||
return nil | |||
end | end | ||
local map = args.map or tables.progression | local map = args.map or tables.progression | ||
local options = { | |||
fmt=args.fmt or map.fields[args.key].fmt, | fmt=args.fmt or map.fields[args.key] and map.fields[args.key].fmt, | ||
color=false, | color=false, | ||
}) | } | ||
if type(value.min) == 'table' and type(value.max) == 'table' then | |||
local formatted_values = {} | |||
for i=1, #value.min do | |||
formatted_values[i] = m_util.html.format_value(tpl_args, {min = value.min[i], max = value.max[i]}, options) | |||
end | |||
return formatted_values | |||
end | |||
return m_util.html.format_value(tpl_args, value, options) | |||
end | end | ||
end | end | ||
function h.display.factory.radius(args) | function h.display.factory.radius(args) | ||
return function (tpl_args | return function (tpl_args) | ||
local radius = tpl_args['radius' .. args.key] | local radius = tpl_args['radius' .. args.key] | ||
if radius == nil then | if radius == nil then | ||
Line 214: | Line 234: | ||
end | end | ||
end | end | ||
end | |||
function h.query_skill(tpl_args) | |||
local fields = { | |||
'skill._pageID=_pageID', | |||
} | |||
local query = { | |||
groupBy = 'skill._pageID', | |||
} | |||
local results = {} | |||
local search_param | |||
if tpl_args.skill_id then -- Query by skill id | |||
query.where = string.format('skill_id="%s"', tpl_args.skill_id) | |||
search_param = 'skill_id' | |||
else -- Query by page name | |||
local page = tpl_args.page or mw.title.getCurrentTitle().prefixedText | |||
query.where = string.format('_pageName="%s"', page) | |||
search_param = 'page' | |||
end | |||
results = m_cargo.query({tables.static.table}, fields, query) | |||
if #results == 0 then | |||
-- No results found | |||
error(string.format(i18n.errors.validate_skill.no_results_found, search_param, tpl_args[search_param])) | |||
elseif #results > 1 then | |||
-- More than one result found | |||
error(string.format(i18n.errors.validate_skill.many_results_found, search_param, tpl_args[search_param])) | |||
end | |||
return results[1] | |||
end | end | ||
Line 254: | Line 302: | ||
field = 'skill_icon', | field = 'skill_icon', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
if tpl_args.active_skill_name then | if value then | ||
value = string.format(i18n.files.skill_icon, value) | |||
elseif tpl_args.active_skill_name then | |||
value = string.format(i18n.files.skill_icon, tpl_args.active_skill_name) | |||
else | |||
value = nil | |||
end | end | ||
return value | |||
end, | end, | ||
}, | }, | ||
Line 264: | Line 317: | ||
field = 'item_class_id_restriction', | field = 'item_class_id_restriction', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = function(tpl_args | func = function(tpl_args, value) | ||
if value == nil then | if value == nil then | ||
return nil | return nil | ||
Line 281: | Line 334: | ||
field = 'item_class_restriction', | field = 'item_class_restriction', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = function(tpl_args | func = function(tpl_args, value) | ||
if tpl_args.item_class_id_restriction == nil then | if tpl_args.item_class_id_restriction == nil then | ||
return | return | ||
Line 305: | Line 358: | ||
name = i18n.parameters.skill.stat_text, | name = i18n.parameters.skill.stat_text, | ||
field = 'stat_text', | field = 'stat_text', | ||
type = 'Text', | type = 'Text', | ||
func = nil, | func = nil, | ||
Line 355: | Line 402: | ||
field = 'skill_screenshot', | field = 'skill_screenshot', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
if tpl_args.skill_screenshot_file then | |||
if tpl_args.skill_screenshot_file | tpl_args._flags.has_deprecated_skill_parameters = true | ||
value = string.format('File:%s', tpl_args.skill_screenshot_file) | |||
elseif | elseif value then | ||
value = string.format(i18n.files.skill_screenshot, value) | |||
elseif tpl_args.active_skill_name then | elseif tpl_args.active_skill_name then | ||
-- When this parameter is set manually, we assume/expect it to | -- When this parameter is set manually, we assume/expect it to exist, but otherwise it probably doesn't and we don't need dead links in that case | ||
value = string.format(i18n.files.skill_screenshot, tpl_args.active_skill_name) | |||
page = mw.title.new( | local page = mw.title.new(value) | ||
if page == nil or not page.exists then | if page == nil or not page.exists then | ||
value = nil | |||
end | end | ||
else | |||
value = nil | |||
end | end | ||
return | return value | ||
end, | end, | ||
}, | }, | ||
Line 384: | Line 433: | ||
type = 'Text', | type = 'Text', | ||
func = nil, | func = nil, | ||
}, | }, | ||
}, | }, | ||
Line 438: | Line 470: | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
cost_multiplier = { | |||
name = i18n.parameters.skill. | name = i18n.parameters.skill.cost_multiplier, | ||
field = ' | field = 'cost_multiplier', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | |||
attack_time = { | |||
name = i18n.parameters.skill.attack_time, | |||
field = 'attack_time', | |||
type = 'Float', | |||
func = h.cast.wrap(m_util.cast.number), | |||
fmt = '%.2f ' .. m_game.units.seconds.short_lower, | |||
}, | }, | ||
critical_strike_chance = { | critical_strike_chance = { | ||
Line 513: | Line 552: | ||
fmt = '%.2f ' .. m_game.units.seconds.short_lower, | fmt = '%.2f ' .. m_game.units.seconds.short_lower, | ||
}, | }, | ||
cost_types = { | |||
name = i18n.parameters.skill.cost_types, | |||
name = i18n.parameters.skill. | field = 'cost_types', | ||
field = ' | type = 'List (,) of String', | ||
func = function(tpl_args, value) | |||
if value == nil then | |||
return nil | |||
end | |||
value = m_util.cast.table(value) | |||
for _, v in ipairs(value) do | |||
if m_game.constants.skill.cost_types[v] == nil then | |||
error(string.format(i18n.errors.skill.invalid_cost_type, v)) | |||
end | |||
end | |||
return value | |||
end, | |||
}, | |||
cost_amounts = { | |||
name = i18n.parameters.skill.cost_amounts, | |||
field = 'cost_amounts', | |||
type = 'List (,) of Integer', | |||
func = function(tpl_args, value) | |||
if value == nil then | |||
return nil | |||
end | |||
value = m_util.cast.table(value, {callback = m_util.cast.number}) | |||
return value | |||
end, | |||
}, | |||
mana_reservation_flat = { | |||
name = i18n.parameters.skill.mana_reservation_flat, | |||
field = 'mana_reservation_flat', | |||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
mana_reservation_percent = { | |||
name = i18n.parameters.skill. | name = i18n.parameters.skill.mana_reservation_percent, | ||
field = ' | field = 'mana_reservation_percent', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
life_reservation_flat = { | |||
name = i18n.parameters.skill.life_reservation_flat, | |||
field = 'life_reservation_flat', | |||
name = | |||
field = ' | |||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | |||
func = h.cast.wrap(m_util.cast. | |||
}, | }, | ||
life_reservation_percent = { | |||
name = i18n.parameters.skill.life_reservation_percent, | |||
field = 'life_reservation_percent', | |||
name = | |||
field = ' | |||
type = 'Integer', | type = 'Integer', | ||
func = | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
spirit_reservation_flat = { | |||
name = | name = i18n.parameters.skill.spirit_reservation_flat, | ||
field = ' | field = 'spirit_reservation_flat', | ||
type = 'Integer', | type = 'Integer', | ||
func = | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
-- from gem experience, optional | |||
name = i18n.parameters.skill. | experience = { | ||
field = ' | name = i18n.parameters.skill.experience, | ||
field = 'experience', | |||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
} | stat_text = { | ||
name = i18n.parameters.skill.stat_text, | |||
field = 'stat_text', | |||
type = 'Text', | |||
func = h.cast.wrap(m_util.cast.text), | |||
}, | |||
} | |||
} | } | ||
Line 680: | Line 713: | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_multiplier', | ||
header = i18n.progression. | header = i18n.progression.cost_multiplier, | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | |||
{ | |||
field = 'attack_time', | |||
header = i18n.progression.attack_time, | |||
fmt = '%.2f ' .. m_game.units.seconds.short_lower, | |||
}, | }, | ||
{ | { | ||
Line 688: | Line 726: | ||
header = i18n.progression.critical_strike_chance, | header = i18n.progression.critical_strike_chance, | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_Mana', | ||
header = i18n.progression.mana_cost, | header = i18n.progression.mana_cost, | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_Life', | ||
header = i18n.progression.life_cost, | header = i18n.progression.life_cost, | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_ES', | ||
header = i18n.progression. | header = i18n.progression.energy_shield_cost, | ||
}, | |||
{ | |||
field = 'cost_Rage', | |||
header = i18n.progression.rage_cost, | |||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_ManaPercent', | ||
header = i18n.progression. | header = i18n.progression.mana_cost, | ||
fmt = '%s%%', | |||
}, | }, | ||
{ | { | ||
field = ' | field = 'cost_LifePercent', | ||
header = i18n.progression. | header = i18n.progression.life_cost, | ||
fmt = '%s%%', | |||
}, | }, | ||
{ | { | ||
field = ' | field = 'mana_reservation_flat', | ||
header = i18n.progression.mana_reserved, | header = i18n.progression.mana_reserved, | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'mana_reservation_percent', | ||
header = i18n.progression.mana_reserved, | header = i18n.progression.mana_reserved, | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'life_reservation_flat', | ||
header = i18n.progression.life_reserved, | header = i18n.progression.life_reserved, | ||
}, | }, | ||
{ | { | ||
field = ' | field = 'life_reservation_percent', | ||
header = i18n.progression.life_reserved, | header = i18n.progression.life_reserved, | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | |||
{ | |||
field = 'spirit_reservation_flat', | |||
header = i18n.progression.spirit_reserved, | |||
}, | }, | ||
{ | { | ||
Line 795: | Line 826: | ||
{ | { | ||
header = i18n.infobox.skill_id, | header = i18n.infobox.skill_id, | ||
func = function (tpl_args | func = function (tpl_args) | ||
return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.skill_id) | return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.skill_id) | ||
end | end | ||
Line 801: | Line 832: | ||
{ | { | ||
header = i18n.infobox.skill_icon, | header = i18n.infobox.skill_icon, | ||
func = function (tpl_args | func = function (tpl_args) | ||
if tpl_args.skill_icon then | if tpl_args.skill_icon then | ||
return string.format('[[%s]]', tpl_args.skill_icon) | return string.format('[[%s]]', tpl_args.skill_icon) | ||
Line 809: | Line 840: | ||
{ | { | ||
header = i18n.infobox.cast_time, | header = i18n.infobox.cast_time, | ||
func = function (tpl_args | func = function (tpl_args) | ||
local value = tpl_args.cast_time | local value = tpl_args.cast_time | ||
if value then | if value then | ||
Line 822: | Line 853: | ||
{ | { | ||
header = i18n.infobox.item_class_restrictions, | header = i18n.infobox.item_class_restrictions, | ||
func = function (tpl_args | func = function (tpl_args) | ||
if tpl_args.item_class_restriction == nil then | if tpl_args.item_class_restriction == nil then | ||
return | return | ||
Line 853: | Line 884: | ||
func = h.display.factory.range_value{key='level_requirement'}, | func = h.display.factory.range_value{key='level_requirement'}, | ||
}, | }, | ||
-- | -- ignore attrbiutes? | ||
{ | { | ||
header = i18n.infobox. | header = i18n.infobox.cost_multiplier, | ||
func = h.display.factory.range_value{key=' | func = h.display.factory.range_value{key='cost_multiplier'}, | ||
}, | |||
{ | |||
header = i18n.infobox.attack_time, | |||
func = h.display.factory.range_value{key='attack_time'}, | |||
}, | }, | ||
{ | { | ||
Line 864: | Line 899: | ||
{ | { | ||
header = i18n.infobox.cost, | header = i18n.infobox.cost, | ||
func = function (tpl_args | func = function (tpl_args) | ||
local parts = {} | |||
for k, v in pairs(m_game.constants.skill.cost_types) do | |||
local key = 'cost_' .. k | |||
local fmt | |||
if string.find(k, 'Percent', 1, true) then | |||
fmt = '%s%% %s' | |||
else | |||
fmt = '%s %s' | |||
end | end | ||
local range = h.display.factory.range_value{key=key}(tpl_args) | |||
if range then | |||
parts[#parts+1] = string.format(fmt, range, v.long_lower) | |||
end | end | ||
end | end | ||
return table.concat( | return table.concat(parts, ', ') | ||
end, | end, | ||
}, | }, | ||
{ | { | ||
header = i18n.infobox.reservation, | header = i18n.infobox.reservation, | ||
func = function (tpl_args | func = function (tpl_args) | ||
local parts = {} | |||
local keys = { | |||
{ | |||
key = 'mana_reservation_flat', | |||
fmt = '%s ' .. m_game.constants.skill.cost_types['Mana'].long_lower, | |||
}, | |||
{ | |||
key = 'mana_reservation_percent', | |||
fmt = '%s%% ' .. m_game.constants.skill.cost_types['Mana'].long_lower, | |||
}, | |||
{ | |||
key = 'life_reservation_flat', | |||
fmt = '%s ' .. m_game.constants.skill.cost_types['Life'].long_lower, | |||
}, | |||
{ | |||
key = 'life_reservation_percent', | |||
fmt = '%s%% ' .. m_game.constants.skill.cost_types['Life'].long_lower, | |||
}, | |||
{ | |||
key = 'spirit_reservation_flat', | |||
fmt = '%s ' .. 'spirit', | |||
}, | |||
} | |||
for _, v in ipairs(keys) do | |||
local range = h.display.factory.range_value{key=v.key}(tpl_args) | |||
if range then | |||
parts[#parts+1] = string.format(v.fmt, range) | |||
end | end | ||
end | end | ||
return table.concat(parts, ', ') | |||
return table.concat( | |||
end, | end, | ||
}, | }, | ||
Line 982: | Line 1,003: | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Main functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local function _process_skill_data(tpl_args) | |||
--[[ | |||
Processes skill data from tpl_args. | |||
Stores skill data in cargo tables. | |||
Attaches page to cargo tables. | |||
--]] | |||
-- | |||
-- | |||
tpl_args = tpl_args or {} | tpl_args = tpl_args or {} | ||
tpl_args._flags = tpl_args._flags or {} | |||
tpl_args.skill_levels = { | tpl_args.skill_levels = { | ||
[0] = {}, | [0] = {}, | ||
Line 1,020: | Line 1,033: | ||
if q.stat_text then | if q.stat_text then | ||
tpl_args.skill_quality[#tpl_args.skill_quality+1] = q | tpl_args.skill_quality[#tpl_args.skill_quality+1] = q | ||
m_cargo.store( | m_cargo.store(q) | ||
q.stats = {} | q.stats = {} | ||
Line 1,036: | Line 1,049: | ||
if s.id and s.value then | if s.id and s.value then | ||
q.stats[#q.stats+1] = s | q.stats[#q.stats+1] = s | ||
m_cargo.store( | m_cargo.store(s) | ||
end | end | ||
s._table = nil | s._table = nil | ||
until s.id == nil or s.value == nil | until s.id == nil or s.value == nil | ||
end | end | ||
until q.stat_text == nil | until q.stat_text == nil | ||
if #tpl_args.skill_quality > 1 then | |||
-- Gem has alternative qualtiy | |||
tpl_args._flags.is_alt_quality_gem = true | |||
end | end | ||
Line 1,089: | Line 1,080: | ||
[tables.progression.fields.level.field] = i | [tables.progression.fields.level.field] = i | ||
} | } | ||
h.map_to_arg(tpl_args | h.map_to_arg(tpl_args, properties, prefix, tables.progression, i) | ||
if not tpl_args.test then | if not tpl_args.test then | ||
m_cargo.store( | m_cargo.store(properties) | ||
end | end | ||
h.stats(tpl_args, prefix, i) | |||
h.stats(tpl_args | |||
end | end | ||
tpl_args.max_level = tpl_args.max_level or level_count | tpl_args.max_level = tpl_args.max_level or level_count | ||
Line 1,105: | Line 1,095: | ||
[tables.progression.fields.level.field] = 0 | [tables.progression.fields.level.field] = 0 | ||
} | } | ||
h.map_to_arg(tpl_args | h.map_to_arg(tpl_args, properties, prefix, tables.progression, 0) | ||
if not tpl_args.test then | if not tpl_args.test then | ||
m_cargo.store( | m_cargo.store(properties) | ||
end | end | ||
end | end | ||
-- Expand costs data | |||
h.expand_costs_data(tpl_args, tpl_args.skill_levels) | |||
-- Handle static arguments | -- Handle static arguments | ||
Line 1,116: | Line 1,109: | ||
[tables.static.fields.max_level.field] = tpl_args.max_level | [tables.static.fields.max_level.field] = tpl_args.max_level | ||
} | } | ||
h.map_to_arg(tpl_args | h.map_to_arg(tpl_args, properties, '', tables.static) | ||
h.stats(tpl_args, prefix, 0) | |||
h.stats(tpl_args | |||
-- Build infobox | -- Build infobox | ||
local infobox = mw.html.create('span') | local infobox = mw.html.create('span') | ||
infobox: | infobox:addClass('skill-box') | ||
local tbl = infobox:tag('table') | local tbl = infobox:tag('table') | ||
tbl: | tbl:addClass('wikitable skill-box-table') | ||
for _, infobox_data in ipairs(data.infobox_table) do | for _, infobox_data in ipairs(data.infobox_table) do | ||
local display = infobox_data.func(tpl_args | local display = infobox_data.func(tpl_args) | ||
if display | if type(display) == 'string' and string.len(display) > 0 then | ||
if infobox_data.fmt ~= nil then | |||
if type(infobox_data.fmt) == 'string' then | |||
display = string.format(infobox_data.fmt, display) | |||
elseif type(infobox_data.fmt) == 'function' then | |||
display = string.format(infobox_data.fmt(tpl_args) or '%s', display) | |||
end | |||
end | end | ||
local tr = tbl:tag('tr') | local tr = tbl:tag('tr') | ||
if infobox_data.header then | if infobox_data.header then | ||
local header_text | local header_text | ||
if type(infobox_data.header) == 'function' then | if type(infobox_data.header) == 'function' then | ||
header_text = infobox_data.header(tpl_args | header_text = infobox_data.header(tpl_args) | ||
else | else | ||
header_text = infobox_data.header | header_text = infobox_data.header | ||
Line 1,150: | Line 1,142: | ||
local td = tr:tag('td') | local td = tr:tag('td') | ||
td:wikitext(display) | td:wikitext(display) | ||
td: | td:addClass(infobox_data.class or 'tc -value') | ||
if infobox_data.header == nil then | if infobox_data.header == nil then | ||
td:attr('colspan', 2) | td:attr('colspan', 2) | ||
Line 1,161: | Line 1,153: | ||
properties[tables.static.fields.html.field] = infobox | properties[tables.static.fields.html.field] = infobox | ||
if not tpl_args.test then | if not tpl_args.test then | ||
m_cargo.store( | m_cargo.store(properties) | ||
end | end | ||
Line 1,173: | Line 1,165: | ||
attach_tables[#attach_tables+1] = tables.skill_quality.table | attach_tables[#attach_tables+1] = tables.skill_quality.table | ||
attach_tables[#attach_tables+1] = tables.skill_quality_stats.table | attach_tables[#attach_tables+1] = tables.skill_quality_stats.table | ||
end | end | ||
if tpl_args.skill_levels.has_stats then | if tpl_args.skill_levels.has_stats then | ||
Line 1,182: | Line 1,170: | ||
end | end | ||
for _, table_name in ipairs(attach_tables) do | for _, table_name in ipairs(attach_tables) do | ||
mw.getCurrentFrame():expandTemplate{ | |||
title = string.format(i18n.templates.cargo_attach, table_name), | title = string.format(i18n.templates.cargo_attach, table_name), | ||
args = {} | args = {} | ||
Line 1,197: | Line 1,185: | ||
end | end | ||
local function _skill(tpl_args) | |||
function | |||
--[[ | --[[ | ||
Display skill infobox | Display skill infobox | ||
Line 1,209: | Line 1,194: | ||
]] | ]] | ||
-- Handle skill data and get infobox | -- Handle skill data and get infobox | ||
local infobox = | local infobox = _process_skill_data(tpl_args) | ||
-- Container | -- Container | ||
local container = mw.html.create('span') | local container = mw.html.create('span') | ||
container | container | ||
: | :addClass('skill-box-page-container') | ||
:wikitext(infobox) | :wikitext(infobox) | ||
if tpl_args.skill_screenshot then | if tpl_args.skill_screenshot then | ||
Line 1,229: | Line 1,209: | ||
-- Generic messages on the page: | -- Generic messages on the page: | ||
out = {} | local out = {} | ||
if mw.ustring.find(tpl_args.skill_id, '_') then | if mw.ustring.find(tpl_args.skill_id, '_') then | ||
out[#out+1] = | out[#out+1] = mw.getCurrentFrame():expandTemplate{ | ||
title = i18n.templates.incorrect_title, | title = i18n.templates.incorrect_title, | ||
args = {title=tpl_args.skill_id} | args = {title=tpl_args.skill_id} | ||
Line 1,251: | Line 1,231: | ||
-- Categories | -- Categories | ||
local cats = {i18n.categories.skill_data} | local cats = {i18n.categories.skill_data} | ||
if tpl_args. | if tpl_args._flags.has_deprecated_skill_parameters then | ||
cats[#cats+1] = i18n.categories.deprecated_parameters | cats[#cats+1] = i18n.categories.deprecated_parameters | ||
end | end | ||
Line 1,258: | Line 1,238: | ||
end | end | ||
function | local function _progression(tpl_args) | ||
--[[ | --[[ | ||
Displays the level progression for the skill gem. | Displays the level progression for the skill gem. | ||
Line 1,266: | Line 1,246: | ||
= p.progression{page='Reave'} | = p.progression{page='Reave'} | ||
]] | ]] | ||
-- Parse column arguments: | -- Parse column arguments: | ||
Line 1,299: | Line 1,274: | ||
-- Query skill data | -- Query skill data | ||
local skill_data = h.query_skill(tpl_args) | |||
local skill_data | |||
-- Query progression data | -- Query progression data | ||
fields = {} | local fields = {} | ||
for _, fmap in pairs(tables.progression.fields) do | for _, fmap in pairs(tables.progression.fields) do | ||
fields[#fields+1] = fmap.field | fields[#fields+1] = fmap.field | ||
end | end | ||
query = { | local query = { | ||
where = string.format( | where = string.format( | ||
' | '_pageID="%s"', | ||
skill_data | skill_data._pageID | ||
), | ), | ||
groupBy = string.format( | groupBy = string.format( | ||
'_pageID, %s', | '_pageID, %s', | ||
tables.progression.fields.level.field | tables.progression.fields.level.field | ||
), | ), | ||
} | } | ||
results = m_cargo.query({tables.progression.table}, fields, query) | local results = m_cargo.query({tables.progression.table}, fields, query) | ||
if # | |||
-- Re-index by level | |||
skill_data.levels = {} | |||
for _, v in ipairs(results) do | |||
skill_data.levels[tonumber(v.level)] = v | |||
end | |||
if #skill_data.levels == 0 then | |||
error(i18n.errors.progression.missing_level_data) | error(i18n.errors.progression.missing_level_data) | ||
end | end | ||
-- Expand costs data | |||
h.expand_costs_data(tpl_args, skill_data.levels) | |||
-- Set up html table headers | -- Set up html table headers | ||
headers = {} | local headers = {} | ||
for _, row in ipairs(skill_data.levels) do | for _, row in ipairs(skill_data.levels) do | ||
for k, v in pairs(row) do | for k, v in pairs(row) do | ||
Line 1,428: | Line 1,314: | ||
local tbl = mw.html.create('table') | local tbl = mw.html.create('table') | ||
tbl | tbl | ||
: | :addClass('wikitable responsive-table skill-progression-table') | ||
local head = tbl:tag('tr') | local head = tbl:tag('tr') | ||
for _, tmap in pairs(data.skill_progression_table) do | for _, tmap in pairs(data.skill_progression_table) do | ||
if headers[tmap.field] then | if headers[tmap.field] then | ||
local text = type(tmap.header) == 'function' and tmap.header(tpl_args | local text = type(tmap.header) == 'function' and tmap.header(tpl_args) or tmap.header | ||
head | head | ||
:tag('th') | :tag('th') | ||
Line 1,463: | Line 1,349: | ||
for _, tmap in pairs(data.skill_progression_table) do | for _, tmap in pairs(data.skill_progression_table) do | ||
if headers[tmap.field] then | if headers[tmap.field] then | ||
h.int_value_or_na(tpl_args | h.int_value_or_na(tpl_args, tblrow, row[tmap.field], tmap) | ||
end | end | ||
end | end | ||
Line 1,486: | Line 1,372: | ||
end | end | ||
if #match == 0 then | if #match == 0 then | ||
tblrow:node(m_util.html. | tblrow:node(m_util.html.table_cell('na')) | ||
else | else | ||
-- used to find broken progression due to game updates | -- used to find broken progression due to game updates | ||
Line 1,505: | Line 1,391: | ||
end | end | ||
end | end | ||
if headers[tables.progression.fields.experience.field] then | if headers[tables.progression.fields.experience.field] then | ||
experience = tonumber(row[tables.progression.fields.experience.field]) | experience = tonumber(row[tables.progression.fields.experience.field]) | ||
if experience ~= nil then | if experience ~= nil then | ||
h.int_value_or_na(tpl_args | h.int_value_or_na(tpl_args, tblrow, experience - lastexp, {}) | ||
lastexp = experience | lastexp = experience | ||
else | else | ||
tblrow:node(m_util.html. | tblrow:node(m_util.html.table_cell('na')) | ||
end | end | ||
h.int_value_or_na(tpl_args | h.int_value_or_na(tpl_args, tblrow, experience, {}) | ||
end | end | ||
end | end | ||
Line 1,528: | Line 1,412: | ||
end | end | ||
return tostring(tbl) .. m_util.misc.add_category(cats) | return tostring(tbl) .. m_util.misc.add_category(cats) | ||
end | |||
local function _quality(tpl_args) | |||
--[[ | |||
Displays a table comparing the stats of superior gem quality with | |||
alternative quality types. | |||
--]] | |||
-- Query skill data | |||
local skill_data = h.query_skill(tpl_args) | |||
-- Query progression data | |||
local fields = {} | |||
for _, fmap in pairs(tables.skill_quality.fields) do | |||
fields[#fields+1] = fmap.field | |||
end | |||
local query = { | |||
where = string.format( | |||
'_pageID="%s"', | |||
skill_data._pageID | |||
), | |||
groupBy = string.format( | |||
'_pageID, %s', | |||
tables.skill_quality.fields.set_id.field | |||
), | |||
orderBy = string.format( | |||
'%s ASC', | |||
tables.skill_quality.fields.set_id.field | |||
), | |||
} | |||
skill_data.quality = m_cargo.query({tables.skill_quality.table}, fields, query) | |||
if #skill_data.quality == 0 then | |||
error(i18n.errors.quality.missing_quality_data) | |||
end | |||
-- Build table | |||
local tbl = mw.html.create('table') | |||
tbl | |||
:addClass('wikitable skill-quality-table') | |||
:tag('tr') | |||
:tag('th') | |||
:wikitext(i18n.quality.type) | |||
:done() | |||
:tag('th') | |||
:wikitext(i18n.quality.stats) | |||
:done() | |||
:tag('th') | |||
:wikitext(i18n.quality.weight) | |||
:done() | |||
for k, row in ipairs(skill_data.quality) do | |||
tbl | |||
:tag('tr') | |||
:tag('td') | |||
:wikitext(m_game.constants.item.gem_quality_types[k].long_upper) | |||
:done() | |||
:tag('td') | |||
:addClass('tc -mod') | |||
:wikitext(skill_data.quality[k].stat_text) | |||
:done() | |||
:tag('td') | |||
:wikitext(skill_data.quality[k].weight) | |||
:done() | |||
end | |||
return tostring(tbl) | |||
end | |||
-- ---------------------------------------------------------------------------- | |||
-- Exported functions | |||
-- ---------------------------------------------------------------------------- | |||
local p = {} | |||
p.table_skills = m_cargo.declare_factory{data=tables.static} | |||
p.table_skill_levels = m_cargo.declare_factory{data=tables.progression} | |||
p.table_skill_stats_per_level = m_cargo.declare_factory{data=tables.skill_stats_per_level} | |||
p.table_skill_quality = m_cargo.declare_factory{data=tables.skill_quality} | |||
p.table_skill_quality_stats = m_cargo.declare_factory{data=tables.skill_quality_stats} | |||
function p.process_skill_data(tpl_args) | |||
_process_skill_data(tpl_args) | |||
end | end | ||
p._skill = p.process_skill_data | |||
-- | |||
-- Template:Skill | |||
-- | |||
p.skill = m_util.misc.invoker_factory(_skill, { | |||
wrappers = cfg.wrappers.skill, | |||
}) | |||
-- | |||
-- Template:Skill progression | |||
-- | |||
p.progression = m_util.misc.invoker_factory(_progression, { | |||
wrappers = cfg.wrappers.progression, | |||
}) | |||
-- | |||
-- Template:Skill quality | |||
-- | |||
p.quality = m_util.misc.invoker_factory(_quality, { | |||
wrappers = cfg.wrappers.quality, | |||
}) | |||
return p | return p |
Latest revision as of 15:25, 8 April 2025
Module for handling skills with Cargo support.
Implemented templates
- {{Skill}}
- {{Skill progression}}
The above documentation is transcluded from Module:Skill/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Skill
--
-- This module implements Template:Skill and Template:Skill progression
-------------------------------------------------------------------------------
require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Skill')
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:Skill/config/sandbox') or mw.loadData('Module:Skill/config')
local mwlanguage = mw.language.getContentLanguage()
local i18n = cfg.i18n
local tables = {}
local data = {}
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
function h.map_to_arg(tpl_args, properties, prefix_in, map, level, set_name, set_id)
if map.fields then
for key, row in pairs(map.fields) do
if row.name then
local val = tpl_args[prefix_in .. row.name]
if row.func ~= nil then
val = row.func(tpl_args, val)
end
if val == nil and row.default ~= nil then
val = row.default
end
if val ~= nil then
if level ~= nil then
if set_name then
tpl_args.skill_levels[level][set_name] = tpl_args.skill_levels[level][set_name] or {}
tpl_args.skill_levels[level][set_name][set_id] = tpl_args.skill_levels[level][set_name][set_id] or {}
tpl_args.skill_levels[level][set_name][set_id][key] = val
else
tpl_args.skill_levels[level][key] = val
end
-- Nuke variables since they're remapped to skill_levels
tpl_args[prefix_in .. row.name] = nil
else
if set_name then
tpl_args[set_name] = tpl_args[set_name] or {}
tpl_args[set_name][set_id] = tpl_args[set_name][set_id] or {}
tpl_args[set_name][set_id][key] = val
-- Nuke variables since they're remapped to [set_name]
tpl_args[prefix_in .. row.name] = nil
else
tpl_args[key] = val
end
end
properties[row.field] = val
-- Deprecated parameters
if val and row.deprecated then
tpl_args._flags.has_deprecated_skill_parameters = true
if tpl_args.test then -- Log when testing
tpl_args.deprecated_parameters = tpl_args.deprecated_parameters or {}
tpl_args.deprecated_parameters[#tpl_args.deprecated_parameters+1] = {row.name, val}
end
end
end
end
end
end
end
function h.expand_costs_data(tpl_args, skill_levels)
--[[
Expand costs data so that each cost type has its own column with amounts
Assumptions:
Cost types are always static
Cost amounts can either be static or leveled, but not both
--]]
if skill_levels[0] then
local cost_types = m_util.cast.table(skill_levels[0].cost_types)
if #cost_types > 0 then
for _, level_data in pairs(skill_levels) do
if type(level_data) == 'table' and level_data.cost_amounts then
local cost_amounts = m_util.cast.table(level_data.cost_amounts, {callback = m_util.cast.number})
for i=1, #cost_types do
local type = cost_types[i]
local amount = cost_amounts[i]
if amount then
level_data['cost_' .. type] = amount
end
end
end
end
end
end
end
function h.stats(tpl_args, prefix_in, level)
for i=1, math.huge do -- repeat until no more stats are found
local stat_prefix = string.format('%s%s%d_', prefix_in, i18n.parameters.skill.stat, i) -- level<level>_stat<i>_
local stat = {
id = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.id.name], --level<level>_stat<i>_id
value = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.value.name], --level<level>_stat<i>_value
}
if stat.id == nil or stat.value == nil then
break
end
local properties = {
_table = tables.skill_stats_per_level.table,
[tables.skill_stats_per_level.fields.level.field] = level,
}
h.map_to_arg(tpl_args, properties, stat_prefix, tables.skill_stats_per_level, level, 'stats', i)
tpl_args.skill_levels.has_stats = true
if not tpl_args.test then
m_cargo.store(properties)
end
end
end
function h.int_value_or_na(tpl_args, tblrow, value, tmap)
value = tonumber(value)
if value == nil then
tblrow:node(m_util.html.table_cell('na'))
else
-- value = mwlanguage:formatNum(value) -- Removed for now. lang:formatNum() returns a string, which causes issues for formatting
if tmap.fmt ~= nil then
if type(tmap.fmt) == 'string' then
value = string.format(tmap.fmt, value)
elseif type(tmap.fmt) == 'function' then
value = string.format(tmap.fmt(tpl_args) or '%s', value)
end
end
tblrow
:tag('td')
:wikitext(value)
:done()
end
end
h.cast = {}
function h.cast.wrap(func)
return function(tpl_args, value)
if value == nil then
return nil
end
return func(value)
end
end
h.display = {}
h.display.factory = {}
function h.display.factory.value(args)
return function (tpl_args)
args.fmt = args.fmt or tables.static.fields[args.key].fmt
local value = tpl_args[args.key]
if args.fmt and value then
return string.format(args.fmt, value)
else
return value
end
end
end
function h.display.factory.range_value(args)
return function (tpl_args)
local value = {}
if args.set_name and args.set_id then
-- Guard against index errors
tpl_args.skill_levels[0][args.set_name] = tpl_args.skill_levels[0][args.set_name] or {}
tpl_args.skill_levels[0][args.set_name][args.set_id] = tpl_args.skill_levels[0][args.set_name][args.set_id] or {}
tpl_args.skill_levels[1][args.set_name] = tpl_args.skill_levels[1][args.set_name] or {}
tpl_args.skill_levels[1][args.set_name][args.set_id] = tpl_args.skill_levels[1][args.set_name][args.set_id] or {}
tpl_args.skill_levels[tpl_args.max_level][args.set_name] = tpl_args.skill_levels[tpl_args.max_level][args.set_name] or {}
tpl_args.skill_levels[tpl_args.max_level][args.set_name][args.set_id] = tpl_args.skill_levels[tpl_args.max_level][args.set_name][args.set_id] or {}
value.min = tpl_args.skill_levels[0][args.set_name][args.set_id][args.key] or tpl_args.skill_levels[1][args.set_name][args.set_id][args.key]
value.max = tpl_args.skill_levels[0][args.set_name][args.set_id][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.set_name][args.set_id][args.key]
else
value.min = tpl_args.skill_levels[0][args.key]
if value.min == nil or type(value.min) == 'table' and #value.min == 0 then
value.min = tpl_args.skill_levels[1][args.key]
end
value.max = tpl_args.skill_levels[0][args.key]
if value.max == nil or type(value.max) == 'table' and #value.max == 0 then
value.max = tpl_args.skill_levels[tpl_args.max_level][args.key]
end
if type(value.min) == 'table' and type(value.max) == 'table' and args.key_index then
value.min = value.min[args.key_index]
value.max = value.max[args.key_index]
end
end
if value.min == nil or value.max == nil then
-- property not set for this skill
return nil
end
local map = args.map or tables.progression
local options = {
fmt=args.fmt or map.fields[args.key] and map.fields[args.key].fmt,
color=false,
}
if type(value.min) == 'table' and type(value.max) == 'table' then
local formatted_values = {}
for i=1, #value.min do
formatted_values[i] = m_util.html.format_value(tpl_args, {min = value.min[i], max = value.max[i]}, options)
end
return formatted_values
end
return m_util.html.format_value(tpl_args, value, options)
end
end
function h.display.factory.radius(args)
return function (tpl_args)
local radius = tpl_args['radius' .. args.key]
if radius == nil then
return
end
local description = tpl_args[string.format('radius%s_description', args.key)]
if description then
return m_util.html.abbr(radius, description)
else
return radius
end
end
end
function h.query_skill(tpl_args)
local fields = {
'skill._pageID=_pageID',
}
local query = {
groupBy = 'skill._pageID',
}
local results = {}
local search_param
if tpl_args.skill_id then -- Query by skill id
query.where = string.format('skill_id="%s"', tpl_args.skill_id)
search_param = 'skill_id'
else -- Query by page name
local page = tpl_args.page or mw.title.getCurrentTitle().prefixedText
query.where = string.format('_pageName="%s"', page)
search_param = 'page'
end
results = m_cargo.query({tables.static.table}, fields, query)
if #results == 0 then
-- No results found
error(string.format(i18n.errors.validate_skill.no_results_found, search_param, tpl_args[search_param]))
elseif #results > 1 then
-- More than one result found
error(string.format(i18n.errors.validate_skill.many_results_found, search_param, tpl_args[search_param]))
end
return results[1]
end
-- ----------------------------------------------------------------------------
-- Cargo tables
-- ----------------------------------------------------------------------------
tables.static = {
table = 'skill',
fields = {
-- GrantedEffects.dat
skill_id = {
name = i18n.parameters.skill.skill_id,
field = 'skill_id',
type = 'String',
func = nil,
},
-- Active Skills.dat
cast_time = {
name = i18n.parameters.skill.cast_time,
field = 'cast_time',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
gem_description = {
name = i18n.parameters.skill.gem_description,
field = 'description',
type = 'Text',
func = nil,
},
active_skill_name = {
name = i18n.parameters.skill.active_skill_name,
field = 'active_skill_name',
type = 'String',
func = nil,
},
skill_icon = {
name = i18n.parameters.skill.skill_icon,
field = 'skill_icon',
type = 'Page',
func = function(tpl_args, value)
if value then
value = string.format(i18n.files.skill_icon, value)
elseif tpl_args.active_skill_name then
value = string.format(i18n.files.skill_icon, tpl_args.active_skill_name)
else
value = nil
end
return value
end,
},
item_class_id_restriction = {
name = i18n.parameters.skill.item_class_id_restriction,
field = 'item_class_id_restriction',
type = 'List (,) of String',
func = function(tpl_args, value)
if value == nil then
return nil
end
value = m_util.string.split(value, ', ')
for _, v in ipairs(value) do
if m_game.constants.item.classes[v] == nil then
error(string.format(i18n.errors.skill.invalid_item_class_id, v))
end
end
return value
end,
},
item_class_restriction = {
name = i18n.parameters.skill.item_class_restriction,
field = 'item_class_restriction',
type = 'List (,) of String',
func = function(tpl_args, value)
if tpl_args.item_class_id_restriction == nil then
return
end
-- This function makes a localized list based on ids
local item_classes = {}
for _, v in ipairs(tpl_args.item_class_id_restriction) do
item_classes[#item_classes+1] = m_game.constants.item.classes[v].full
end
return item_classes
end,
},
-- Projectiles.dat - manually mapped to the skills
projectile_speed = {
name = i18n.parameters.skill.projectile_speed,
field = 'projectile_speed',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
-- Misc data derieved from stats
stat_text = {
name = i18n.parameters.skill.stat_text,
field = 'stat_text',
type = 'Text',
func = nil,
},
-- Misc data currently not from game data
radius = {
name = i18n.parameters.skill.radius,
field = 'radius',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_description = {
name = i18n.parameters.skill.radius_description,
field = 'radius_description',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
radius_secondary = {
name = i18n.parameters.skill.radius_secondary,
field = 'radius_secondary',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_secondary_description = {
name = i18n.parameters.skill.radius_secondary_description,
field = 'radius_secondary_description',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
radius_tertiary = { -- not sure if any skill actually has 3 radius componets
name = i18n.parameters.skill.radius_tertiary,
field = 'radius_tertiary',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_tertiary_description = {
name = i18n.parameters.skill.radius_tertiary_description,
field = 'radius_tertiary_description',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
skill_screenshot = {
name = i18n.parameters.skill.skill_screenshot,
field = 'skill_screenshot',
type = 'Page',
func = function(tpl_args, value)
if tpl_args.skill_screenshot_file then
tpl_args._flags.has_deprecated_skill_parameters = true
value = string.format('File:%s', tpl_args.skill_screenshot_file)
elseif value then
value = string.format(i18n.files.skill_screenshot, value)
elseif tpl_args.active_skill_name then
-- When this parameter is set manually, we assume/expect it to exist, but otherwise it probably doesn't and we don't need dead links in that case
value = string.format(i18n.files.skill_screenshot, tpl_args.active_skill_name)
local page = mw.title.new(value)
if page == nil or not page.exists then
value = nil
end
else
value = nil
end
return value
end,
},
-- Set programmatically
max_level = {
name = nil,
field = 'max_level',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
html = {
name = nil,
field = 'html',
type = 'Text',
func = nil,
},
},
}
tables.progression = {
table = 'skill_levels',
fields = {
level = {
name = nil,
field = 'level',
type = 'Integer',
func = nil,
},
level_requirement = {
name = i18n.parameters.skill.level_requirement,
field = 'level_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
dexterity_requirement = {
name = i18n.parameters.skill.dexterity_requirement,
field = 'dexterity_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
strength_requirement = {
name = i18n.parameters.skill.strength_requirement,
field = 'strength_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
intelligence_requirement = {
name = i18n.parameters.skill.intelligence_requirement,
field = 'intelligence_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
cost_multiplier = {
name = i18n.parameters.skill.cost_multiplier,
field = 'cost_multiplier',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
attack_time = {
name = i18n.parameters.skill.attack_time,
field = 'attack_time',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
critical_strike_chance = {
name = i18n.parameters.skill.critical_strike_chance,
field = 'critical_strike_chance',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
damage_effectiveness = {
name = i18n.parameters.skill.damage_effectiveness,
field = 'damage_effectiveness',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
stored_uses = {
name = i18n.parameters.skill.stored_uses,
field = 'stored_uses',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
cooldown = {
name = i18n.parameters.skill.cooldown,
field = 'cooldown',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
vaal_souls_requirement = {
name = i18n.parameters.skill.vaal_souls_requirement,
field = 'vaal_souls_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
vaal_stored_uses = {
name = i18n.parameters.skill.vaal_stored_uses,
field = 'vaal_stored_uses',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.vaal_stored_uses,
},
vaal_soul_gain_prevention_time = {
name = i18n.parameters.skill.vaal_soul_gain_prevention_time,
field = 'vaal_soul_gain_prevention_time',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%i ' .. m_game.units.seconds.short_lower,
},
damage_multiplier = {
name = i18n.parameters.skill.damage_multiplier,
field = 'damage_multiplier',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
attack_speed_multiplier = {
name = i18n.parameters.skill.attack_speed_multiplier,
field = 'attack_speed_multiplier',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
duration = {
name = i18n.parameters.skill.duration,
field = 'duration',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
cost_types = {
name = i18n.parameters.skill.cost_types,
field = 'cost_types',
type = 'List (,) of String',
func = function(tpl_args, value)
if value == nil then
return nil
end
value = m_util.cast.table(value)
for _, v in ipairs(value) do
if m_game.constants.skill.cost_types[v] == nil then
error(string.format(i18n.errors.skill.invalid_cost_type, v))
end
end
return value
end,
},
cost_amounts = {
name = i18n.parameters.skill.cost_amounts,
field = 'cost_amounts',
type = 'List (,) of Integer',
func = function(tpl_args, value)
if value == nil then
return nil
end
value = m_util.cast.table(value, {callback = m_util.cast.number})
return value
end,
},
mana_reservation_flat = {
name = i18n.parameters.skill.mana_reservation_flat,
field = 'mana_reservation_flat',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
mana_reservation_percent = {
name = i18n.parameters.skill.mana_reservation_percent,
field = 'mana_reservation_percent',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
life_reservation_flat = {
name = i18n.parameters.skill.life_reservation_flat,
field = 'life_reservation_flat',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
life_reservation_percent = {
name = i18n.parameters.skill.life_reservation_percent,
field = 'life_reservation_percent',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
spirit_reservation_flat = {
name = i18n.parameters.skill.spirit_reservation_flat,
field = 'spirit_reservation_flat',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
-- from gem experience, optional
experience = {
name = i18n.parameters.skill.experience,
field = 'experience',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
stat_text = {
name = i18n.parameters.skill.stat_text,
field = 'stat_text',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
}
}
tables.skill_stats_per_level = {
table = 'skill_stats_per_level',
fields = {
level = {
name = nil,
field = 'level',
type = 'Integer',
func = nil,
},
id = {
name = i18n.parameters.skill.stat_id,
field = 'id',
type = 'String',
func = h.cast.wrap(m_util.cast.text),
},
value = {
name = i18n.parameters.skill.stat_value,
field = 'value',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
},
}
tables.skill_quality = {
table = 'skill_quality',
fields = {
set_id = {
field = 'set_id',
type = 'Integer',
},
weight = {
field = 'weight',
type = 'Integer',
},
stat_text = {
field = 'stat_text',
type = 'String',
},
},
}
tables.skill_quality_stats = {
table = 'skill_quality_stats',
fields = {
set_id = {
field = 'set_id',
type = 'Integer',
},
id = {
field = 'id',
type = 'String',
},
value = {
field = 'value',
type = 'Integer',
},
},
}
-- ----------------------------------------------------------------------------
-- Data
-- ----------------------------------------------------------------------------
data.skill_progression_table = {
{
field = 'level',
header = i18n.progression.level,
},
{
field = 'level_requirement',
header = i18n.progression.level_requirement,
},
{
field = 'dexterity_requirement',
header = i18n.progression.dexterity_requirement,
},
{
field = 'strength_requirement',
header = i18n.progression.strength_requirement,
},
{
field = 'intelligence_requirement',
header = i18n.progression.intelligence_requirement,
},
{
field = 'cost_multiplier',
header = i18n.progression.cost_multiplier,
fmt = '%s%%',
},
{
field = 'attack_time',
header = i18n.progression.attack_time,
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
{
field = 'critical_strike_chance',
header = i18n.progression.critical_strike_chance,
fmt = '%s%%',
},
{
field = 'cost_Mana',
header = i18n.progression.mana_cost,
},
{
field = 'cost_Life',
header = i18n.progression.life_cost,
},
{
field = 'cost_ES',
header = i18n.progression.energy_shield_cost,
},
{
field = 'cost_Rage',
header = i18n.progression.rage_cost,
},
{
field = 'cost_ManaPercent',
header = i18n.progression.mana_cost,
fmt = '%s%%',
},
{
field = 'cost_LifePercent',
header = i18n.progression.life_cost,
fmt = '%s%%',
},
{
field = 'mana_reservation_flat',
header = i18n.progression.mana_reserved,
},
{
field = 'mana_reservation_percent',
header = i18n.progression.mana_reserved,
fmt = '%s%%',
},
{
field = 'life_reservation_flat',
header = i18n.progression.life_reserved,
},
{
field = 'life_reservation_percent',
header = i18n.progression.life_reserved,
fmt = '%s%%',
},
{
field = 'spirit_reservation_flat',
header = i18n.progression.spirit_reserved,
},
{
field = 'damage_effectiveness',
header = i18n.progression.damage_effectiveness,
fmt = '%s%%',
},
{
field = 'stored_uses',
header = i18n.progression.stored_uses,
},
{
field = 'cooldown',
header = i18n.progression.cooldown,
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
{
field = 'vaal_souls_requirement',
header = i18n.progression.vaal_souls_requirement,
},
{
field = 'vaal_stored_uses',
header = i18n.progression.vaal_stored_uses,
},
{
field = 'vaal_soul_gain_prevention_time',
header = i18n.progression.vaal_soul_gain_prevention_time,
fmt = '%i ' .. m_game.units.seconds.short_lower,
},
{
field = 'damage_multiplier',
header = i18n.progression.damage_multiplier,
fmt = '%s%%',
},
{
field = 'duration',
header = i18n.progression.duration,
fmt = '%.2f ' .. m_game.units.seconds.short_lower,
},
{
field = 'attack_speed_multiplier',
header = i18n.progression.attack_speed_multiplier,
fmt = '%s%%',
},
}
data.infobox_table = {
{
header = i18n.infobox.active_skill_name,
func = h.display.factory.value{key='active_skill_name'},
},
{
header = i18n.infobox.skill_id,
func = function (tpl_args)
return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.skill_id)
end
},
{
header = i18n.infobox.skill_icon,
func = function (tpl_args)
if tpl_args.skill_icon then
return string.format('[[%s]]', tpl_args.skill_icon)
end
end,
},
{
header = i18n.infobox.cast_time,
func = function (tpl_args)
local value = tpl_args.cast_time
if value then
if value == 0 then
return i18n.infobox.instant_cast_time
end
return string.format('%.2f %s', value, m_game.units.seconds.short_lower)
end
return value
end,
},
{
header = i18n.infobox.item_class_restrictions,
func = function (tpl_args)
if tpl_args.item_class_restriction == nil then
return
end
local out = {}
for _, class in ipairs(tpl_args.item_class_restriction) do
out[#out+1] = string.format('[[%s]]', class)
end
return table.concat(out, '<br>')
end,
},
{
header = i18n.infobox.projectile_speed,
func = h.display.factory.value{key='projectile_speed'},
},
{
header = i18n.infobox.radius,
func = h.display.factory.radius{key=''},
},
{
header = i18n.infobox.radius_secondary,
func = h.display.factory.radius{key='_secondary'},
},
{
header = i18n.infobox.radius_tertiary,
func = h.display.factory.radius{key='_tertiary'},
},
{
header = i18n.infobox.level_requirement,
func = h.display.factory.range_value{key='level_requirement'},
},
-- ignore attrbiutes?
{
header = i18n.infobox.cost_multiplier,
func = h.display.factory.range_value{key='cost_multiplier'},
},
{
header = i18n.infobox.attack_time,
func = h.display.factory.range_value{key='attack_time'},
},
{
header = i18n.infobox.critical_strike_chance,
func = h.display.factory.range_value{key='critical_strike_chance'},
},
{
header = i18n.infobox.cost,
func = function (tpl_args)
local parts = {}
for k, v in pairs(m_game.constants.skill.cost_types) do
local key = 'cost_' .. k
local fmt
if string.find(k, 'Percent', 1, true) then
fmt = '%s%% %s'
else
fmt = '%s %s'
end
local range = h.display.factory.range_value{key=key}(tpl_args)
if range then
parts[#parts+1] = string.format(fmt, range, v.long_lower)
end
end
return table.concat(parts, ', ')
end,
},
{
header = i18n.infobox.reservation,
func = function (tpl_args)
local parts = {}
local keys = {
{
key = 'mana_reservation_flat',
fmt = '%s ' .. m_game.constants.skill.cost_types['Mana'].long_lower,
},
{
key = 'mana_reservation_percent',
fmt = '%s%% ' .. m_game.constants.skill.cost_types['Mana'].long_lower,
},
{
key = 'life_reservation_flat',
fmt = '%s ' .. m_game.constants.skill.cost_types['Life'].long_lower,
},
{
key = 'life_reservation_percent',
fmt = '%s%% ' .. m_game.constants.skill.cost_types['Life'].long_lower,
},
{
key = 'spirit_reservation_flat',
fmt = '%s ' .. 'spirit',
},
}
for _, v in ipairs(keys) do
local range = h.display.factory.range_value{key=v.key}(tpl_args)
if range then
parts[#parts+1] = string.format(v.fmt, range)
end
end
return table.concat(parts, ', ')
end,
},
{
header = i18n.infobox.attack_speed_multiplier,
func = h.display.factory.range_value{key='attack_speed_multiplier'},
fmt = '%s ' .. i18n.infobox.of_base_stat,
},
{
header = i18n.infobox.damage_multiplier,
func = h.display.factory.range_value{key='damage_multiplier'},
fmt = '%s ' .. i18n.infobox.of_base_stat,
},
{
header = i18n.infobox.damage_effectiveness,
func = h.display.factory.range_value{key='damage_effectiveness'},
},
{
header = i18n.infobox.stored_uses,
func = h.display.factory.range_value{key='stored_uses'},
},
{
header = i18n.infobox.cooldown,
func = h.display.factory.range_value{key='cooldown'},
},
{
header = i18n.infobox.vaal_souls_requirement,
func = h.display.factory.range_value{key='vaal_souls_requirement'},
},
{
header = i18n.infobox.vaal_stored_uses,
func = h.display.factory.range_value{key='vaal_stored_uses'},
},
{
header = i18n.infobox.vaal_soul_gain_prevention_time,
func = h.display.factory.range_value{key='vaal_soul_gain_prevention_time'},
},
{
header = i18n.infobox.duration,
func = h.display.factory.range_value{key='duration'},
},
{
header = nil,
func = h.display.factory.value{key='gem_description'},
class = 'tc -gemdesc',
},
{
header = nil,
func = h.display.factory.value{key='stat_text'},
class = 'tc -mod',
},
}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _process_skill_data(tpl_args)
--[[
Processes skill data from tpl_args.
Stores skill data in cargo tables.
Attaches page to cargo tables.
--]]
tpl_args = tpl_args or {}
tpl_args._flags = tpl_args._flags or {}
tpl_args.skill_levels = {
[0] = {},
}
-- Quality
tpl_args.skill_quality = {}
local i = 0
repeat
i = i + 1
local prefix = string.format('quality_type%s', i)
local q = {
_table = tables.skill_quality.table,
set_id = i,
weight = tonumber(tpl_args[string.format('%s_weight', prefix)]),
stat_text = tpl_args[string.format('%s_stat_text', prefix)],
}
if q.stat_text then
tpl_args.skill_quality[#tpl_args.skill_quality+1] = q
m_cargo.store(q)
q.stats = {}
q._table = nil
local j = 0
repeat
j = j + 1
local stat_prefix = string.format('%s_stat%s', prefix, j)
local s = {
_table = tables.skill_quality_stats.table,
set_id = i,
id = tpl_args[string.format('%s_id', stat_prefix)],
value = tonumber(tpl_args[string.format('%s_value', stat_prefix)]),
}
if s.id and s.value then
q.stats[#q.stats+1] = s
m_cargo.store(s)
end
s._table = nil
until s.id == nil or s.value == nil
end
until q.stat_text == nil
if #tpl_args.skill_quality > 1 then
-- Gem has alternative qualtiy
tpl_args._flags.is_alt_quality_gem = true
end
-- Handle level progression
local level_count = 0
for i=1, math.huge do -- repeat until no more levels are found
local prefix = i18n.parameters.skill.level .. i
local level = m_util.cast.boolean(tpl_args[prefix])
if not level then
break
end
tpl_args.skill_levels[i] = {}
prefix = prefix .. '_'
level_count = i
if tpl_args[prefix .. i18n.parameters.skill.experience] ~= nil then
-- For skill gems, max level is the highest level with experience.
tpl_args.max_level = i
end
local properties = {
_table = tables.progression.table,
[tables.progression.fields.level.field] = i
}
h.map_to_arg(tpl_args, properties, prefix, tables.progression, i)
if not tpl_args.test then
m_cargo.store(properties)
end
h.stats(tpl_args, prefix, i)
end
tpl_args.max_level = tpl_args.max_level or level_count
-- handle static progression
local prefix = i18n.parameters.skill.static .. '_'
do
local properties = {
_table = tables.progression.table,
[tables.progression.fields.level.field] = 0
}
h.map_to_arg(tpl_args, properties, prefix, tables.progression, 0)
if not tpl_args.test then
m_cargo.store(properties)
end
end
-- Expand costs data
h.expand_costs_data(tpl_args, tpl_args.skill_levels)
-- Handle static arguments
local properties = {
_table = tables.static.table,
[tables.static.fields.max_level.field] = tpl_args.max_level
}
h.map_to_arg(tpl_args, properties, '', tables.static)
h.stats(tpl_args, prefix, 0)
-- Build infobox
local infobox = mw.html.create('span')
infobox:addClass('skill-box')
local tbl = infobox:tag('table')
tbl:addClass('wikitable skill-box-table')
for _, infobox_data in ipairs(data.infobox_table) do
local display = infobox_data.func(tpl_args)
if type(display) == 'string' and string.len(display) > 0 then
if infobox_data.fmt ~= nil then
if type(infobox_data.fmt) == 'string' then
display = string.format(infobox_data.fmt, display)
elseif type(infobox_data.fmt) == 'function' then
display = string.format(infobox_data.fmt(tpl_args) or '%s', display)
end
end
local tr = tbl:tag('tr')
if infobox_data.header then
local header_text
if type(infobox_data.header) == 'function' then
header_text = infobox_data.header(tpl_args)
else
header_text = infobox_data.header
end
tr
:tag('th')
:wikitext(header_text)
:done()
end
local td = tr:tag('td')
td:wikitext(display)
td:addClass(infobox_data.class or 'tc -value')
if infobox_data.header == nil then
td:attr('colspan', 2)
end
end
end
infobox = tostring(infobox)
-- Store data
properties[tables.static.fields.html.field] = infobox
if not tpl_args.test then
m_cargo.store(properties)
end
-- Attach tables
if not tpl_args.test then
local attach_tables = {
tables.static.table,
tables.progression.table,
}
if #tpl_args.skill_quality > 0 then
attach_tables[#attach_tables+1] = tables.skill_quality.table
attach_tables[#attach_tables+1] = tables.skill_quality_stats.table
end
if tpl_args.skill_levels.has_stats then
attach_tables[#attach_tables+1] = tables.skill_stats_per_level.table
end
for _, table_name in ipairs(attach_tables) do
mw.getCurrentFrame():expandTemplate{
title = string.format(i18n.templates.cargo_attach, table_name),
args = {}
}
end
end
-- Log when testing
if tpl_args.test then
mw.logObject(tpl_args)
end
return infobox
end
local function _skill(tpl_args)
--[[
Display skill infobox
Examples
--------
=p.skill{gem_description='Icy bolts rain down over the targeted area.', active_skill_name='Icestorm', skill_id='IcestormUniqueStaff12', cast_time=0.75, required_level=1, static_mana_cost=22, static_critical_strike_chance=6, static_damage_effectiveness=30, static_damage_multiplier=100, static_stat1_id='spell_minimum_base_cold_damage_+_per_10_intelligence', static_stat1_value=1, static_stat2_id='spell_maximum_base_cold_damage_+_per_10_intelligence', static_stat2_value=3, static_stat3_id='base_skill_effect_duration', static_stat3_value=1500, static_stat4_id='fire_storm_fireball_delay_ms', static_stat4_value=100, static_stat5_id='skill_effect_duration_per_100_int', static_stat5_value=150, static_stat6_id='skill_override_pvp_scaling_time_ms', static_stat6_value=450, static_stat7_id='firestorm_drop_ground_ice_duration_ms', static_stat7_value=500, static_stat8_id='skill_art_variation', static_stat8_value=4, static_stat9_id='base_skill_show_average_damage_instead_of_dps', static_stat9_value=1, static_stat10_id='is_area_damage', static_stat10_value=1, stat_text='Deals 1 to 3 base Cold Damage per 10 Intelligence<br>Base duration is 1.5 seconds<br>One impact every 0.1 seconds<br>0.15 seconds additional Base Duration per 100 Intelligence', quality_stat_text = nil, level1=true, level1_level_requirement=1}
]]
-- Handle skill data and get infobox
local infobox = _process_skill_data(tpl_args)
-- Container
local container = mw.html.create('span')
container
:addClass('skill-box-page-container')
:wikitext(infobox)
if tpl_args.skill_screenshot then
container
:wikitext(string.format('[[%s]]', tpl_args.skill_screenshot))
end
-- Generic messages on the page:
local out = {}
if mw.ustring.find(tpl_args.skill_id, '_') then
out[#out+1] = mw.getCurrentFrame():expandTemplate{
title = i18n.templates.incorrect_title,
args = {title=tpl_args.skill_id}
} .. '\n\n\n'
end
if tpl_args.active_skill_name then
out[#out+1] = string.format(
i18n.messages.intro_named_id,
tpl_args.skill_id,
tpl_args.active_skill_name
)
else
out[#out+1] = string.format(
i18n.messages.intro_unnamed_id,
tpl_args.skill_id
)
end
-- Categories
local cats = {i18n.categories.skill_data}
if tpl_args._flags.has_deprecated_skill_parameters then
cats[#cats+1] = i18n.categories.deprecated_parameters
end
return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end
local function _progression(tpl_args)
--[[
Displays the level progression for the skill gem.
Examples
--------
= p.progression{page='Reave'}
]]
-- Parse column arguments:
tpl_args.stat_format = {}
local param_keys = {
i18n.parameters.progression.header,
i18n.parameters.progression.abbr,
i18n.parameters.progression.pattern_extract,
i18n.parameters.progression.pattern_value,
}
for i=1, math.huge do -- repeat until no more columns are found
local prefix = string.format('%s%d_', i18n.parameters.progression.column, i)
if tpl_args[prefix .. param_keys[1]] == nil then
break
end
local statfmt = {counter = 0}
for _, key in ipairs(param_keys) do
local arg = prefix .. key
if tpl_args[arg] == nil then
error(string.format(i18n.errors.progression.argument_unspecified, arg))
end
statfmt[key] = tpl_args[arg]
end
statfmt.header = m_util.html.abbr(statfmt.abbr, statfmt.header)
statfmt.abbr = nil
tpl_args.stat_format[#tpl_args.stat_format+1] = statfmt
end
-- Query skill data
local skill_data = h.query_skill(tpl_args)
-- Query progression data
local fields = {}
for _, fmap in pairs(tables.progression.fields) do
fields[#fields+1] = fmap.field
end
local query = {
where = string.format(
'_pageID="%s"',
skill_data._pageID
),
groupBy = string.format(
'_pageID, %s',
tables.progression.fields.level.field
),
}
local results = m_cargo.query({tables.progression.table}, fields, query)
-- Re-index by level
skill_data.levels = {}
for _, v in ipairs(results) do
skill_data.levels[tonumber(v.level)] = v
end
if #skill_data.levels == 0 then
error(i18n.errors.progression.missing_level_data)
end
-- Expand costs data
h.expand_costs_data(tpl_args, skill_data.levels)
-- Set up html table headers
local headers = {}
for _, row in ipairs(skill_data.levels) do
for k, v in pairs(row) do
headers[k] = true
end
end
local tbl = mw.html.create('table')
tbl
:addClass('wikitable responsive-table skill-progression-table')
local head = tbl:tag('tr')
for _, tmap in pairs(data.skill_progression_table) do
if headers[tmap.field] then
local text = type(tmap.header) == 'function' and tmap.header(tpl_args) or tmap.header
head
:tag('th')
:wikitext(text)
:done()
end
end
for _, statfmt in ipairs(tpl_args.stat_format) do
head
:tag('th')
:wikitext(statfmt.header)
:done()
end
if headers[tables.progression.fields.experience.field] then
head
:tag('th')
:wikitext(i18n.progression.experience)
:done()
:tag('th')
:wikitext(i18n.progression.total_experience)
:done()
end
-- Table rows
local tblrow
local lastexp = 0
local experience
for _, row in ipairs(skill_data.levels) do
tblrow = tbl:tag('tr')
for _, tmap in pairs(data.skill_progression_table) do
if headers[tmap.field] then
h.int_value_or_na(tpl_args, tblrow, row[tmap.field], tmap)
end
end
-- stats
local stats = {}
if row[tables.progression.fields.stat_text.field] then
stats = m_util.string.split(
row[tables.progression.fields.stat_text.field],
'<br>'
)
end
for _, statfmt in ipairs(tpl_args.stat_format) do
local match = {}
for j, stat in ipairs(stats) do
match = {string.match(stat, statfmt.pattern_extract)}
if #match > 0 then
-- TODO maybe remove stat here to avoid testing
-- against in future loops
break
end
end
if #match == 0 then
tblrow:node(m_util.html.table_cell('na'))
else
-- used to find broken progression due to game updates
-- for example:
statfmt.counter = statfmt.counter + 1
tblrow
:tag('td')
:wikitext(string.format(
statfmt.pattern_value,
match[1],
match[2],
match[3],
match[4],
match[5]
)
)
:done()
end
end
if headers[tables.progression.fields.experience.field] then
experience = tonumber(row[tables.progression.fields.experience.field])
if experience ~= nil then
h.int_value_or_na(tpl_args, tblrow, experience - lastexp, {})
lastexp = experience
else
tblrow:node(m_util.html.table_cell('na'))
end
h.int_value_or_na(tpl_args, tblrow, experience, {})
end
end
local cats = {}
for _, statfmt in ipairs(tpl_args.stat_format) do
if statfmt.counter == 0 then
cats = i18n.categories.broken_progression_table
break
end
end
return tostring(tbl) .. m_util.misc.add_category(cats)
end
local function _quality(tpl_args)
--[[
Displays a table comparing the stats of superior gem quality with
alternative quality types.
--]]
-- Query skill data
local skill_data = h.query_skill(tpl_args)
-- Query progression data
local fields = {}
for _, fmap in pairs(tables.skill_quality.fields) do
fields[#fields+1] = fmap.field
end
local query = {
where = string.format(
'_pageID="%s"',
skill_data._pageID
),
groupBy = string.format(
'_pageID, %s',
tables.skill_quality.fields.set_id.field
),
orderBy = string.format(
'%s ASC',
tables.skill_quality.fields.set_id.field
),
}
skill_data.quality = m_cargo.query({tables.skill_quality.table}, fields, query)
if #skill_data.quality == 0 then
error(i18n.errors.quality.missing_quality_data)
end
-- Build table
local tbl = mw.html.create('table')
tbl
:addClass('wikitable skill-quality-table')
:tag('tr')
:tag('th')
:wikitext(i18n.quality.type)
:done()
:tag('th')
:wikitext(i18n.quality.stats)
:done()
:tag('th')
:wikitext(i18n.quality.weight)
:done()
for k, row in ipairs(skill_data.quality) do
tbl
:tag('tr')
:tag('td')
:wikitext(m_game.constants.item.gem_quality_types[k].long_upper)
:done()
:tag('td')
:addClass('tc -mod')
:wikitext(skill_data.quality[k].stat_text)
:done()
:tag('td')
:wikitext(skill_data.quality[k].weight)
:done()
end
return tostring(tbl)
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
p.table_skills = m_cargo.declare_factory{data=tables.static}
p.table_skill_levels = m_cargo.declare_factory{data=tables.progression}
p.table_skill_stats_per_level = m_cargo.declare_factory{data=tables.skill_stats_per_level}
p.table_skill_quality = m_cargo.declare_factory{data=tables.skill_quality}
p.table_skill_quality_stats = m_cargo.declare_factory{data=tables.skill_quality_stats}
function p.process_skill_data(tpl_args)
_process_skill_data(tpl_args)
end
p._skill = p.process_skill_data
--
-- Template:Skill
--
p.skill = m_util.misc.invoker_factory(_skill, {
wrappers = cfg.wrappers.skill,
})
--
-- Template:Skill progression
--
p.progression = m_util.misc.invoker_factory(_progression, {
wrappers = cfg.wrappers.progression,
})
--
-- Template:Skill quality
--
p.quality = m_util.misc.invoker_factory(_quality, {
wrappers = cfg.wrappers.quality,
})
return p