[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
(Undo revision 698307 by Vinifera7 (talk) Issues detected on Awakened Minion Damage Support) |
(Fixed issue on Awakened Minion Damage Support) |
||
Line 13: | Line 13: | ||
--- define here to avoid errors | --- define here to avoid errors | ||
local tables = {} | |||
local data = {} | local data = {} | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
Line 25: | Line 21: | ||
local i18n = { | local i18n = { | ||
skill_icon = ' | arguments = { | ||
skill = { | |||
-- static | |||
static = 'static', | |||
skill_id = 'skill_id', | |||
cast_time = 'cast_time', | |||
gem_description = 'gem_description', | |||
active_skill_name = 'active_skill_name', | |||
skill_icon = 'skill_icon', | |||
item_class_id_restriction = 'item_class_id_restriction', | |||
item_class_restriction = 'item_class_restriction', | |||
projectile_speed = 'projectile_speed', | |||
stat_text = 'stat_text', | |||
quality_stat_text = 'quality_stat_text', | |||
radius = 'radius', | |||
radius_description = 'radius_description', | |||
radius_secondary = 'radius_secondary', | |||
radius_secondary_description = 'radius_secondary_description', | |||
radius_tertiary = 'radius_tertiary', | |||
radius_tertiary_description = 'radius_tertiary_description', | |||
skill_screenshot = 'skill_screenshot', | |||
has_percentage_mana_cost = 'has_percentage_mana_cost', | |||
has_reservation_mana_cost = 'has_reservation_mana_cost', | |||
-- progression | |||
level = 'level', | |||
level_requirement = 'level_requirement', | |||
dexterity_requirement = 'dexterity_requirement', | |||
strength_requirement = 'strength_requirement', | |||
intelligence_requirement = 'intelligence_requirement', | |||
mana_multiplier = 'mana_multiplier', | |||
critical_strike_chance = 'critical_strike_chance', | |||
damage_effectiveness = 'damage_effectiveness', | |||
stored_uses = 'stored_uses', | |||
cooldown = 'cooldown', | |||
vaal_souls_requirement = 'vaal_souls_requirement', | |||
vaal_stored_uses = 'vaal_stored_uses', | |||
vaal_soul_gain_prevention_time = 'vaal_soul_gain_prevention_time', | |||
damage_multiplier = 'damage_multiplier', | |||
attack_speed_multiplier = 'attack_speed_multiplier', | |||
duration = 'duration', | |||
experience = 'experience', | |||
mana_cost = 'mana_cost', | |||
-- costs | |||
skill_cost = 'skill_cost', | |||
cost = 'cost', | |||
cost_type = 'type', | |||
cost_is_reservation = 'is_reservation', | |||
cost_amount = 'amount', | |||
-- stats | |||
stat = 'stat', | |||
stat_id = 'id', | |||
stat_value = 'value', | |||
}, | |||
progression = { | |||
column = 'c', | |||
header = 'header', | |||
abbr = 'abbr', | |||
pattern_extract = 'pattern_extract', | |||
pattern_value = 'pattern_value', | |||
}, | |||
}, | |||
errors = { | errors = { | ||
skill = { | |||
no_results_for_skill_id = | invalid_item_class_id = 'The item class id "%s" is invalid.', | ||
invalid_cost_type = 'The cost type "%s" is invalid. Acceptable values are "mana", "life", "energy_shield", "rage", "mana_percent" and "life_percent".', | |||
}, | |||
progression = { | |||
argument_unspecified = 'The argument "%s" is unspecified.', | |||
no_results_for_skill_id = 'Unable to find skill data for skill id "%s".', | |||
no_results_for_skill_page = 'Unable to find skill data on page "%s".', | |||
missing_level_data = 'Unable to find skill level progression data.', | |||
}, | |||
}, | |||
templates = { | |||
incorrect_title = 'Template:Incorrect title', | |||
cargo_attach = 'Template:Skill/cargo/attach/%s', | |||
}, | }, | ||
categories = { | categories = { | ||
broken_progression_table = 'Pages with broken skill progression tables' | skill_data = 'Skill data', | ||
deprecated_arguments = 'Pages with deprecated arguments for skill data', | |||
broken_progression_table = 'Pages with broken skill progression tables', | |||
}, | |||
files = { | |||
skill_icon = 'File:%s skill icon.png', | |||
skill_screenshot = 'File:%s skill screenshot.jpg', | |||
}, | |||
messages = { | |||
intro_named_id = "'''%s''' is the internal id of the [[skill]] '''%s'''.\n", | |||
intro_unnamed_id = "'''%s''' is the internal id of an unnamed [[skill]].\n", | |||
}, | }, | ||
Line 56: | Line 130: | ||
mana_multiplier = 'Mana Multiplier', | mana_multiplier = 'Mana Multiplier', | ||
critical_strike_chance = 'Critical Strike Chance', | critical_strike_chance = 'Critical Strike Chance', | ||
cost = 'Cost', | |||
reservation = 'Reservation', | |||
attack_speed_multiplier = 'Attack Speed', | attack_speed_multiplier = 'Attack Speed', | ||
damage_effectiveness = 'Effectiveness of Added Damage', | damage_effectiveness = 'Effectiveness of Added Damage', | ||
Line 70: | Line 144: | ||
progression = { | progression = { | ||
level = 'Level', | |||
level_requirement = m_util.html.abbr('[[Image:Level_up_icon_small.png|link=|Lvl.]]', 'Required Level', 'nounderline'), | level_requirement = m_util.html.abbr('[[Image:Level_up_icon_small.png|link=|Lvl.]]', 'Required Level', 'nounderline'), | ||
dexterity_requirement = m_util.html.abbr('[[Image:DexterityIcon_small.png|link=|dexterity]]', 'Required Dexterity', 'nounderline'), | dexterity_requirement = m_util.html.abbr('[[Image:DexterityIcon_small.png|link=|dexterity]]', 'Required Dexterity', 'nounderline'), | ||
Line 77: | Line 152: | ||
critical_strike_chance = 'Critical<br>Strike<br>Chance', | critical_strike_chance = 'Critical<br>Strike<br>Chance', | ||
mana_cost = 'Mana<br>Cost', | mana_cost = 'Mana<br>Cost', | ||
life_cost = 'Life<br>Cost', | |||
energy_shield_cost = m_util.html.abbr('ES Cost', 'Energy shield cost'), | |||
rage_cost = 'Rage<br>Cost', | |||
mana_reserved = 'Mana<br>Reserved', | mana_reserved = 'Mana<br>Reserved', | ||
life_reserved = 'Life<br>Reserved', | |||
attack_speed_multiplier = 'Attack<br>Speed<br>Multiplier', | attack_speed_multiplier = 'Attack<br>Speed<br>Multiplier', | ||
damage_effectiveness = 'Damage<br>Effectiveness', | damage_effectiveness = 'Damage<br>Effectiveness', | ||
Line 85: | Line 164: | ||
vaal_stored_uses = 'Stored<br>Uses', | vaal_stored_uses = 'Stored<br>Uses', | ||
vaal_soul_gain_prevention_time = 'Soul<br>Prevention<br>Time', | vaal_soul_gain_prevention_time = 'Soul<br>Prevention<br>Time', | ||
damage_multiplier = m_util.html.abbr('Damage<br>Multiplier', 'Deals x% of | damage_multiplier = m_util.html.abbr('Damage<br>Multiplier', 'Deals x% of base damage'), | ||
duration = m_util.html.abbr('Base duration', 'Base duration is x seconds'), | duration = m_util.html.abbr('Base duration', 'Base duration is x seconds'), | ||
experience = m_util.html.abbr('Exp.', 'Experience needed to level up'), | |||
total_experience = m_util.html.abbr('Total Exp.', 'Total experience needed'), | |||
na = 'N/A', | |||
}, | }, | ||
} | } | ||
Line 99: | Line 177: | ||
local h = {} | local h = {} | ||
function h.map_to_arg(tpl_args, frame, properties, prefix_in, map, level) | function h.map_to_arg(tpl_args, frame, properties, prefix_in, map, level, set_name, set_id) | ||
if map. | if map.fields then | ||
for | for key, row in pairs(map.fields) do | ||
if row.name then | if row.name then | ||
local val = tpl_args[prefix_in .. row.name] | local val = tpl_args[prefix_in .. row.name] | ||
Line 111: | Line 188: | ||
val = row.default | val = row.default | ||
end | end | ||
if val ~= nil then | if val ~= nil then | ||
if level ~= nil then | if level ~= nil then | ||
tpl_args.skill_levels[level][key] = val | 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 | -- Nuke variables since they're remapped to skill_levels | ||
tpl_args[prefix_in .. row.name] = nil | tpl_args[prefix_in .. row.name] = nil | ||
else | else | ||
tpl_args[row.name] = val | 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 | end | ||
properties[row.field] = val | properties[row.field] = val | ||
end | end | ||
-- Deprecated arguments | |||
if row.deprecated then | |||
tpl_args.has_deprecated_arguments = true | |||
end | |||
end | |||
end | |||
end | |||
end | |||
function h.costs(tpl_args, frame, prefix_in, level) | |||
tpl_args.skill_costs = tpl_args.skill_costs or {} | |||
for i=1, #tpl_args.skill_costs do | |||
local cost_prefix = string.format('%s%s%d_', prefix_in, i18n.arguments.skill.cost, i) -- level<level>_cost<i>_ | |||
local cost = { | |||
amount = tpl_args[cost_prefix .. tables.skill_level_costs.fields.amount.name], --level<level>_cost<i>_amount | |||
} | |||
if cost.amount ~= nil then | |||
local properties = { | |||
_table = tables.skill_level_costs.table, | |||
[tables.skill_level_costs.fields.set_id.field] = i, | |||
[tables.skill_level_costs.fields.level.field] = level, | |||
} | |||
h.map_to_arg(tpl_args, frame, properties, cost_prefix, tables.skill_level_costs, level, 'costs', i) | |||
if not tpl_args.test then | |||
m_cargo.store(frame, properties) | |||
end | end | ||
end | end | ||
Line 129: | Line 246: | ||
function h.stats(tpl_args, frame, prefix_in, level) | function h.stats(tpl_args, frame, prefix_in, level) | ||
for i=1, data.max_stats_per_level do | for i=1, data.max_stats_per_level do | ||
local | local stat_prefix = string.format('%s%s%d_', prefix_in, i18n.arguments.skill.stat, i) -- level<level>_stat<i>_ | ||
local stat = { | local stat = { | ||
id = tpl_args[ | id = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.id.name], --level<level>_stat<i>_id | ||
value = | value = tpl_args[stat_prefix .. tables.skill_stats_per_level.fields.value.name], --level<level>_stat<i>_value | ||
} | } | ||
if stat.id ~= nil and stat.value ~= nil then | if stat.id ~= nil and stat.value ~= nil then | ||
local properties = { | |||
_table = tables.skill_stats_per_level.table, | |||
[tables.skill_stats_per_level.fields.level.field] = level, | |||
} | |||
h.map_to_arg(tpl_args, frame, properties, stat_prefix, tables.skill_stats_per_level, level, 'stats', i) | |||
tpl_args.skill_levels.has_stats = true | |||
if not tpl_args.test then | if not tpl_args.test then | ||
m_cargo.store(frame, | m_cargo.store(frame, properties) | ||
end | end | ||
end | end | ||
end | end | ||
Line 161: | Line 270: | ||
:tag('td') | :tag('td') | ||
:attr('class', 'table-na') | :attr('class', 'table-na') | ||
:wikitext( | :wikitext(i18n.progression.na) | ||
:done() | :done() | ||
end | end | ||
Line 212: | Line 321: | ||
function h.display.factory.range_value(args) | function h.display.factory.range_value(args) | ||
return function (tpl_args, frame) | return function (tpl_args, frame) | ||
local value = { | local value = {} | ||
min = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[1][args.key] | if args.set_name and args.set_id then | ||
max = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.key] | -- 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] or tpl_args.skill_levels[1][args.key] | |||
value.max = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.key] | |||
end | |||
-- property not set for this skill | -- property not set for this skill | ||
if value.min == nil or value.max == nil then | if value.min == nil or value.max == nil then | ||
return | return | ||
end | end | ||
local map = args.map or tables.progression | |||
return m_util.html.format_value(tpl_args, frame, value, { | return m_util.html.format_value(tpl_args, frame, value, { | ||
fmt=args.fmt or | fmt=args.fmt or map.fields[args.key].fmt, | ||
no_color=true, | no_color=true, | ||
}) | }) | ||
Line 244: | Line 367: | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Cargo tables | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
tables.static = { | tables.static = { | ||
table = 'skill', | table = 'skill', | ||
fields = { | fields = { | ||
-- GrantedEffects.dat | -- GrantedEffects.dat | ||
skill_id = { | skill_id = { | ||
name = | name = i18n.arguments.skill.skill_id, | ||
field = 'skill_id', | field = 'skill_id', | ||
type = 'String', | type = 'String', | ||
Line 260: | Line 382: | ||
-- Active Skills.dat | -- Active Skills.dat | ||
cast_time = { | cast_time = { | ||
name = | name = i18n.arguments.skill.cast_time, | ||
field = 'cast_time', | field = 'cast_time', | ||
type = 'Float', | type = 'Float', | ||
Line 267: | Line 389: | ||
}, | }, | ||
gem_description = { | gem_description = { | ||
name = | name = i18n.arguments.skill.gem_description, | ||
field = 'description', | field = 'description', | ||
type = 'Text', | type = 'Text', | ||
Line 273: | Line 395: | ||
}, | }, | ||
active_skill_name = { | active_skill_name = { | ||
name = | name = i18n.arguments.skill.active_skill_name, | ||
field = 'active_skill_name', | field = 'active_skill_name', | ||
type = 'String', | type = 'String', | ||
Line 279: | Line 401: | ||
}, | }, | ||
skill_icon = { | skill_icon = { | ||
name = | name = i18n.arguments.skill.skill_icon, | ||
field = 'skill_icon', | field = 'skill_icon', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args, frame) | func = function(tpl_args, frame) | ||
if tpl_args.active_skill_name then | if tpl_args.active_skill_name then | ||
return string.format(i18n.skill_icon, tpl_args.active_skill_name) | return string.format(i18n.files.skill_icon, tpl_args.active_skill_name) | ||
end | end | ||
end, | end, | ||
}, | }, | ||
item_class_id_restriction = { | item_class_id_restriction = { | ||
name = | name = i18n.arguments.skill.item_class_id_restriction, | ||
field = 'item_class_id_restriction', | field = 'item_class_id_restriction', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
Line 299: | Line 421: | ||
for _, v in ipairs(value) do | for _, v in ipairs(value) do | ||
if m_game.constants.item.classes[v] == nil then | if m_game.constants.item.classes[v] == nil then | ||
error(string.format( | error(string.format(i18n.errors.skill.invalid_item_class_id, v)) | ||
end | end | ||
end | end | ||
Line 306: | Line 428: | ||
}, | }, | ||
item_class_restriction = { | item_class_restriction = { | ||
name = | name = i18n.arguments.skill.item_class_restriction, | ||
field = 'item_class_restriction', | field = 'item_class_restriction', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
Line 324: | Line 446: | ||
-- Projectiles.dat - manually mapped to the skills | -- Projectiles.dat - manually mapped to the skills | ||
projectile_speed = { | projectile_speed = { | ||
name = | name = i18n.arguments.skill.projectile_speed, | ||
field = 'projectile_speed', | field = 'projectile_speed', | ||
type = 'Integer', | type = 'Integer', | ||
Line 331: | Line 453: | ||
-- Misc data derieved from stats | -- Misc data derieved from stats | ||
stat_text = { | stat_text = { | ||
name = | name = i18n.arguments.skill.stat_text, | ||
field = 'stat_text', | field = 'stat_text', | ||
type = 'Text', | type = 'Text', | ||
Line 337: | Line 459: | ||
}, | }, | ||
quality_stat_text = { | quality_stat_text = { | ||
name = | name = i18n.arguments.skill.quality_stat_text, | ||
field = 'quality_stat_text', | field = 'quality_stat_text', | ||
type = 'Text', | type = 'Text', | ||
Line 343: | Line 465: | ||
}, | }, | ||
-- Misc data currently not from game data | -- Misc data currently not from game data | ||
radius = { | radius = { | ||
name = | name = i18n.arguments.skill.radius, | ||
field = 'radius', | field = 'radius', | ||
type = 'Integer', | type = 'Integer', | ||
Line 364: | Line 472: | ||
}, | }, | ||
radius_description = { | radius_description = { | ||
name = | name = i18n.arguments.skill.radius_description, | ||
field = 'radius_description', | field = 'radius_description', | ||
type = 'Text', | type = 'Text', | ||
func = | func = h.cast.wrap(m_util.cast.text), | ||
}, | }, | ||
radius_secondary = { | radius_secondary = { | ||
name = | name = i18n.arguments.skill.radius_secondary, | ||
field = 'radius_secondary', | field = 'radius_secondary', | ||
type = 'Integer', | type = 'Integer', | ||
Line 376: | Line 484: | ||
}, | }, | ||
radius_secondary_description = { | radius_secondary_description = { | ||
name = | name = i18n.arguments.skill.radius_secondary_description, | ||
field = 'radius_secondary_description', | field = 'radius_secondary_description', | ||
type = 'Text', | type = 'Text', | ||
func = | func = h.cast.wrap(m_util.cast.text), | ||
}, | }, | ||
-- not sure if any skill actually has 3 radius componets | radius_tertiary = { -- not sure if any skill actually has 3 radius componets | ||
name = i18n.arguments.skill.radius_tertiary, | |||
name = | |||
field = 'radius_tertiary', | field = 'radius_tertiary', | ||
type = 'Integer', | type = 'Integer', | ||
Line 389: | Line 496: | ||
}, | }, | ||
radius_tertiary_description = { | radius_tertiary_description = { | ||
name = | name = i18n.arguments.skill.radius_tertiary_description, | ||
field = 'radius_tertiary_description', | field = 'radius_tertiary_description', | ||
type = 'Text', | type = 'Text', | ||
func = | func = h.cast.wrap(m_util.cast.text), | ||
}, | }, | ||
skill_screenshot = { | skill_screenshot = { | ||
name = | name = i18n.arguments.skill.skill_screenshot, | ||
field = 'skill_screenshot', | field = 'skill_screenshot', | ||
type = 'Page', | type = 'Page', | ||
Line 412: | Line 510: | ||
ss = string.format('File:%s', tpl_args.skill_screenshot_file) | ss = string.format('File:%s', tpl_args.skill_screenshot_file) | ||
elseif tpl_args.skill_screenshot ~= nil then | elseif tpl_args.skill_screenshot ~= nil then | ||
ss = string.format(i18n.skill_screenshot, tpl_args.skill_screenshot) | ss = string.format(i18n.files.skill_screenshot, tpl_args.skill_screenshot) | ||
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 be exist, but otherwise it probably doesn't and we don't need dead links in that case | -- When this parameter is set manually, we assume/expect it to be exist, but otherwise it probably doesn't and we don't need dead links in that case | ||
ss = string.format(i18n.skill_screenshot, tpl_args.active_skill_name) | ss = string.format(i18n.files.skill_screenshot, tpl_args.active_skill_name) | ||
page = mw.title.new(ss) | page = mw.title.new(ss) | ||
if page == nil or not page.exists then | if page == nil or not page.exists then | ||
Line 423: | Line 521: | ||
return ss | return ss | ||
end, | 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, | |||
}, | |||
-- Deprecated | |||
has_percentage_mana_cost = { | |||
name = i18n.arguments.skill.has_percentage_mana_cost, | |||
field = 'has_percentage_mana_cost', | |||
type = 'Boolean', | |||
func = h.cast.wrap(m_util.cast.boolean), | |||
default = false, | |||
deprecated = true, | |||
}, | |||
has_reservation_mana_cost = { | |||
name = i18n.arguments.skill.has_reservation_mana_cost, | |||
field = 'has_reservation_mana_cost', | |||
type = 'Boolean', | |||
func = h.cast.wrap(m_util.cast.boolean), | |||
default = false, | |||
deprecated = true, | |||
}, | }, | ||
}, | }, | ||
Line 429: | Line 557: | ||
tables.progression = { | tables.progression = { | ||
table = 'skill_levels', | table = 'skill_levels', | ||
fields = { | fields = { | ||
level = { | level = { | ||
Line 436: | Line 563: | ||
type = 'Integer', | type = 'Integer', | ||
func = nil, | func = nil, | ||
}, | }, | ||
level_requirement = { | level_requirement = { | ||
name = | name = i18n.arguments.skill.level_requirement, | ||
field = 'level_requirement', | field = 'level_requirement', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
dexterity_requirement = { | dexterity_requirement = { | ||
name = | name = i18n.arguments.skill.dexterity_requirement, | ||
field = 'dexterity_requirement', | field = 'dexterity_requirement', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
strength_requirement = { | strength_requirement = { | ||
name = | name = i18n.arguments.skill.strength_requirement, | ||
field = 'strength_requirement', | field = 'strength_requirement', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
intelligence_requirement = { | intelligence_requirement = { | ||
name = | name = i18n.arguments.skill.intelligence_requirement, | ||
field = 'intelligence_requirement', | field = 'intelligence_requirement', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
mana_multiplier = { | mana_multiplier = { | ||
name = | name = i18n.arguments.skill.mana_multiplier, | ||
field = 'mana_multiplier', | field = 'mana_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%%', | ||
}, | }, | ||
critical_strike_chance = { | critical_strike_chance = { | ||
name = | name = i18n.arguments.skill.critical_strike_chance, | ||
field = 'critical_strike_chance', | field = 'critical_strike_chance', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | }, | ||
damage_effectiveness = { | damage_effectiveness = { | ||
name = | name = i18n.arguments.skill.damage_effectiveness, | ||
field = 'damage_effectiveness', | field = 'damage_effectiveness', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | }, | ||
stored_uses = { | stored_uses = { | ||
name = | name = i18n.arguments.skill.stored_uses, | ||
field = 'stored_uses', | field = 'stored_uses', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
cooldown = { | cooldown = { | ||
name = | name = i18n.arguments.skill.cooldown, | ||
field = 'cooldown', | field = 'cooldown', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%ss', | fmt = '%ss', | ||
}, | }, | ||
vaal_souls_requirement = { | vaal_souls_requirement = { | ||
name = | name = i18n.arguments.skill.vaal_souls_requirement, | ||
field = 'vaal_souls_requirement', | field = 'vaal_souls_requirement', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
}, | }, | ||
vaal_stored_uses = { | vaal_stored_uses = { | ||
name = | name = i18n.arguments.skill.vaal_stored_uses, | ||
field = 'vaal_stored_uses', | field = 'vaal_stored_uses', | ||
type = 'Integer', | type = 'Integer', | ||
Line 542: | Line 636: | ||
}, | }, | ||
vaal_soul_gain_prevention_time = { | vaal_soul_gain_prevention_time = { | ||
name = | name = i18n.arguments.skill.vaal_soul_gain_prevention_time, | ||
field = 'vaal_soul_gain_prevention_time', | field = 'vaal_soul_gain_prevention_time', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%ss', | fmt = '%ss', | ||
}, | }, | ||
damage_multiplier = { | damage_multiplier = { | ||
name = | name = i18n.arguments.skill.damage_multiplier, | ||
field = 'damage_multiplier', | field = 'damage_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_speed_multiplier = { | attack_speed_multiplier = { | ||
name = | name = i18n.arguments.skill.attack_speed_multiplier, | ||
field = 'attack_speed_multiplier', | field = 'attack_speed_multiplier', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%s%%', | fmt = '%s%%', | ||
}, | }, | ||
duration = { | duration = { | ||
name = | name = i18n.arguments.skill.duration, | ||
field = 'duration', | field = 'duration', | ||
type = 'Float', | type = 'Float', | ||
func = h.cast.wrap(m_util.cast.number), | func = h.cast.wrap(m_util.cast.number), | ||
fmt = '%ss', | fmt = '%ss', | ||
}, | }, | ||
-- from gem experience, optional | -- from gem experience, optional | ||
experience = { | experience = { | ||
name = | name = i18n.arguments.skill.experience, | ||
field = '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 = { | stat_text = { | ||
name = | name = i18n.arguments.skill.stat_text, | ||
field = 'stat_text', | field = 'stat_text', | ||
type = 'Text', | type = 'Text', | ||
func = h.cast.wrap(m_util.cast.text), | |||
}, | |||
-- Deprecated | |||
mana_cost = { | |||
name = i18n.arguments.skill.mana_cost, | |||
field = 'mana_cost', | |||
type = 'Integer', | |||
func = h.cast.wrap(m_util.cast.number), | |||
deprecated = true, | |||
}, | |||
} | |||
} | |||
tables.skill_costs = { | |||
table = 'skill_costs', | |||
fields = { | |||
set_id = { | |||
name = nil, | |||
field = 'set_id', | |||
type = 'Integer', | |||
func = nil, | func = nil, | ||
}, | |||
type = { | |||
name = i18n.arguments.skill.cost_type, | |||
field = 'type', | |||
type = 'String', | |||
func = function(tpl_args, frame, value) | |||
if value == nil then | |||
return nil | |||
end | |||
if m_game.constants.skill.cost_types[value] == nil then | |||
error(string.format(i18n.errors.skill.invalid_cost_type, value)) | |||
end | |||
return value | |||
end, | |||
}, | |||
is_reservation = { | |||
name = i18n.arguments.skill.cost_is_reservation, | |||
field = 'is_reservation', | |||
type = 'Boolean', | |||
func = h.cast.wrap(m_util.cast.boolean), | |||
default = false, | |||
}, | }, | ||
} | } | ||
} | } | ||
tables.skill_level_costs = { | |||
table = 'skill_level_costs', | |||
fields = { | |||
set_id = { | |||
name = nil, | |||
field = 'set_id', | |||
type = 'Integer', | |||
func = nil, | |||
}, | |||
level = { | |||
name = nil, | |||
field = 'level', | |||
type = 'Integer', | |||
func = nil, | |||
}, | |||
amount = { | |||
name = i18n.arguments.skill.cost_amount, | |||
field = 'amount', | |||
type = 'Integer', | |||
func = h.cast.wrap(m_util.cast.number), | |||
}, | |||
}, | |||
} | |||
tables.skill_stats_per_level = { | tables.skill_stats_per_level = { | ||
Line 597: | Line 748: | ||
fields = { | fields = { | ||
level = { | level = { | ||
name = nil, | |||
field = 'level', | field = 'level', | ||
type = 'Integer', | type = 'Integer', | ||
func = nil, | |||
}, | }, | ||
id = { | id = { | ||
name = i18n.arguments.skill.stat_id, | |||
field = 'id', | field = 'id', | ||
type = 'String', | type = 'String', | ||
func = h.cast.wrap(m_util.cast.text), | |||
}, | }, | ||
value = { | value = { | ||
name = i18n.arguments.skill.stat_value, | |||
field = 'value', | field = 'value', | ||
type = 'Integer', | type = 'Integer', | ||
func = h.cast.wrap(m_util.cast.number), | |||
}, | }, | ||
}, | }, | ||
} | } | ||
tables.skill_quality = { | tables.skill_quality = { | ||
Line 648: | Line 803: | ||
}, | }, | ||
} | } | ||
-- ---------------------------------------------------------------------------- | |||
-- Data | |||
-- ---------------------------------------------------------------------------- | |||
data.skill_progression_table = { | |||
order = { | |||
'level', | |||
'level_requirement', | |||
'dexterity_requirement', | |||
'strength_requirement', | |||
'intelligence_requirement', | |||
'mana_multiplier', | |||
'critical_strike_chance', | |||
'mana_cost', | |||
'life_cost', | |||
'energy_shield_cost', | |||
'rage_cost', | |||
'mana_reserved', | |||
'life_reserved', | |||
'damage_effectiveness', | |||
'stored_uses', | |||
'cooldown', | |||
'vaal_souls_requirement', | |||
'vaal_stored_uses', | |||
'vaal_soul_gain_prevention_time', | |||
'damage_multiplier', | |||
'duration', | |||
'attack_speed_multiplier', | |||
}, | |||
columns = { | |||
level = { | |||
name = nil, | |||
field = 'level', | |||
func = nil, | |||
header = i18n.progression.level, | |||
}, | |||
level_requirement = { | |||
name = i18n.arguments.skill.level_requirement, | |||
field = 'level_requirement', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.level_requirement, | |||
}, | |||
dexterity_requirement = { | |||
name = i18n.arguments.skill.dexterity_requirement, | |||
field = 'dexterity_requirement', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.dexterity_requirement, | |||
}, | |||
strength_requirement = { | |||
name = i18n.arguments.skill.strength_requirement, | |||
field = 'strength_requirement', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.strength_requirement, | |||
}, | |||
intelligence_requirement = { | |||
name = i18n.arguments.skill.intelligence_requirement, | |||
field = 'intelligence_requirement', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.intelligence_requirement, | |||
}, | |||
mana_multiplier = { | |||
name = i18n.arguments.skill.mana_multiplier, | |||
field = 'mana_multiplier', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.mana_multiplier, | |||
fmt = '%s%%', | |||
}, | |||
critical_strike_chance = { | |||
name = i18n.arguments.skill.critical_strike_chance, | |||
field = 'critical_strike_chance', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.critical_strike_chance, | |||
fmt = '%s%%', | |||
}, | |||
--[[mana_cost = { | |||
name = i18n.arguments.skill.mana_cost, | |||
field = 'mana_cost', | |||
type = 'Integer', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = function (skill_data) | |||
if skill_data["skill.has_reservation_mana_cost"] then | |||
return i18n.progression.mana_reserved | |||
else | |||
return i18n.progression.mana_cost | |||
end | |||
end, | |||
deprecated = true, | |||
},--]] | |||
mana_cost = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.mana_cost, | |||
}, | |||
life_cost = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.life_cost, | |||
}, | |||
energy_shield_cost = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.energy_shield_cost, | |||
}, | |||
rage_cost = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.rage_cost, | |||
}, | |||
mana_reserved = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.mana_reserved, | |||
}, | |||
life_reserved = { | |||
name = nil, | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.life_reserved, | |||
}, | |||
damage_effectiveness = { | |||
name = i18n.arguments.skill.damage_effectiveness, | |||
field = 'damage_effectiveness', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.damage_effectiveness, | |||
fmt = '%s%%', | |||
}, | |||
stored_uses = { | |||
name = i18n.arguments.skill.stored_uses, | |||
field = 'stored_uses', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.stored_uses, | |||
}, | |||
cooldown = { | |||
name = i18n.arguments.skill.cooldown, | |||
field = 'cooldown', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.cooldown, | |||
fmt = '%ss', | |||
}, | |||
vaal_souls_requirement = { | |||
name = i18n.arguments.skill.vaal_souls_requirement, | |||
field = 'vaal_souls_requirement', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.vaal_souls_requirement, | |||
}, | |||
vaal_stored_uses = { | |||
name = i18n.arguments.skill.vaal_stored_uses, | |||
field = 'vaal_stored_uses', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.vaal_stored_uses, | |||
}, | |||
vaal_soul_gain_prevention_time = { | |||
name = i18n.arguments.skill.vaal_soul_gain_prevention_time, | |||
field = 'vaal_soul_gain_prevention_time', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.vaal_soul_gain_prevention_time, | |||
fmt = '%ss', | |||
}, | |||
damage_multiplier = { | |||
name = i18n.arguments.skill.damage_multiplier, | |||
field = 'damage_multiplier', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.damage_multiplier, | |||
fmt = '%s%%', | |||
}, | |||
duration = { | |||
name = i18n.arguments.skill.duration, | |||
field = 'duration', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.duration, | |||
fmt = '%ss', | |||
}, | |||
attack_speed_multiplier = { | |||
name = i18n.arguments.skill.attack_speed_multiplier, | |||
field = 'attack_speed_multiplier', | |||
func = h.cast.wrap(m_util.cast.number), | |||
header = i18n.progression.attack_speed_multiplier, | |||
fmt = '%s%%', | |||
}, | |||
}, | |||
} | |||
data.max_stats_per_level = 8 | |||
data.infobox_table = { | data.infobox_table = { | ||
Line 715: | Line 1,053: | ||
}, | }, | ||
{ | { | ||
header = i18n.infobox.mana_cost, | header = i18n.infobox.cost, | ||
func = h.display.factory.range_value{key='mana_cost'}, | func = function (tpl_args, frame) | ||
if not tpl_args.skill_costs.has_flat_cost then | |||
-- Try falling back to deprecated arguments | |||
if not tpl_args.has_reservation_mana_cost then | |||
local range = h.display.factory.range_value{key='mana_cost'}(tpl_args, frame) | |||
if range then | |||
return string.format('%s %s', range, m_game.constants.skill.cost_types.mana.long_upper) | |||
end | |||
return | |||
end | |||
return | |||
end | |||
local sets = {} | |||
for i=1, #tpl_args.skill_costs do | |||
if not tpl_args.skill_costs[i].is_reservation then -- Only get flat costs | |||
local cost_type = tpl_args.skill_costs[i].type | |||
local range = h.display.factory.range_value{key='amount', set_name='costs', set_id=i, map=tables.skill_level_costs}(tpl_args, frame) | |||
if range then | |||
local fmt | |||
if string.find(cost_type, 'percent', 1, true) then | |||
fmt = '%s%% %s' | |||
else | |||
fmt = '%s %s' | |||
end | |||
sets[#sets+1] = string.format(fmt, range, m_game.constants.skill.cost_types[cost_type].long_upper) | |||
end | |||
end | |||
end | |||
return table.concat(sets, ', ') | |||
end, | |||
}, | |||
{ | |||
header = i18n.infobox.reservation, | |||
func = function (tpl_args, frame) | |||
if not tpl_args.skill_costs.has_reservation_cost then | |||
-- Try falling back to deprecated arguments | |||
if tpl_args.has_reservation_mana_cost then | |||
local range = h.display.factory.range_value{key='mana_cost'}(tpl_args, frame) | |||
if range then | |||
if tpl_args.has_percentage_mana_cost then | |||
return string.format('%s%% %s', range, m_game.constants.skill.cost_types.mana.long_upper) | |||
end | |||
return string.format('%s %s', range, m_game.constants.skill.cost_types.mana.long_upper) | |||
end | |||
return | |||
end | |||
return | |||
end | |||
local sets = {} | |||
for i=1, #tpl_args.skill_costs do | |||
if tpl_args.skill_costs[i].is_reservation then -- Only get reservation costs | |||
local cost_type = tpl_args.skill_costs[i].type | |||
local range = h.display.factory.range_value{key='amount', set_name='costs', set_id=i, map=tables.skill_level_costs}(tpl_args, frame) | |||
if range then | |||
local fmt | |||
if string.find(cost_type, 'percent', 1, true) then | |||
fmt = '%s%% %s' | |||
else | |||
fmt = '%s %s' | |||
end | |||
sets[#sets+1] = string.format(fmt, range, m_game.constants.skill.cost_types[cost_type].long_upper) | |||
end | |||
end | |||
end | |||
return table.concat(sets, ', ') | |||
end, | |||
}, | }, | ||
{ | { | ||
Line 773: | Line 1,176: | ||
p.table_skills = m_cargo.declare_factory{data=tables.static} | p.table_skills = m_cargo.declare_factory{data=tables.static} | ||
p.table_skill_levels = m_cargo.declare_factory{data=tables.progression} | p.table_skill_levels = m_cargo.declare_factory{data=tables.progression} | ||
p.table_skill_costs = m_cargo.declare_factory{data=tables.skill_costs} | |||
p.table_skill_level_costs = m_cargo.declare_factory{data=tables.skill_level_costs} | |||
p.table_skill_stats_per_level = m_cargo.declare_factory{data=tables.skill_stats_per_level} | 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 = m_cargo.declare_factory{data=tables.skill_quality} | ||
Line 796: | Line 1,201: | ||
end | end | ||
frame = m_util.misc.get_frame(frame) | frame = m_util.misc.get_frame(frame) | ||
tpl_args.skill_levels = { | tpl_args.skill_levels = { | ||
Line 843: | Line 1,243: | ||
end | end | ||
until q.stat_text == nil | until q.stat_text == nil | ||
-- Costs | |||
for i=1, math.huge do -- repeat until no more cost sets are found | |||
local prefix = string.format('%s%d_', i18n.arguments.skill.skill_cost, i) | |||
if tpl_args[prefix .. tables.skill_costs.fields.type.field] == nil then | |||
break | |||
end | |||
local properties = { | |||
_table = tables.skill_costs.table, | |||
[tables.skill_costs.fields.set_id.field] = i, | |||
} | |||
h.map_to_arg(tpl_args, frame, properties, prefix, tables.skill_costs, nil, 'skill_costs', i) | |||
if properties.is_reservation then | |||
tpl_args.skill_costs.has_reservation_cost = true | |||
else | |||
tpl_args.skill_costs.has_flat_cost = true | |||
end | |||
tpl_args.skill_costs[i] = { | |||
set_id = properties.set_id, | |||
type = properties.type, | |||
is_reservation = properties.is_reservation, | |||
} | |||
if not tpl_args.test then | |||
m_cargo.store(frame, properties) | |||
end | |||
end | |||
-- Handle level progression | -- Handle level progression | ||
Line 848: | Line 1,274: | ||
repeat | repeat | ||
i = i + 1 | i = i + 1 | ||
local prefix = | local prefix = i18n.arguments.skill.level .. i | ||
local level = m_util.cast.boolean(tpl_args[prefix]) | local level = m_util.cast.boolean(tpl_args[prefix]) | ||
if level == true then | if level == true then | ||
Line 856: | Line 1,282: | ||
prefix = prefix .. '_' | prefix = prefix .. '_' | ||
if tpl_args[prefix .. | if tpl_args[prefix .. i18n.arguments.skill.experience] ~= nil then | ||
tpl_args.max_level = i | tpl_args.max_level = i | ||
end | end | ||
properties = { | local properties = { | ||
_table = tables.progression.table, | _table = tables.progression.table, | ||
[tables.progression.fields.level.field] = i | [tables.progression.fields.level.field] = i | ||
Line 869: | Line 1,295: | ||
end | end | ||
h.costs(tpl_args, frame, prefix, i) | |||
h.stats(tpl_args, frame, prefix, i) | h.stats(tpl_args, frame, prefix, i) | ||
end | end | ||
until level ~= true | until level ~= true | ||
-- If no experience is given, assume this is a non skill gem skill. | -- If no experience is given, assume this is a non skill gem skill. | ||
tpl_args.max_level = tpl_args.max_level or (i - 1) | tpl_args.max_level = tpl_args.max_level or (i - 1) | ||
-- handle static progression | -- handle static progression | ||
properties = { | local prefix = i18n.arguments.skill.static .. '_' | ||
do | |||
local properties = { | |||
_table = tables.progression.table, | |||
[tables.progression.fields.level.field] = 0 | |||
} | |||
h.map_to_arg(tpl_args, frame, properties, prefix, tables.progression, 0) | |||
if not tpl_args.test then | |||
m_cargo.store(frame, properties) | |||
end | |||
end | end | ||
-- Handle static arguments | -- Handle static arguments | ||
properties = { | local properties = { | ||
_table = tables.static.table, | _table = tables.static.table, | ||
[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, frame, properties, '', tables.static) | h.map_to_arg(tpl_args, frame, properties, '', tables.static) | ||
h. | h.costs(tpl_args, frame, prefix, 0) | ||
h.stats(tpl_args, frame, prefix, 0) | |||
-- | -- | ||
Line 909: | Line 1,339: | ||
local tr = tbl:tag('tr') | local tr = tbl:tag('tr') | ||
if infobox_data.header then | if infobox_data.header then | ||
local header_text | |||
if type(infobox_data.header) == 'function' then | |||
header_text = infobox_data.header(tpl_args, frame) | |||
else | |||
header_text = infobox_data.header | |||
end | |||
tr | tr | ||
:tag('th') | :tag('th') | ||
:wikitext( | :wikitext(header_text) | ||
:done() | :done() | ||
end | end | ||
Line 949: | Line 1,385: | ||
out = {} | out = {} | ||
if mw.ustring.find(tpl_args.skill_id, '_') then | if mw.ustring.find(tpl_args.skill_id, '_') then | ||
out[#out+1] = frame:expandTemplate{ | out[#out+1] = frame:expandTemplate{ | ||
title = | title = i18n.templates.incorrect_title, | ||
args = {title=tpl_args.skill_id} | args = {title=tpl_args.skill_id} | ||
} .. '\n\n\n' | } .. '\n\n\n' | ||
end | end | ||
if tpl_args.active_skill_name then | if tpl_args.active_skill_name then | ||
out[#out+1] = string.format( | out[#out+1] = string.format( | ||
i18n.intro_named_id, | i18n.messages.intro_named_id, | ||
tpl_args.skill_id, | tpl_args.skill_id, | ||
tpl_args.active_skill_name | tpl_args.active_skill_name | ||
Line 962: | Line 1,398: | ||
else | else | ||
out[#out+1] = string.format( | out[#out+1] = string.format( | ||
i18n.intro_unnamed_id, | i18n.messages.intro_unnamed_id, | ||
tpl_args.skill_id | tpl_args.skill_id | ||
) | ) | ||
Line 972: | Line 1,408: | ||
tables.static.table, | tables.static.table, | ||
tables.progression.table, | tables.progression.table, | ||
} | } | ||
if #tpl_args.skill_quality > 0 then | if #tpl_args.skill_quality > 0 then | ||
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 | |||
if #tpl_args.skill_costs > 0 then | |||
attach_tables[#attach_tables+1] = tables.skill_costs.table | |||
attach_tables[#attach_tables+1] = tables.skill_level_costs.table | |||
end | |||
if tpl_args.skill_levels.has_stats then | |||
attach_tables[#attach_tables+1] = tables.skill_stats_per_level.table | |||
end | end | ||
for _, table_name in ipairs(attach_tables) do | for _, table_name in ipairs(attach_tables) do | ||
frame:expandTemplate{title=string.format( | frame:expandTemplate{ | ||
title = string.format(i18n.templates.cargo_attach, table_name), | |||
args = {} | |||
} | |||
end | end | ||
end | |||
-- Categories | |||
local cats = {i18n.categories.skill_data} | |||
if tpl_args.has_deprecated_arguments then | |||
cats[#cats+1] = i18n.categories.deprecated_arguments | |||
end | end | ||
return tostring(container) .. m_util.misc.add_category( | return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out) | ||
end | end | ||
Line 1,002: | Line 1,453: | ||
-- Parse column arguments: | -- Parse column arguments: | ||
tpl_args.stat_format = {} | tpl_args.stat_format = {} | ||
local | local argument_keys = { | ||
i18n.arguments.progression.header, | |||
i18n.arguments.progression.abbr, | |||
i18n.arguments.progression.pattern_extract, | |||
i18n.arguments.progression.pattern_value, | |||
} | |||
for i=1, math.huge do -- repeat until no more columns are found | |||
local prefix = string.format('%s%d_', i18n.arguments.progression.column, i) | |||
if tpl_args[prefix .. argument_keys[1]] == nil then | |||
local | |||
break | break | ||
end | end | ||
local statfmt = {counter = 0} | |||
for _, key in ipairs(argument_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 | end | ||
statfmt.header = m_util.html.abbr(statfmt.abbr, statfmt.header) | statfmt.header = m_util.html.abbr(statfmt.abbr, statfmt.header) | ||
statfmt.abbr = nil | statfmt.abbr = nil | ||
Line 1,029: | Line 1,477: | ||
end | end | ||
-- Query skill data | |||
local results = {} | local results = {} | ||
local skill_data | local skill_data | ||
local fields = { | local fields = { | ||
' | '_pageName', | ||
tables.static.fields.has_reservation_mana_cost.name, | |||
tables.static.fields.has_percentage_mana_cost.name, | |||
} | |||
local query = { | |||
groupBy = '_pageID', | |||
} | } | ||
if tpl_args.skill_id then -- Query by skill id | |||
if tpl_args.skill_id then | query.where = string.format('skill_id="%s"', tpl_args.skill_id) | ||
query.where = string.format( | results = m_cargo.query({tables.static.table}, fields, query) | ||
results = m_cargo.query({ | |||
if #results == 0 then | if #results == 0 then | ||
error(i18n.errors.no_results_for_skill_id) | error(string.format(i18n.errors.progression.no_results_for_skill_id, tpl_args.skill_id)) | ||
end | end | ||
else -- Query by page name | |||
else | page = tpl_args.page or mw.title.getCurrentTitle().prefixedText | ||
query.where = string.format('_pageName="%s"', page) | |||
results = m_cargo.query({tables.static.table}, fields, query) | |||
query.where = string.format(' | |||
results = m_cargo.query({ | |||
if #results == 0 then | if #results == 0 then | ||
error(i18n.errors.no_results_for_skill_page) | error(string.format(i18n.errors.progression.no_results_for_skill_page, page)) | ||
end | end | ||
end | end | ||
skill_data = results[1] | |||
skill_data[tables.static.fields.has_reservation_mana_cost.name] = m_util.cast.boolean(skill_data[tables.static.fields.has_reservation_mana_cost.name]) | |||
skill_data[tables.static.fields.has_percentage_mana_cost.name] = m_util.cast.boolean(skill_data[tables.static.fields.has_percentage_mana_cost.name]) | |||
tpl_args.skill_data = skill_data | tpl_args.skill_data = skill_data | ||
-- Query progression data | |||
fields = {} | |||
for _, fmap in pairs(tables.progression.fields) do | |||
query | fields[#fields+1] = fmap.field | ||
end | |||
query = { | |||
) | where = string.format( | ||
'_pageName="%s" AND %s > 0', | |||
skill_data['_pageName'], | |||
tables.progression.fields.level.field | |||
), | |||
groupBy = string.format( | |||
'_pageID, %s', | |||
tables.progression.fields.level.field | |||
), | |||
orderBy = string.format( | |||
'%s ASC', | |||
tables.progression.fields.level.field | |||
), | |||
} | |||
results = m_cargo.query({tables.progression.table}, fields, query) | |||
if #results == 0 then | |||
error(i18n.errors.progression.missing_level_data) | |||
end | |||
skill_data.levels = results | |||
-- Query cost data | |||
fields = {} | fields = {} | ||
for _, | for _, fmap in pairs(tables.skill_costs.fields) do | ||
fields[#fields+1] = | fields[#fields+1] = fmap.field | ||
end | end | ||
query = { | |||
results = m_cargo.query( | where = string.format( | ||
{ | '_pageName="%s"', | ||
skill_data['_pageName'] | |||
{ | ), | ||
where=string.format( | groupBy = string.format( | ||
' | '_pageID, %s', | ||
skill_data[' | tables.skill_costs.fields.set_id.field | ||
), | |||
orderBy = string.format( | |||
'%s ASC', | |||
tables.skill_costs.fields.set_id.field | |||
), | |||
} | |||
results = m_cargo.query({tables.skill_costs.table}, fields, query) | |||
skill_data.costs = results | |||
if #results > 0 then -- If skill has costs, query cost data by levels | |||
fields = {} | |||
for _, fmap in pairs(tables.skill_level_costs.fields) do | |||
fields[#fields+1] = fmap.field | |||
end | |||
query = { | |||
where = string.format( | |||
'_pageName="%s" AND %s > 0', | |||
skill_data['_pageName'], | |||
tables.skill_level_costs.fields.level.field | |||
), | |||
groupBy = string.format( | |||
'_pageID, %s, %s', | |||
tables.skill_level_costs.fields.set_id.field, | |||
tables.skill_level_costs.fields.level.field | |||
), | |||
orderBy = string.format( | |||
'%s ASC, %s ASC', | |||
tables.skill_level_costs.fields.set_id.field, | |||
tables.skill_level_costs.fields.level.field | |||
), | ), | ||
} | } | ||
results = m_cargo.query({tables.skill_level_costs.table}, fields, query) | |||
skill_data.costs_by_level = results | |||
-- Interpolate cost data into level data | |||
local column | |||
for _,cdata in ipairs(skill_data.costs) do | |||
if m_util.cast.boolean(cdata[tables.skill_costs.fields.is_reservation.field]) then | |||
column = string.format('%s_reserved', cdata[tables.skill_costs.fields.type.field]) | |||
else | |||
column = string.format('%s_cost', cdata[tables.skill_costs.fields.type.field]) | |||
end | |||
for _,ldata in ipairs(skill_data.levels) do | |||
for _,rdata in ipairs(skill_data.costs_by_level) do | |||
if rdata[tables.skill_level_costs.fields.set_id.field] == cdata[tables.skill_costs.fields.set_id.field] and rdata[tables.skill_level_costs.fields.level.field] == ldata[tables.progression.fields.level.field] then | |||
ldata[column] = rdata[tables.skill_level_costs.fields.amount.field] | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | end | ||
-- Set up html table headers | |||
headers = {} | headers = {} | ||
for | for _, row in ipairs(skill_data.levels) do | ||
for k, v in pairs(row) do | for k, v in pairs(row) do | ||
headers[k] = true | headers[k] = true | ||
end | end | ||
end | end | ||
local tbl = mw.html.create('table') | local tbl = mw.html.create('table') | ||
tbl | tbl | ||
:attr( | :attr('class', 'wikitable responsive-table skill-progression-table') | ||
local head = tbl:tag('tr') | local head = tbl:tag('tr') | ||
for _, key in ipairs(data.skill_progression_table.order) do | |||
local tmap = data.skill_progression_table.columns[key] | |||
if headers[key] then | |||
local text = type(tmap.header) == 'function' and tmap.header(skill_data) or tmap.header | |||
for _, key in ipairs(data. | |||
local | |||
if | |||
local text | |||
head | head | ||
:tag('th') | :tag('th') | ||
Line 1,135: | Line 1,619: | ||
end | end | ||
end | end | ||
for _, statfmt in ipairs(tpl_args.stat_format) do | for _, statfmt in ipairs(tpl_args.stat_format) do | ||
head | head | ||
Line 1,142: | Line 1,625: | ||
:done() | :done() | ||
end | end | ||
if headers[tables.progression.fields.experience.field] then | |||
if headers[ | |||
head | head | ||
:tag('th') | :tag('th') | ||
:wikitext( | :wikitext(i18n.progression.experience) | ||
:done() | :done() | ||
:tag('th') | :tag('th') | ||
:wikitext( | :wikitext(i18n.progression.total_experience) | ||
:done() | :done() | ||
end | end | ||
-- Table rows | |||
local tblrow | local tblrow | ||
local lastexp = 0 | local lastexp = 0 | ||
local experience | local experience | ||
for _, row in ipairs(skill_data.levels) do | |||
for | |||
tblrow = tbl:tag('tr') | tblrow = tbl:tag('tr') | ||
for _, key in ipairs(data.skill_progression_table.order) do | |||
local tmap = data.skill_progression_table.columns[key] | |||
if headers[key] then | |||
h.int_value_or_na(tpl_args, frame, tblrow, row[key], tmap) | |||
for _, key in ipairs(data. | |||
local | |||
if | |||
h.int_value_or_na( | |||
end | end | ||
end | end | ||
-- stats | -- stats | ||
if row[ | local stats = {} | ||
if row[tables.progression.fields.stat_text.field] then | |||
stats = m_util.string.split( | stats = m_util.string.split( | ||
row[ | row[tables.progression.fields.stat_text.field], | ||
'<br>' | '<br>' | ||
) | ) | ||
end | end | ||
for _, statfmt in ipairs(tpl_args.stat_format) do | for _, statfmt in ipairs(tpl_args.stat_format) do | ||
Line 1,227: | Line 1,689: | ||
-- TODO: Quality stats, afaik no gems use this atm | -- TODO: Quality stats, afaik no gems use this atm | ||
if headers[ | if headers[tables.progression.fields.experience.field] then | ||
experience = tonumber(row[ | experience = tonumber(row[tables.progression.fields.experience.field]) | ||
if experience ~= nil then | if experience ~= nil then | ||
h.int_value_or_na( | h.int_value_or_na(tpl_args, frame, tblrow, experience - lastexp, {}) | ||
lastexp = experience | lastexp = experience | ||
else | else | ||
Line 1,255: | Line 1,710: | ||
return tostring(tbl) .. m_util.misc.add_category(cats) | return tostring(tbl) .. m_util.misc.add_category(cats) | ||
end | end | ||
return p | return p |
Revision as of 11:06, 11 May 2021
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.
-- Skill module
-- ----------------------------------------------------------------------------
-- Includes
-- ----------------------------------------------------------------------------
local getArgs = require('Module:Arguments').getArgs
local m_cargo = require('Module:Cargo')
local m_util = require('Module:Util')
local m_game = require('Module:Game')
local mwlanguage = mw.language.getContentLanguage()
--- define here to avoid errors
local tables = {}
local data = {}
-- ----------------------------------------------------------------------------
-- i18n
-- ----------------------------------------------------------------------------
local i18n = {
arguments = {
skill = {
-- static
static = 'static',
skill_id = 'skill_id',
cast_time = 'cast_time',
gem_description = 'gem_description',
active_skill_name = 'active_skill_name',
skill_icon = 'skill_icon',
item_class_id_restriction = 'item_class_id_restriction',
item_class_restriction = 'item_class_restriction',
projectile_speed = 'projectile_speed',
stat_text = 'stat_text',
quality_stat_text = 'quality_stat_text',
radius = 'radius',
radius_description = 'radius_description',
radius_secondary = 'radius_secondary',
radius_secondary_description = 'radius_secondary_description',
radius_tertiary = 'radius_tertiary',
radius_tertiary_description = 'radius_tertiary_description',
skill_screenshot = 'skill_screenshot',
has_percentage_mana_cost = 'has_percentage_mana_cost',
has_reservation_mana_cost = 'has_reservation_mana_cost',
-- progression
level = 'level',
level_requirement = 'level_requirement',
dexterity_requirement = 'dexterity_requirement',
strength_requirement = 'strength_requirement',
intelligence_requirement = 'intelligence_requirement',
mana_multiplier = 'mana_multiplier',
critical_strike_chance = 'critical_strike_chance',
damage_effectiveness = 'damage_effectiveness',
stored_uses = 'stored_uses',
cooldown = 'cooldown',
vaal_souls_requirement = 'vaal_souls_requirement',
vaal_stored_uses = 'vaal_stored_uses',
vaal_soul_gain_prevention_time = 'vaal_soul_gain_prevention_time',
damage_multiplier = 'damage_multiplier',
attack_speed_multiplier = 'attack_speed_multiplier',
duration = 'duration',
experience = 'experience',
mana_cost = 'mana_cost',
-- costs
skill_cost = 'skill_cost',
cost = 'cost',
cost_type = 'type',
cost_is_reservation = 'is_reservation',
cost_amount = 'amount',
-- stats
stat = 'stat',
stat_id = 'id',
stat_value = 'value',
},
progression = {
column = 'c',
header = 'header',
abbr = 'abbr',
pattern_extract = 'pattern_extract',
pattern_value = 'pattern_value',
},
},
errors = {
skill = {
invalid_item_class_id = 'The item class id "%s" is invalid.',
invalid_cost_type = 'The cost type "%s" is invalid. Acceptable values are "mana", "life", "energy_shield", "rage", "mana_percent" and "life_percent".',
},
progression = {
argument_unspecified = 'The argument "%s" is unspecified.',
no_results_for_skill_id = 'Unable to find skill data for skill id "%s".',
no_results_for_skill_page = 'Unable to find skill data on page "%s".',
missing_level_data = 'Unable to find skill level progression data.',
},
},
templates = {
incorrect_title = 'Template:Incorrect title',
cargo_attach = 'Template:Skill/cargo/attach/%s',
},
categories = {
skill_data = 'Skill data',
deprecated_arguments = 'Pages with deprecated arguments for skill data',
broken_progression_table = 'Pages with broken skill progression tables',
},
files = {
skill_icon = 'File:%s skill icon.png',
skill_screenshot = 'File:%s skill screenshot.jpg',
},
messages = {
intro_named_id = "'''%s''' is the internal id of the [[skill]] '''%s'''.\n",
intro_unnamed_id = "'''%s''' is the internal id of an unnamed [[skill]].\n",
},
infobox = {
skill_id = 'Skill Id',
active_skill_name = 'Name',
skill_icon = 'Icon',
cast_time = 'Cast Time',
item_class_restrictions = 'Item Class<br>Restrictions',
projectile_speed = 'Projectile Speed',
radius = 'Radius',
radius_secondary = 'Radius 2',
radius_tertiary = 'Radius 3',
level_requirement = 'Level Req.',
mana_multiplier = 'Mana Multiplier',
critical_strike_chance = 'Critical Strike Chance',
cost = 'Cost',
reservation = 'Reservation',
attack_speed_multiplier = 'Attack Speed',
damage_effectiveness = 'Effectiveness of Added Damage',
stored_uses = 'Stored Uses',
cooldown = 'Cooldown',
vaal_souls_requirement = 'Vaal Souls',
vaal_stored_uses = 'Vaal Stored Uses',
vaal_soul_gain_prevention_time = 'Soul Gain Prevention',
damage_multiplier = 'Damage Multiplier',
duration = 'Base duration',
},
progression = {
level = 'Level',
level_requirement = m_util.html.abbr('[[Image:Level_up_icon_small.png|link=|Lvl.]]', 'Required Level', 'nounderline'),
dexterity_requirement = m_util.html.abbr('[[Image:DexterityIcon_small.png|link=|dexterity]]', 'Required Dexterity', 'nounderline'),
strength_requirement = m_util.html.abbr('[[Image:StrengthIcon_small.png|link=|strength]]', 'Required Strength', 'nounderline'),
intelligence_requirement = m_util.html.abbr('[[Image:IntelligenceIcon_small.png|link=|intelligence]]', 'Required Intelligence', 'nounderline'),
mana_multiplier = 'Mana<br>Multiplier',
critical_strike_chance = 'Critical<br>Strike<br>Chance',
mana_cost = 'Mana<br>Cost',
life_cost = 'Life<br>Cost',
energy_shield_cost = m_util.html.abbr('ES Cost', 'Energy shield cost'),
rage_cost = 'Rage<br>Cost',
mana_reserved = 'Mana<br>Reserved',
life_reserved = 'Life<br>Reserved',
attack_speed_multiplier = 'Attack<br>Speed<br>Multiplier',
damage_effectiveness = 'Damage<br>Effectiveness',
stored_uses = 'Stored<br>Uses',
cooldown = 'Cooldown',
vaal_souls_requirement = 'Vaal<br>souls',
vaal_stored_uses = 'Stored<br>Uses',
vaal_soul_gain_prevention_time = 'Soul<br>Prevention<br>Time',
damage_multiplier = m_util.html.abbr('Damage<br>Multiplier', 'Deals x% of base damage'),
duration = m_util.html.abbr('Base duration', 'Base duration is x seconds'),
experience = m_util.html.abbr('Exp.', 'Experience needed to level up'),
total_experience = m_util.html.abbr('Total Exp.', 'Total experience needed'),
na = 'N/A',
},
}
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
function h.map_to_arg(tpl_args, frame, 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, frame, 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
end
-- Deprecated arguments
if row.deprecated then
tpl_args.has_deprecated_arguments = true
end
end
end
end
end
function h.costs(tpl_args, frame, prefix_in, level)
tpl_args.skill_costs = tpl_args.skill_costs or {}
for i=1, #tpl_args.skill_costs do
local cost_prefix = string.format('%s%s%d_', prefix_in, i18n.arguments.skill.cost, i) -- level<level>_cost<i>_
local cost = {
amount = tpl_args[cost_prefix .. tables.skill_level_costs.fields.amount.name], --level<level>_cost<i>_amount
}
if cost.amount ~= nil then
local properties = {
_table = tables.skill_level_costs.table,
[tables.skill_level_costs.fields.set_id.field] = i,
[tables.skill_level_costs.fields.level.field] = level,
}
h.map_to_arg(tpl_args, frame, properties, cost_prefix, tables.skill_level_costs, level, 'costs', i)
if not tpl_args.test then
m_cargo.store(frame, properties)
end
end
end
end
function h.stats(tpl_args, frame, prefix_in, level)
for i=1, data.max_stats_per_level do
local stat_prefix = string.format('%s%s%d_', prefix_in, i18n.arguments.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 and stat.value ~= nil then
local properties = {
_table = tables.skill_stats_per_level.table,
[tables.skill_stats_per_level.fields.level.field] = level,
}
h.map_to_arg(tpl_args, frame, 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(frame, properties)
end
end
end
end
function h.na(tr)
tr
:tag('td')
:attr('class', 'table-na')
:wikitext(i18n.progression.na)
:done()
end
function h.int_value_or_na(tpl_args, frame, tr, value, pdata)
value = tonumber(value)
if value == nil then
h.na(tr)
else
value = mwlanguage:formatNum(value)
if pdata.fmt ~= nil then
if type(pdata.fmt) == 'string' then
value = string.format(pdata.fmt, value)
elseif type(pdata.fmt) == 'function' then
value = string.format(pdata.fmt(tpl_args, frame), value)
end
end
tr
:tag('td')
:wikitext(value)
:done()
end
end
h.cast = {}
function h.cast.wrap (f)
return function(tpl_args, frame, value)
if value == nil then
return nil
else
return f(value)
end
end
end
h.display = {}
h.display.factory = {}
function h.display.factory.value(args)
return function (tpl_args, frame)
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, frame)
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] or tpl_args.skill_levels[1][args.key]
value.max = tpl_args.skill_levels[0][args.key] or tpl_args.skill_levels[tpl_args.max_level][args.key]
end
-- property not set for this skill
if value.min == nil or value.max == nil then
return
end
local map = args.map or tables.progression
return m_util.html.format_value(tpl_args, frame, value, {
fmt=args.fmt or map.fields[args.key].fmt,
no_color=true,
})
end
end
function h.display.factory.radius(args)
return function (tpl_args, frame)
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
-- ----------------------------------------------------------------------------
-- Cargo tables
-- ----------------------------------------------------------------------------
tables.static = {
table = 'skill',
fields = {
-- GrantedEffects.dat
skill_id = {
name = i18n.arguments.skill.skill_id,
field = 'skill_id',
type = 'String',
func = nil,
},
-- Active Skills.dat
cast_time = {
name = i18n.arguments.skill.cast_time,
field = 'cast_time',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%ss',
},
gem_description = {
name = i18n.arguments.skill.gem_description,
field = 'description',
type = 'Text',
func = nil,
},
active_skill_name = {
name = i18n.arguments.skill.active_skill_name,
field = 'active_skill_name',
type = 'String',
func = nil,
},
skill_icon = {
name = i18n.arguments.skill.skill_icon,
field = 'skill_icon',
type = 'Page',
func = function(tpl_args, frame)
if tpl_args.active_skill_name then
return string.format(i18n.files.skill_icon, tpl_args.active_skill_name)
end
end,
},
item_class_id_restriction = {
name = i18n.arguments.skill.item_class_id_restriction,
field = 'item_class_id_restriction',
type = 'List (,) of String',
func = function(tpl_args, frame, 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.arguments.skill.item_class_restriction,
field = 'item_class_restriction',
type = 'List (,) of String',
func = function(tpl_args, frame, 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.arguments.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.arguments.skill.stat_text,
field = 'stat_text',
type = 'Text',
func = nil,
},
quality_stat_text = {
name = i18n.arguments.skill.quality_stat_text,
field = 'quality_stat_text',
type = 'Text',
func = nil,
},
-- Misc data currently not from game data
radius = {
name = i18n.arguments.skill.radius,
field = 'radius',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_description = {
name = i18n.arguments.skill.radius_description,
field = 'radius_description',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
radius_secondary = {
name = i18n.arguments.skill.radius_secondary,
field = 'radius_secondary',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_secondary_description = {
name = i18n.arguments.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.arguments.skill.radius_tertiary,
field = 'radius_tertiary',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
radius_tertiary_description = {
name = i18n.arguments.skill.radius_tertiary_description,
field = 'radius_tertiary_description',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
skill_screenshot = {
name = i18n.arguments.skill.skill_screenshot,
field = 'skill_screenshot',
type = 'Page',
func = function(tpl_args, frame)
local ss
if tpl_args.skill_screenshot_file ~= nil then
ss = string.format('File:%s', tpl_args.skill_screenshot_file)
elseif tpl_args.skill_screenshot ~= nil then
ss = string.format(i18n.files.skill_screenshot, tpl_args.skill_screenshot)
elseif tpl_args.active_skill_name then
-- When this parameter is set manually, we assume/expect it to be exist, but otherwise it probably doesn't and we don't need dead links in that case
ss = string.format(i18n.files.skill_screenshot, tpl_args.active_skill_name)
page = mw.title.new(ss)
if page == nil or not page.exists then
ss = nil
end
end
return ss
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,
},
-- Deprecated
has_percentage_mana_cost = {
name = i18n.arguments.skill.has_percentage_mana_cost,
field = 'has_percentage_mana_cost',
type = 'Boolean',
func = h.cast.wrap(m_util.cast.boolean),
default = false,
deprecated = true,
},
has_reservation_mana_cost = {
name = i18n.arguments.skill.has_reservation_mana_cost,
field = 'has_reservation_mana_cost',
type = 'Boolean',
func = h.cast.wrap(m_util.cast.boolean),
default = false,
deprecated = true,
},
},
}
tables.progression = {
table = 'skill_levels',
fields = {
level = {
name = nil,
field = 'level',
type = 'Integer',
func = nil,
},
level_requirement = {
name = i18n.arguments.skill.level_requirement,
field = 'level_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
dexterity_requirement = {
name = i18n.arguments.skill.dexterity_requirement,
field = 'dexterity_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
strength_requirement = {
name = i18n.arguments.skill.strength_requirement,
field = 'strength_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
intelligence_requirement = {
name = i18n.arguments.skill.intelligence_requirement,
field = 'intelligence_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
mana_multiplier = {
name = i18n.arguments.skill.mana_multiplier,
field = 'mana_multiplier',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
critical_strike_chance = {
name = i18n.arguments.skill.critical_strike_chance,
field = 'critical_strike_chance',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
damage_effectiveness = {
name = i18n.arguments.skill.damage_effectiveness,
field = 'damage_effectiveness',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
stored_uses = {
name = i18n.arguments.skill.stored_uses,
field = 'stored_uses',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
cooldown = {
name = i18n.arguments.skill.cooldown,
field = 'cooldown',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%ss',
},
vaal_souls_requirement = {
name = i18n.arguments.skill.vaal_souls_requirement,
field = 'vaal_souls_requirement',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
vaal_stored_uses = {
name = i18n.arguments.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.arguments.skill.vaal_soul_gain_prevention_time,
field = 'vaal_soul_gain_prevention_time',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%ss',
},
damage_multiplier = {
name = i18n.arguments.skill.damage_multiplier,
field = 'damage_multiplier',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
attack_speed_multiplier = {
name = i18n.arguments.skill.attack_speed_multiplier,
field = 'attack_speed_multiplier',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
fmt = '%s%%',
},
duration = {
name = i18n.arguments.skill.duration,
field = 'duration',
type = 'Float',
func = h.cast.wrap(m_util.cast.number),
fmt = '%ss',
},
-- from gem experience, optional
experience = {
name = i18n.arguments.skill.experience,
field = 'experience',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
stat_text = {
name = i18n.arguments.skill.stat_text,
field = 'stat_text',
type = 'Text',
func = h.cast.wrap(m_util.cast.text),
},
-- Deprecated
mana_cost = {
name = i18n.arguments.skill.mana_cost,
field = 'mana_cost',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
deprecated = true,
},
}
}
tables.skill_costs = {
table = 'skill_costs',
fields = {
set_id = {
name = nil,
field = 'set_id',
type = 'Integer',
func = nil,
},
type = {
name = i18n.arguments.skill.cost_type,
field = 'type',
type = 'String',
func = function(tpl_args, frame, value)
if value == nil then
return nil
end
if m_game.constants.skill.cost_types[value] == nil then
error(string.format(i18n.errors.skill.invalid_cost_type, value))
end
return value
end,
},
is_reservation = {
name = i18n.arguments.skill.cost_is_reservation,
field = 'is_reservation',
type = 'Boolean',
func = h.cast.wrap(m_util.cast.boolean),
default = false,
},
}
}
tables.skill_level_costs = {
table = 'skill_level_costs',
fields = {
set_id = {
name = nil,
field = 'set_id',
type = 'Integer',
func = nil,
},
level = {
name = nil,
field = 'level',
type = 'Integer',
func = nil,
},
amount = {
name = i18n.arguments.skill.cost_amount,
field = 'amount',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
},
},
}
tables.skill_stats_per_level = {
table = 'skill_stats_per_level',
fields = {
level = {
name = nil,
field = 'level',
type = 'Integer',
func = nil,
},
id = {
name = i18n.arguments.skill.stat_id,
field = 'id',
type = 'String',
func = h.cast.wrap(m_util.cast.text),
},
value = {
name = i18n.arguments.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 = {
order = {
'level',
'level_requirement',
'dexterity_requirement',
'strength_requirement',
'intelligence_requirement',
'mana_multiplier',
'critical_strike_chance',
'mana_cost',
'life_cost',
'energy_shield_cost',
'rage_cost',
'mana_reserved',
'life_reserved',
'damage_effectiveness',
'stored_uses',
'cooldown',
'vaal_souls_requirement',
'vaal_stored_uses',
'vaal_soul_gain_prevention_time',
'damage_multiplier',
'duration',
'attack_speed_multiplier',
},
columns = {
level = {
name = nil,
field = 'level',
func = nil,
header = i18n.progression.level,
},
level_requirement = {
name = i18n.arguments.skill.level_requirement,
field = 'level_requirement',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.level_requirement,
},
dexterity_requirement = {
name = i18n.arguments.skill.dexterity_requirement,
field = 'dexterity_requirement',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.dexterity_requirement,
},
strength_requirement = {
name = i18n.arguments.skill.strength_requirement,
field = 'strength_requirement',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.strength_requirement,
},
intelligence_requirement = {
name = i18n.arguments.skill.intelligence_requirement,
field = 'intelligence_requirement',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.intelligence_requirement,
},
mana_multiplier = {
name = i18n.arguments.skill.mana_multiplier,
field = 'mana_multiplier',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.mana_multiplier,
fmt = '%s%%',
},
critical_strike_chance = {
name = i18n.arguments.skill.critical_strike_chance,
field = 'critical_strike_chance',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.critical_strike_chance,
fmt = '%s%%',
},
--[[mana_cost = {
name = i18n.arguments.skill.mana_cost,
field = 'mana_cost',
type = 'Integer',
func = h.cast.wrap(m_util.cast.number),
header = function (skill_data)
if skill_data["skill.has_reservation_mana_cost"] then
return i18n.progression.mana_reserved
else
return i18n.progression.mana_cost
end
end,
deprecated = true,
},--]]
mana_cost = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.mana_cost,
},
life_cost = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.life_cost,
},
energy_shield_cost = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.energy_shield_cost,
},
rage_cost = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.rage_cost,
},
mana_reserved = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.mana_reserved,
},
life_reserved = {
name = nil,
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.life_reserved,
},
damage_effectiveness = {
name = i18n.arguments.skill.damage_effectiveness,
field = 'damage_effectiveness',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.damage_effectiveness,
fmt = '%s%%',
},
stored_uses = {
name = i18n.arguments.skill.stored_uses,
field = 'stored_uses',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.stored_uses,
},
cooldown = {
name = i18n.arguments.skill.cooldown,
field = 'cooldown',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.cooldown,
fmt = '%ss',
},
vaal_souls_requirement = {
name = i18n.arguments.skill.vaal_souls_requirement,
field = 'vaal_souls_requirement',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.vaal_souls_requirement,
},
vaal_stored_uses = {
name = i18n.arguments.skill.vaal_stored_uses,
field = 'vaal_stored_uses',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.vaal_stored_uses,
},
vaal_soul_gain_prevention_time = {
name = i18n.arguments.skill.vaal_soul_gain_prevention_time,
field = 'vaal_soul_gain_prevention_time',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.vaal_soul_gain_prevention_time,
fmt = '%ss',
},
damage_multiplier = {
name = i18n.arguments.skill.damage_multiplier,
field = 'damage_multiplier',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.damage_multiplier,
fmt = '%s%%',
},
duration = {
name = i18n.arguments.skill.duration,
field = 'duration',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.duration,
fmt = '%ss',
},
attack_speed_multiplier = {
name = i18n.arguments.skill.attack_speed_multiplier,
field = 'attack_speed_multiplier',
func = h.cast.wrap(m_util.cast.number),
header = i18n.progression.attack_speed_multiplier,
fmt = '%s%%',
},
},
}
data.max_stats_per_level = 8
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, frame)
return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.skill_id)
end
},
{
header = i18n.infobox.skill_icon,
func = function (tpl_args, frame)
if tpl_args.skill_icon then
return string.format('[[%s]]', tpl_args.skill_icon)
end
end,
},
{
header = i18n.infobox.cast_time,
func = h.display.factory.value{key='cast_time'},
},
{
header = i18n.infobox.item_class_restrictions,
func = function (tpl_args, frame)
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'},
},
-- ingore attrbiutes?
{
header = i18n.infobox.mana_multiplier,
func = h.display.factory.range_value{key='mana_multiplier'},
},
{
header = i18n.infobox.critical_strike_chance,
func = h.display.factory.range_value{key='critical_strike_chance'},
},
{
header = i18n.infobox.cost,
func = function (tpl_args, frame)
if not tpl_args.skill_costs.has_flat_cost then
-- Try falling back to deprecated arguments
if not tpl_args.has_reservation_mana_cost then
local range = h.display.factory.range_value{key='mana_cost'}(tpl_args, frame)
if range then
return string.format('%s %s', range, m_game.constants.skill.cost_types.mana.long_upper)
end
return
end
return
end
local sets = {}
for i=1, #tpl_args.skill_costs do
if not tpl_args.skill_costs[i].is_reservation then -- Only get flat costs
local cost_type = tpl_args.skill_costs[i].type
local range = h.display.factory.range_value{key='amount', set_name='costs', set_id=i, map=tables.skill_level_costs}(tpl_args, frame)
if range then
local fmt
if string.find(cost_type, 'percent', 1, true) then
fmt = '%s%% %s'
else
fmt = '%s %s'
end
sets[#sets+1] = string.format(fmt, range, m_game.constants.skill.cost_types[cost_type].long_upper)
end
end
end
return table.concat(sets, ', ')
end,
},
{
header = i18n.infobox.reservation,
func = function (tpl_args, frame)
if not tpl_args.skill_costs.has_reservation_cost then
-- Try falling back to deprecated arguments
if tpl_args.has_reservation_mana_cost then
local range = h.display.factory.range_value{key='mana_cost'}(tpl_args, frame)
if range then
if tpl_args.has_percentage_mana_cost then
return string.format('%s%% %s', range, m_game.constants.skill.cost_types.mana.long_upper)
end
return string.format('%s %s', range, m_game.constants.skill.cost_types.mana.long_upper)
end
return
end
return
end
local sets = {}
for i=1, #tpl_args.skill_costs do
if tpl_args.skill_costs[i].is_reservation then -- Only get reservation costs
local cost_type = tpl_args.skill_costs[i].type
local range = h.display.factory.range_value{key='amount', set_name='costs', set_id=i, map=tables.skill_level_costs}(tpl_args, frame)
if range then
local fmt
if string.find(cost_type, 'percent', 1, true) then
fmt = '%s%% %s'
else
fmt = '%s %s'
end
sets[#sets+1] = string.format(fmt, range, m_game.constants.skill.cost_types[cost_type].long_upper)
end
end
end
return table.concat(sets, ', ')
end,
},
{
header = i18n.infobox.attack_speed_multiplier,
func = h.display.factory.range_value{key='attack_speed_multiplier'},
},
{
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.damage_multiplier,
func = h.display.factory.range_value{key='damage_multiplier'},
},
{
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',
},
}
-- ----------------------------------------------------------------------------
-- Templates
-- ----------------------------------------------------------------------------
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_costs = m_cargo.declare_factory{data=tables.skill_costs}
p.table_skill_level_costs = m_cargo.declare_factory{data=tables.skill_level_costs}
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}
--
-- Template:Skill
--
function p.skill(frame, tpl_args)
--[[
Creates an infobox for skills.
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}
]]
if tpl_args == nil then
tpl_args = getArgs(frame, {
parentFirst = true
})
end
frame = m_util.misc.get_frame(frame)
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(frame, 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(frame, s)
end
s._table = nil
until s.id == nil or s.value == nil
end
until q.stat_text == nil
-- Costs
for i=1, math.huge do -- repeat until no more cost sets are found
local prefix = string.format('%s%d_', i18n.arguments.skill.skill_cost, i)
if tpl_args[prefix .. tables.skill_costs.fields.type.field] == nil then
break
end
local properties = {
_table = tables.skill_costs.table,
[tables.skill_costs.fields.set_id.field] = i,
}
h.map_to_arg(tpl_args, frame, properties, prefix, tables.skill_costs, nil, 'skill_costs', i)
if properties.is_reservation then
tpl_args.skill_costs.has_reservation_cost = true
else
tpl_args.skill_costs.has_flat_cost = true
end
tpl_args.skill_costs[i] = {
set_id = properties.set_id,
type = properties.type,
is_reservation = properties.is_reservation,
}
if not tpl_args.test then
m_cargo.store(frame, properties)
end
end
-- Handle level progression
local i = 0
repeat
i = i + 1
local prefix = i18n.arguments.skill.level .. i
local level = m_util.cast.boolean(tpl_args[prefix])
if level == true then
-- Don't need this anymore
tpl_args[prefix] = nil
tpl_args.skill_levels[i] = {}
prefix = prefix .. '_'
if tpl_args[prefix .. i18n.arguments.skill.experience] ~= nil then
tpl_args.max_level = i
end
local properties = {
_table = tables.progression.table,
[tables.progression.fields.level.field] = i
}
h.map_to_arg(tpl_args, frame, properties, prefix, tables.progression, i)
if not tpl_args.test then
m_cargo.store(frame, properties)
end
h.costs(tpl_args, frame, prefix, i)
h.stats(tpl_args, frame, prefix, i)
end
until level ~= true
-- If no experience is given, assume this is a non skill gem skill.
tpl_args.max_level = tpl_args.max_level or (i - 1)
-- handle static progression
local prefix = i18n.arguments.skill.static .. '_'
do
local properties = {
_table = tables.progression.table,
[tables.progression.fields.level.field] = 0
}
h.map_to_arg(tpl_args, frame, properties, prefix, tables.progression, 0)
if not tpl_args.test then
m_cargo.store(frame, properties)
end
end
-- 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, frame, properties, '', tables.static)
h.costs(tpl_args, frame, prefix, 0)
h.stats(tpl_args, frame, prefix, 0)
--
-- Infobox progressing
--
local infobox = mw.html.create('span')
infobox:attr('class', 'skill-box')
-- tablular sections
local tbl = infobox:tag('table')
tbl:attr('class', 'wikitable skill-box-table')
for _, infobox_data in ipairs(data.infobox_table) do
local display = infobox_data.func(tpl_args, frame)
if display then
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, frame)
else
header_text = infobox_data.header
end
tr
:tag('th')
:wikitext(header_text)
:done()
end
local td = tr:tag('td')
td:wikitext(display)
td:attr('class', 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(frame, properties)
end
--
--
--
local container = mw.html.create('span')
container
:attr('class', '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:
out = {}
if mw.ustring.find(tpl_args.skill_id, '_') then
out[#out+1] = frame: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
-- 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_costs > 0 then
attach_tables[#attach_tables+1] = tables.skill_costs.table
attach_tables[#attach_tables+1] = tables.skill_level_costs.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
frame:expandTemplate{
title = string.format(i18n.templates.cargo_attach, table_name),
args = {}
}
end
end
-- Categories
local cats = {i18n.categories.skill_data}
if tpl_args.has_deprecated_arguments then
cats[#cats+1] = i18n.categories.deprecated_arguments
end
return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end
function p.progression(frame)
--[[
Displays the level progression for the skill gem.
Examples
--------
= p.progression{page='Reave'}
]]
local tpl_args = getArgs(frame, {
parentFirst = true
})
frame = m_util.misc.get_frame(frame)
-- Parse column arguments:
tpl_args.stat_format = {}
local argument_keys = {
i18n.arguments.progression.header,
i18n.arguments.progression.abbr,
i18n.arguments.progression.pattern_extract,
i18n.arguments.progression.pattern_value,
}
for i=1, math.huge do -- repeat until no more columns are found
local prefix = string.format('%s%d_', i18n.arguments.progression.column, i)
if tpl_args[prefix .. argument_keys[1]] == nil then
break
end
local statfmt = {counter = 0}
for _, key in ipairs(argument_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 results = {}
local skill_data
local fields = {
'_pageName',
tables.static.fields.has_reservation_mana_cost.name,
tables.static.fields.has_percentage_mana_cost.name,
}
local query = {
groupBy = '_pageID',
}
if tpl_args.skill_id then -- Query by skill id
query.where = string.format('skill_id="%s"', tpl_args.skill_id)
results = m_cargo.query({tables.static.table}, fields, query)
if #results == 0 then
error(string.format(i18n.errors.progression.no_results_for_skill_id, tpl_args.skill_id))
end
else -- Query by page name
page = tpl_args.page or mw.title.getCurrentTitle().prefixedText
query.where = string.format('_pageName="%s"', page)
results = m_cargo.query({tables.static.table}, fields, query)
if #results == 0 then
error(string.format(i18n.errors.progression.no_results_for_skill_page, page))
end
end
skill_data = results[1]
skill_data[tables.static.fields.has_reservation_mana_cost.name] = m_util.cast.boolean(skill_data[tables.static.fields.has_reservation_mana_cost.name])
skill_data[tables.static.fields.has_percentage_mana_cost.name] = m_util.cast.boolean(skill_data[tables.static.fields.has_percentage_mana_cost.name])
tpl_args.skill_data = skill_data
-- Query progression data
fields = {}
for _, fmap in pairs(tables.progression.fields) do
fields[#fields+1] = fmap.field
end
query = {
where = string.format(
'_pageName="%s" AND %s > 0',
skill_data['_pageName'],
tables.progression.fields.level.field
),
groupBy = string.format(
'_pageID, %s',
tables.progression.fields.level.field
),
orderBy = string.format(
'%s ASC',
tables.progression.fields.level.field
),
}
results = m_cargo.query({tables.progression.table}, fields, query)
if #results == 0 then
error(i18n.errors.progression.missing_level_data)
end
skill_data.levels = results
-- Query cost data
fields = {}
for _, fmap in pairs(tables.skill_costs.fields) do
fields[#fields+1] = fmap.field
end
query = {
where = string.format(
'_pageName="%s"',
skill_data['_pageName']
),
groupBy = string.format(
'_pageID, %s',
tables.skill_costs.fields.set_id.field
),
orderBy = string.format(
'%s ASC',
tables.skill_costs.fields.set_id.field
),
}
results = m_cargo.query({tables.skill_costs.table}, fields, query)
skill_data.costs = results
if #results > 0 then -- If skill has costs, query cost data by levels
fields = {}
for _, fmap in pairs(tables.skill_level_costs.fields) do
fields[#fields+1] = fmap.field
end
query = {
where = string.format(
'_pageName="%s" AND %s > 0',
skill_data['_pageName'],
tables.skill_level_costs.fields.level.field
),
groupBy = string.format(
'_pageID, %s, %s',
tables.skill_level_costs.fields.set_id.field,
tables.skill_level_costs.fields.level.field
),
orderBy = string.format(
'%s ASC, %s ASC',
tables.skill_level_costs.fields.set_id.field,
tables.skill_level_costs.fields.level.field
),
}
results = m_cargo.query({tables.skill_level_costs.table}, fields, query)
skill_data.costs_by_level = results
-- Interpolate cost data into level data
local column
for _,cdata in ipairs(skill_data.costs) do
if m_util.cast.boolean(cdata[tables.skill_costs.fields.is_reservation.field]) then
column = string.format('%s_reserved', cdata[tables.skill_costs.fields.type.field])
else
column = string.format('%s_cost', cdata[tables.skill_costs.fields.type.field])
end
for _,ldata in ipairs(skill_data.levels) do
for _,rdata in ipairs(skill_data.costs_by_level) do
if rdata[tables.skill_level_costs.fields.set_id.field] == cdata[tables.skill_costs.fields.set_id.field] and rdata[tables.skill_level_costs.fields.level.field] == ldata[tables.progression.fields.level.field] then
ldata[column] = rdata[tables.skill_level_costs.fields.amount.field]
break
end
end
end
end
end
-- Set up html table headers
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
:attr('class', 'wikitable responsive-table skill-progression-table')
local head = tbl:tag('tr')
for _, key in ipairs(data.skill_progression_table.order) do
local tmap = data.skill_progression_table.columns[key]
if headers[key] then
local text = type(tmap.header) == 'function' and tmap.header(skill_data) 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 _, key in ipairs(data.skill_progression_table.order) do
local tmap = data.skill_progression_table.columns[key]
if headers[key] then
h.int_value_or_na(tpl_args, frame, tblrow, row[key], 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
h.na(tblrow)
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
-- TODO: Quality stats, afaik no gems use this atm
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, frame, tblrow, experience - lastexp, {})
lastexp = experience
else
h.na(tblrow)
end
h.int_value_or_na(tpl_args, frame, 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
return p