Module:Passive skill: Difference between revisions
Jump to navigation
Jump to search
mNo edit summary |
(Bringing in some changes from poewiki:Module:Passive_skill) |
||
| (22 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
-- | ------------------------------------------------------------------------------- | ||
-- Module | -- | ||
-- | -- Module:Passive skill | ||
-- | |||
-- This module implements [[Template:Passive skill]] and | |||
-- [[Template:Passive skill box]] | |||
------------------------------------------------------------------------------- | |||
require('strict') | |||
local m_util = require('Module:Util') | local m_util = require('Module:Util') | ||
local | -- Should we use the sandbox version of our submodules? | ||
local use_sandbox = m_util.misc.maybe_sandbox('Passive skill') | |||
local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo') | |||
-- Lazy loading | |||
local f_infocard -- require('Module:Infocard').main | |||
-- The cfg table contains all localisable strings and configuration, to make it | |||
-- easier to port this module to another wiki. | |||
local cfg = use_sandbox and mw.loadData('Module:Passive skill/config/sandbox') or mw.loadData('Module:Passive skill/config') | |||
local i18n = cfg.i18n | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Helper functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local h = {} | ||
-- Lazy loading for Module:Infocard | |||
function h.infocard(args) | |||
if not f_infocard then | |||
f_infocard = use_sandbox and require('Module:Infocard/sandbox').main or require('Module:Infocard').main | |||
end | |||
return f_infocard(args) | |||
end | |||
function h.format_passive_icon(passive, passive_type) | |||
if passive['passive_skills.icon'] == nil then | |||
return '' | |||
end | |||
local cls = string.format('passive-icon-type__%s', passive_type) | |||
local main_page = passive['main_pages._pageName'] or passive['passive_skills.name'] or passive['passive_skills.icon'] | |||
local div = mw.html.create('div') | |||
div:addClass('passive-icon-container') | |||
div:addClass(cls) | |||
div:tag('div') | |||
:addClass('passive-icon-frame') | |||
:done() | |||
div:wikitext( | |||
string.format( | |||
'[[%s|link=%s]]', | |||
passive['passive_skills.icon'], | |||
main_page | |||
) | |||
) | |||
return tostring(div) | |||
end | |||
function h.make_stat_order(results) | |||
local stats = {} | |||
local stat_order = {} | |||
for _, row in ipairs(results) do | |||
local stat = row['passive_skills.stat_text'] | |||
-- Can't show results here that don't have a stat line | |||
if stat then | |||
if stats[stat] == nil then | |||
stats[stat] = {row} | |||
table.insert(stat_order, stat) | |||
else | |||
table.insert(stats[stat], row) | |||
end | |||
end | |||
end | |||
return stats, stat_order | |||
end | |||
function h.stat_page_links(stat_order, stats) | |||
local out = {} | |||
for i, key in ipairs(stat_order) do | |||
local links = {} | |||
for j, row in ipairs(stats[key]) do | |||
links[#links+1] = string.format('[[%s|[%s]]]', row['passive_skills._pageName'], j) | |||
end | |||
out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' ')) | |||
end | |||
return table.concat(out, '<hr>') | |||
end | |||
keystone | h.type_order = {'basic', 'notable', 'keystone', 'ascendancy_basic', 'ascendancy_notable', 'atlas_basic', 'atlas_notable', 'atlas_keystone'} | ||
function h.get_type(passive) | |||
basic | local key | ||
if tonumber(passive['passive_skills.is_keystone']) == 1 then | |||
key = 'keystone' | |||
}, | elseif tonumber(passive['passive_skills.is_notable']) == 1 then | ||
key = 'notable' | |||
else | |||
key = 'basic' | |||
end | |||
if passive['passive_skills.ascendancy_class'] ~= nil then | |||
key = 'ascendancy_' .. key | |||
elseif tonumber(passive['passive_skills.is_atlas_passive']) == 1 then | |||
key = 'atlas_' .. key | |||
end | |||
return key | |||
end | |||
function h.sort_by_type(results) | |||
local new = {} | |||
for _, key in ipairs(h.type_order) do | |||
new[key] = {} | |||
end | |||
for _, passive in ipairs(results) do | |||
table.insert(new[h.get_type(passive)], passive) | |||
end | |||
return new | |||
end | |||
function h.intro_text(tpl_args) | |||
--[[ | |||
Display an introductory text about the passive skill. | |||
]] | |||
local out = {} | |||
if mw.ustring.find(tpl_args['id'], '_') then | |||
out[#out+1] = mw.getCurrentFrame():expandTemplate{ | |||
title='Incorrect title', | |||
args = {title=tpl_args['id']} | |||
} | |||
end | |||
if tpl_args['name'] then | |||
out[#out+1] = string.format( | |||
i18n.intro.text_with_name, | |||
tpl_args['id'], | |||
tostring(mw.title.getCurrentTitle()), | |||
tpl_args['name'] | |||
) | |||
else | |||
out[#out+1] = string.format( | |||
i18n.intro.text_without_name, | |||
tpl_args['id'] | |||
) | |||
end | |||
return table.concat(out) | |||
end | |||
stats = ' | function h.stat_box(tpl_args) | ||
--[[ | |||
Display the stat box. | |||
]] | |||
local use_value_range = false | |||
for _, stat_data in ipairs(tpl_args.stats) do | |||
if stat_data.min and stat_data.max then | |||
use_value_range = true | |||
break | |||
end | |||
end | |||
-- stat table | |||
local container = mw.html.create('div') | |||
container | |||
:addClass('modbox floatright') | |||
local tbl = container:tag('table') | |||
tbl | |||
:addClass('wikitable sortable') | |||
:tag('caption') | |||
:wikitext(i18n.data_table.stats) | |||
:done() | |||
local tbl_header = tbl:tag('tr') | |||
:tag('th') | |||
:wikitext(i18n.data_table.ordinal) | |||
:done() | |||
:tag('th') | |||
:wikitext(i18n.data_table.stat_id) | |||
:done() | |||
if use_value_range then | |||
tbl_header | |||
:tag('th') | |||
:wikitext(i18n.data_table.min) | |||
:done() | |||
:tag('th') | |||
:wikitext(i18n.data_table.max) | |||
:done() | |||
else | |||
tbl_header | |||
:tag('th') | |||
:wikitext(i18n.data_table.value) | |||
:done() | |||
end | |||
for i, stat_data in ipairs(tpl_args.stats) do | |||
local tbl_row = tbl:tag('tr') | |||
:tag('td') | |||
:wikitext(i) | |||
:done() | |||
:tag('td') | |||
:wikitext(stat_data.id) | |||
:done() | |||
if use_value_range then | |||
tbl_row | |||
:tag('td') | |||
:wikitext(stat_data.min) | |||
:done() | |||
:tag('td') | |||
:wikitext(stat_data.max) | |||
:done() | |||
else | |||
tbl_row | |||
:tag('td') | |||
:wikitext(stat_data.value) | |||
:done() | |||
end | |||
end | |||
return tostring(container) | |||
end | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- Cargo | -- Cargo tables | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
| Line 69: | Line 235: | ||
tables.passive_skills = { | tables.passive_skills = { | ||
table = 'passive_skills', | table = 'passive_skills', | ||
order = {'id', ' | order = { | ||
'id', 'name', 'is_in_game', 'frame', 'flavour_text', 'reminder_text', | |||
'icon', 'is_icon_only', 'is_jewel_socket', | |||
'is_keystone', 'is_notable', 'is_attribute', 'is_free', | |||
'is_multiple_choice_option', 'is_multiple_choice', | |||
'is_starting_node', 'ascendancy_class', 'is_atlas_passive', 'atlas_sub_tree', | |||
'buff_id', 'skill_points', 'weapon_set_points', 'stat_text', 'stat_text_raw', 'connections', | |||
}, | |||
fields = { | fields = { | ||
id = { | id = { | ||
field = 'id', | field = 'id', | ||
type = 'String', | type = 'String', | ||
required = true, | required = true, | ||
}, | }, | ||
| Line 85: | Line 253: | ||
type = 'String', | type = 'String', | ||
}, | }, | ||
is_in_game = { | |||
field = ' | field = 'is_in_game', | ||
type = ' | type = 'Boolean', | ||
default = true, | |||
}, | |||
frame = { | |||
field = 'frame', | |||
type = 'String', | |||
}, | }, | ||
flavour_text = { | flavour_text = { | ||
| Line 96: | Line 269: | ||
field = 'reminder_text', | field = 'reminder_text', | ||
type = 'Text', | type = 'Text', | ||
}, | }, | ||
icon = { | icon = { | ||
field = 'icon', | field = 'icon', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args | func = function(tpl_args, value) | ||
if value then | if value then | ||
return string.format(i18n.icon_name, value) | return string.format(i18n.images.icon_name, value) | ||
end | end | ||
end | end | ||
}, | }, | ||
is_icon_only = { | |||
field = ' | field = 'is_icon_only', | ||
type = ' | type = 'Boolean', | ||
default = false, | |||
}, | |||
is_jewel_socket = { | |||
field = 'is_jewel_socket', | |||
type = 'Boolean', | |||
default = false, | |||
}, | }, | ||
is_keystone = { | is_keystone = { | ||
| Line 127: | Line 296: | ||
is_notable = { | is_notable = { | ||
field = 'is_notable', | field = 'is_notable', | ||
type = 'Boolean', | |||
default = false, | |||
}, | |||
is_attribute = { | |||
field = 'is_attribute', | |||
type = 'Boolean', | |||
default = false, | |||
}, | |||
is_free = { | |||
field = 'is_free', | |||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
| Line 140: | Line 319: | ||
default = false, | default = false, | ||
}, | }, | ||
is_starting_node = { | |||
field = ' | field = 'is_starting_node', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
ascendancy_class = { | |||
field = ' | field = 'ascendancy_class', | ||
type = 'String', | |||
}, | |||
is_atlas_passive = { | |||
field = 'is_atlas_passive', | |||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
atlas_sub_tree = { | |||
field = ' | field = 'atlas_sub_tree', | ||
type = ' | type = 'String' | ||
default = | }, | ||
buff_id = { | |||
field = 'buff_id', | |||
type = 'String', | |||
}, | |||
-- TODO: Other buff stuff | |||
skill_points = { | |||
field = 'skill_points', | |||
type = 'Integer', | |||
default = 0, | |||
}, | |||
weapon_set_points = { | |||
field = 'weapon_set_points', | |||
type = 'Integer', | |||
default = 0, | |||
}, | }, | ||
stat_text = { | stat_text = { | ||
| Line 160: | Line 357: | ||
}, | }, | ||
stat_text_raw = { | stat_text_raw = { | ||
field = ' | field = 'stat_text_raw', | ||
type = 'Text', | type = 'Text', | ||
func = function (tpl_args, | func = function (tpl_args, value) | ||
if tpl_args.stat_text then | if tpl_args.stat_text then | ||
tpl_args.stat_text_raw = string.gsub( | tpl_args.stat_text_raw = string.gsub( | ||
| Line 176: | Line 373: | ||
end | end | ||
}, | }, | ||
connections = { | connections = { | ||
field = 'connections', | field = 'connections', | ||
| Line 197: | Line 393: | ||
} | } | ||
} | } | ||
-- ---------------------------------------------------------------------------- | |||
-- Display mapping | |||
-- ---------------------------------------------------------------------------- | |||
local display = {} | local display = {} | ||
display.map_to_property = {'icon', 'is_keystone', 'is_notable', 'ascendancy_class'} | display.map_to_property = {'icon', 'is_keystone', 'is_notable', 'ascendancy_class', 'is_atlas_passive'} | ||
display. | display.table_map = { | ||
{ | { | ||
key = 'id', | key = 'id', | ||
header = i18n. | header = i18n.data_table.id, | ||
display = nil, | display = nil, | ||
}, | }, | ||
{ | { | ||
class = 'tc -flavour', | |||
key = 'flavour_text', | key = 'flavour_text', | ||
header = i18n. | header = i18n.data_table.flavour_text, | ||
display = nil, | display = nil, | ||
}, | }, | ||
{ | { | ||
key = 'reminder_text', | key = 'reminder_text', | ||
header = i18n. | header = i18n.data_table.reminder_text, | ||
display = nil, | display = nil, | ||
}, | }, | ||
{ | { | ||
key = 'skill_points', | key = 'skill_points', | ||
header = i18n. | header = i18n.data_table.skill_points, | ||
display = nil, | display = nil, | ||
}, | }, | ||
{ | { | ||
key = 'ascendancy_class', | key = 'ascendancy_class', | ||
header = i18n. | header = i18n.data_table.ascendancy_class, | ||
display = function (tpl_args | display = function (tpl_args, value) | ||
return string.format('[[%s]]', value) | return string.format('[[%s]]', value) | ||
end, | end, | ||
| Line 236: | Line 431: | ||
{ | { | ||
key = 'connections', | key = 'connections', | ||
header = i18n. | header = i18n.data_table.connections, | ||
display = function (tpl_args | display = function (tpl_args, value) | ||
local results = m_cargo.map_results_to_id{ | local results = m_cargo.map_results_to_id{ | ||
field='passive_skills.id', | field='passive_skills.id', | ||
| Line 273: | Line 468: | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Main functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local function _passive_skill(tpl_args) | ||
function | |||
--[[ | --[[ | ||
Stores data and displays a infobox about the passive skill. | Stores data and displays a infobox about the passive skill. | ||
| Line 478: | Line 479: | ||
= p.passive_skill{ | = p.passive_skill{ | ||
id = 'life_life_leech1629', | id = 'life_life_leech1629', | ||
name = 'Blood Drinker', | name = 'Blood Drinker', | ||
is_notable = 'True', | is_notable = 'True', | ||
| Line 492: | Line 492: | ||
]] | ]] | ||
-- | -- | ||
-- Validate and store | |||
-- | |||
local store = true | |||
-- | |||
-- Validate and store passive skill data | |||
tpl_args=tpl_args, | m_cargo.store_mapped_args{ | ||
tpl_args = tpl_args, | |||
table_map = tables.passive_skills, | |||
rtr = not store, | |||
} | } | ||
-- | -- Validate and store stats | ||
m_util.args.stats(tpl_args | m_util.args.stats(tpl_args) | ||
for _, | for _, stat_data in ipairs(tpl_args.stats) do | ||
if store then | |||
m_cargo.store({ | |||
_table = tables.passive_skill_stats.table, | |||
id = stat_data.id, | |||
value = stat_data.value, | |||
min = stat_data.min, | |||
max = stat_data.max, | |||
}) | |||
end | |||
end | |||
-- Attach to tables | |||
if store then | |||
mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.passive_skills} | |||
mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.passive_skill_stats} | |||
end | end | ||
| Line 534: | Line 548: | ||
local tbl = mw.html.create('table') | local tbl = mw.html.create('table') | ||
for _, data in ipairs(display. | for _, data in ipairs(display.table_map) do | ||
local value = tpl_args[data.key] | local value = tpl_args[data.key] | ||
-- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed | -- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed | ||
| Line 540: | Line 554: | ||
local dsp | local dsp | ||
if data.display then | if data.display then | ||
dsp = data.display(tpl_args | dsp = data.display(tpl_args, value) | ||
else | else | ||
dsp = value | dsp = value | ||
| Line 550: | Line 564: | ||
:done() | :done() | ||
:tag('td') | :tag('td') | ||
: | :addClass(data.class) | ||
:wikitext(dsp) | :wikitext(dsp) | ||
:done() | :done() | ||
| Line 559: | Line 573: | ||
infocard_args[1] = tostring(tbl) | infocard_args[1] = tostring(tbl) | ||
infocard_args[2] = tpl_args.stat_text | infocard_args[2] = tpl_args.stat_text | ||
infocard_args[3] = h.format_passive_icon(passive, type_key) | if not tpl_args.stat_text then | ||
infocard_args[2] = h.format_passive_icon(passive, type_key) | |||
else | |||
infocard_args[3] = h.format_passive_icon(passive, type_key) | |||
end | |||
local out = { | local out = { | ||
h.infocard(infocard_args), | |||
h.intro_text(tpl_args | h.intro_text(tpl_args), | ||
h.stat_box(tpl_args | h.stat_box(tpl_args), | ||
} | } | ||
local cats = { | local cats = { | ||
i18n. | i18n.categories.data, | ||
} | } | ||
return table.concat(out) .. m_util.misc.add_category(cats) | |||
if tpl_args.debug then | |||
mw.log('Start logging tpl_args.') | |||
mw.logObject(tpl_args) | |||
mw.log('Stop logging tpl_args.') | |||
end | |||
return table.concat(out) .. m_util.misc.add_category(cats, {ignore_blacklist=tpl_args.debug}) | |||
end | end | ||
function | local function _passive_skill_box(tpl_args) | ||
--[[ | --[[ | ||
Queries a passive skill and displays it. | Queries a passive skill and displays it. | ||
| Line 582: | Line 605: | ||
]] | ]] | ||
tpl_args.name = tpl_args.name or tpl_args[1] | tpl_args.name = tpl_args.name or tpl_args[1] | ||
if not tpl_args.q_where and tpl_args.name then | if not tpl_args.q_where and tpl_args.name then | ||
tpl_args.q_where = string.format('passive_skills.name="%s" | tpl_args.q_where = string.format('passive_skills.name="%s"', tpl_args.name) | ||
elseif not (tpl_args.q_where and not tpl_args.name) then | elseif not (tpl_args.q_where and not tpl_args.name) then | ||
error('q_where or name must be specified') | error('q_where or name must be specified') | ||
| Line 600: | Line 617: | ||
{'passive_skills'}, | {'passive_skills'}, | ||
{ | { | ||
'passive_skills._pageName | 'passive_skills._pageName', | ||
'passive_skills.name', | |||
'passive_skills.name', | |||
'passive_skills.stat_text', | 'passive_skills.stat_text', | ||
-- TODO: only really need these once, maybe put in extra query | -- TODO: only really need these once, maybe put in extra query | ||
| Line 610: | Line 626: | ||
'passive_skills.is_notable', | 'passive_skills.is_notable', | ||
'passive_skills.ascendancy_class', | 'passive_skills.ascendancy_class', | ||
'passive_skills.id' | 'passive_skills.is_atlas_passive', | ||
'passive_skills.id', | |||
}, | }, | ||
{ | { | ||
| Line 628: | Line 645: | ||
local type_results = results[type_key] | local type_results = results[type_key] | ||
if #type_results > 0 then | if #type_results > 0 then | ||
cats[#cats+1] = i18n. | cats[#cats+1] = i18n.categories[type_key] | ||
local stats, stat_order = h.make_stat_order(type_results) | local stats, stat_order = h.make_stat_order(type_results) | ||
| Line 641: | Line 658: | ||
infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text']) | infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text']) | ||
out[#out+1] = | out[#out+1] = h.infocard(infocard_args) | ||
-- Store as main page: | -- Store as main page: | ||
for _, v in ipairs(type_results) do | for _, v in ipairs(type_results) do | ||
m_cargo.store( | m_cargo.store({ | ||
_table = 'main_pages', | |||
data_page = v['passive_skills._pageName'], | |||
id = v['passive_skills.id'], | |||
name = v['passive_skills.name'], | |||
} | }) | ||
) | mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.main_pages} | ||
end | end | ||
end | end | ||
| Line 663: | Line 680: | ||
end | end | ||
-- ---------------------------------------------------------------------------- | |||
-- Exported functions | |||
-- ---------------------------------------------------------------------------- | |||
local p = {} | |||
p.table_passive_skills = m_cargo.declare_factory{data=tables.passive_skills} | |||
p.table_passive_skill_stats = m_cargo.declare_factory{data=tables.passive_skill_stats} | |||
-- | |||
-- [[Template:Passive skill]] | |||
-- | |||
p.passive_skill = m_util.misc.invoker_factory(_passive_skill, { | |||
wrappers = cfg.wrappers.passive_skill, | |||
}) | |||
-- ---- | -- | ||
-- [[Template:Passive skill box]] | |||
-- | |||
p.passive_skill_box = m_util.misc.invoker_factory(_passive_skill_box, { | |||
wrappers = cfg.wrappers.passive_skill_box, | |||
}) | |||
return p | return p | ||
Latest revision as of 23:36, 2 March 2026
This module is used on 7400+ pages.
To avoid major disruption and server load, do not make unnecessary edits to this module. Test changes to this module first using its /sandbox and /testcases subpages or your user space. All of the changes can then be applied to this module in a single edit.
Consider discussing changes on the talk page or on Discord before implementing them.
Implements {{passive skill}} and {{passive skill box}}.
The above documentation is transcluded from Module:Passive skill/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Passive skill
--
-- This module implements [[Template:Passive skill]] and
-- [[Template:Passive skill box]]
-------------------------------------------------------------------------------
require('strict')
local m_util = require('Module:Util')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Passive skill')
local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')
-- Lazy loading
local f_infocard -- require('Module:Infocard').main
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Passive skill/config/sandbox') or mw.loadData('Module:Passive skill/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
-- Lazy loading for Module:Infocard
function h.infocard(args)
if not f_infocard then
f_infocard = use_sandbox and require('Module:Infocard/sandbox').main or require('Module:Infocard').main
end
return f_infocard(args)
end
function h.format_passive_icon(passive, passive_type)
if passive['passive_skills.icon'] == nil then
return ''
end
local cls = string.format('passive-icon-type__%s', passive_type)
local main_page = passive['main_pages._pageName'] or passive['passive_skills.name'] or passive['passive_skills.icon']
local div = mw.html.create('div')
div:addClass('passive-icon-container')
div:addClass(cls)
div:tag('div')
:addClass('passive-icon-frame')
:done()
div:wikitext(
string.format(
'[[%s|link=%s]]',
passive['passive_skills.icon'],
main_page
)
)
return tostring(div)
end
function h.make_stat_order(results)
local stats = {}
local stat_order = {}
for _, row in ipairs(results) do
local stat = row['passive_skills.stat_text']
-- Can't show results here that don't have a stat line
if stat then
if stats[stat] == nil then
stats[stat] = {row}
table.insert(stat_order, stat)
else
table.insert(stats[stat], row)
end
end
end
return stats, stat_order
end
function h.stat_page_links(stat_order, stats)
local out = {}
for i, key in ipairs(stat_order) do
local links = {}
for j, row in ipairs(stats[key]) do
links[#links+1] = string.format('[[%s|[%s]]]', row['passive_skills._pageName'], j)
end
out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' '))
end
return table.concat(out, '<hr>')
end
h.type_order = {'basic', 'notable', 'keystone', 'ascendancy_basic', 'ascendancy_notable', 'atlas_basic', 'atlas_notable', 'atlas_keystone'}
function h.get_type(passive)
local key
if tonumber(passive['passive_skills.is_keystone']) == 1 then
key = 'keystone'
elseif tonumber(passive['passive_skills.is_notable']) == 1 then
key = 'notable'
else
key = 'basic'
end
if passive['passive_skills.ascendancy_class'] ~= nil then
key = 'ascendancy_' .. key
elseif tonumber(passive['passive_skills.is_atlas_passive']) == 1 then
key = 'atlas_' .. key
end
return key
end
function h.sort_by_type(results)
local new = {}
for _, key in ipairs(h.type_order) do
new[key] = {}
end
for _, passive in ipairs(results) do
table.insert(new[h.get_type(passive)], passive)
end
return new
end
function h.intro_text(tpl_args)
--[[
Display an introductory text about the passive skill.
]]
local out = {}
if mw.ustring.find(tpl_args['id'], '_') then
out[#out+1] = mw.getCurrentFrame():expandTemplate{
title='Incorrect title',
args = {title=tpl_args['id']}
}
end
if tpl_args['name'] then
out[#out+1] = string.format(
i18n.intro.text_with_name,
tpl_args['id'],
tostring(mw.title.getCurrentTitle()),
tpl_args['name']
)
else
out[#out+1] = string.format(
i18n.intro.text_without_name,
tpl_args['id']
)
end
return table.concat(out)
end
function h.stat_box(tpl_args)
--[[
Display the stat box.
]]
local use_value_range = false
for _, stat_data in ipairs(tpl_args.stats) do
if stat_data.min and stat_data.max then
use_value_range = true
break
end
end
-- stat table
local container = mw.html.create('div')
container
:addClass('modbox floatright')
local tbl = container:tag('table')
tbl
:addClass('wikitable sortable')
:tag('caption')
:wikitext(i18n.data_table.stats)
:done()
local tbl_header = tbl:tag('tr')
:tag('th')
:wikitext(i18n.data_table.ordinal)
:done()
:tag('th')
:wikitext(i18n.data_table.stat_id)
:done()
if use_value_range then
tbl_header
:tag('th')
:wikitext(i18n.data_table.min)
:done()
:tag('th')
:wikitext(i18n.data_table.max)
:done()
else
tbl_header
:tag('th')
:wikitext(i18n.data_table.value)
:done()
end
for i, stat_data in ipairs(tpl_args.stats) do
local tbl_row = tbl:tag('tr')
:tag('td')
:wikitext(i)
:done()
:tag('td')
:wikitext(stat_data.id)
:done()
if use_value_range then
tbl_row
:tag('td')
:wikitext(stat_data.min)
:done()
:tag('td')
:wikitext(stat_data.max)
:done()
else
tbl_row
:tag('td')
:wikitext(stat_data.value)
:done()
end
end
return tostring(container)
end
-- ----------------------------------------------------------------------------
-- Cargo tables
-- ----------------------------------------------------------------------------
local tables = {}
tables.passive_skills = {
table = 'passive_skills',
order = {
'id', 'name', 'is_in_game', 'frame', 'flavour_text', 'reminder_text',
'icon', 'is_icon_only', 'is_jewel_socket',
'is_keystone', 'is_notable', 'is_attribute', 'is_free',
'is_multiple_choice_option', 'is_multiple_choice',
'is_starting_node', 'ascendancy_class', 'is_atlas_passive', 'atlas_sub_tree',
'buff_id', 'skill_points', 'weapon_set_points', 'stat_text', 'stat_text_raw', 'connections',
},
fields = {
id = {
field = 'id',
type = 'String',
required = true,
},
name = {
field = 'name',
type = 'String',
},
is_in_game = {
field = 'is_in_game',
type = 'Boolean',
default = true,
},
frame = {
field = 'frame',
type = 'String',
},
flavour_text = {
field = 'flavour_text',
type = 'Text',
},
reminder_text = {
field = 'reminder_text',
type = 'Text',
},
icon = {
field = 'icon',
type = 'Page',
func = function(tpl_args, value)
if value then
return string.format(i18n.images.icon_name, value)
end
end
},
is_icon_only = {
field = 'is_icon_only',
type = 'Boolean',
default = false,
},
is_jewel_socket = {
field = 'is_jewel_socket',
type = 'Boolean',
default = false,
},
is_keystone = {
field = 'is_keystone',
type = 'Boolean',
default = false,
},
is_notable = {
field = 'is_notable',
type = 'Boolean',
default = false,
},
is_attribute = {
field = 'is_attribute',
type = 'Boolean',
default = false,
},
is_free = {
field = 'is_free',
type = 'Boolean',
default = false,
},
is_multiple_choice_option = {
field = 'is_multiple_choice_option',
type = 'Boolean',
default = false,
},
is_multiple_choice = {
field = 'is_multiple_choice',
type = 'Boolean',
default = false,
},
is_starting_node = {
field = 'is_starting_node',
type = 'Boolean',
default = false,
},
ascendancy_class = {
field = 'ascendancy_class',
type = 'String',
},
is_atlas_passive = {
field = 'is_atlas_passive',
type = 'Boolean',
default = false,
},
atlas_sub_tree = {
field = 'atlas_sub_tree',
type = 'String'
},
buff_id = {
field = 'buff_id',
type = 'String',
},
-- TODO: Other buff stuff
skill_points = {
field = 'skill_points',
type = 'Integer',
default = 0,
},
weapon_set_points = {
field = 'weapon_set_points',
type = 'Integer',
default = 0,
},
stat_text = {
field = 'stat_text',
type = 'Text',
},
stat_text_raw = {
field = 'stat_text_raw',
type = 'Text',
func = function (tpl_args, value)
if tpl_args.stat_text then
tpl_args.stat_text_raw = string.gsub(
-- [[x]] -> x
string.gsub(
tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1'
),
-- [[x|y]] -> y
'%[%[[^|]+|([^%]|]+)%]%]', '%1'
)
end
return tpl_args.stat_text_raw
end
},
connections = {
field = 'connections',
type = 'List (,) of String',
},
}
}
tables.passive_skill_stats = {
table = 'passive_skill_stats',
fields = {
id = {
field = 'id',
type = 'String',
},
value = {
field = 'value',
type = 'Integer',
},
}
}
-- ----------------------------------------------------------------------------
-- Display mapping
-- ----------------------------------------------------------------------------
local display = {}
display.map_to_property = {'icon', 'is_keystone', 'is_notable', 'ascendancy_class', 'is_atlas_passive'}
display.table_map = {
{
key = 'id',
header = i18n.data_table.id,
display = nil,
},
{
class = 'tc -flavour',
key = 'flavour_text',
header = i18n.data_table.flavour_text,
display = nil,
},
{
key = 'reminder_text',
header = i18n.data_table.reminder_text,
display = nil,
},
{
key = 'skill_points',
header = i18n.data_table.skill_points,
display = nil,
},
{
key = 'ascendancy_class',
header = i18n.data_table.ascendancy_class,
display = function (tpl_args, value)
return string.format('[[%s]]', value)
end,
},
{
key = 'connections',
header = i18n.data_table.connections,
display = function (tpl_args, value)
local results = m_cargo.map_results_to_id{
field='passive_skills.id',
results=m_cargo.array_query{
tables={'passive_skills'},
fields={'passive_skills.name', 'passive_skills._pageName'},
id_array=value,
id_field='passive_skills.id',
ignore_missing=true,
}
}
local ul = mw.html.create('ul')
for _, key in ipairs(value) do
local row = results[key]
if row then
row = row[1]
end
local text
if row then
text = string.format('[[%s|%s]]', row['passive_skills._pageName'], row['passive_skills.name'] or row['passive_skills._pageName'])
else
text = key
end
ul
:tag('li')
:wikitext(text)
:done()
end
return tostring(ul)
end,
},
}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _passive_skill(tpl_args)
--[[
Stores data and displays a infobox about the passive skill.
Examples
--------
= p.passive_skill{
id = 'life_life_leech1629',
name = 'Blood Drinker',
is_notable = 'True',
icon = 'lifeleech',
stat1_id = 'maximum_life_+%',
stat1_value = '8',
stat2_id = 'base_life_leech_from_attack_damage_permyriad',
stat2_value = '40',
stat_text = '8% increased maximum life<br>0.4% of Attack Damage Leeched as Life',
connections = 'life1415,life1413',
}
]]
--
-- Validate and store
--
local store = true
-- Validate and store passive skill data
m_cargo.store_mapped_args{
tpl_args = tpl_args,
table_map = tables.passive_skills,
rtr = not store,
}
-- Validate and store stats
m_util.args.stats(tpl_args)
for _, stat_data in ipairs(tpl_args.stats) do
if store then
m_cargo.store({
_table = tables.passive_skill_stats.table,
id = stat_data.id,
value = stat_data.value,
min = stat_data.min,
max = stat_data.max,
})
end
end
-- Attach to tables
if store then
mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.passive_skills}
mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.passive_skill_stats}
end
--
-- Infobox
--
local passive = {}
for _, key in ipairs(display.map_to_property) do
local v = tpl_args[key]
if type(v) == 'boolean' then
if v then
v = 1
else
v = 0
end
end
passive[string.format('%s.%s', tables.passive_skills.table, tables.passive_skills.fields[key].field)] = v
end
local type_key = h.get_type(passive)
local infocard_args = {}
infocard_args.header = tpl_args.name
infocard_args.subheader = i18n.passive_box[type_key]
local tbl = mw.html.create('table')
for _, data in ipairs(display.table_map) do
local value = tpl_args[data.key]
-- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed
if value ~= tables.passive_skills.fields[data.key].default then
local dsp
if data.display then
dsp = data.display(tpl_args, value)
else
dsp = value
end
tbl
:tag('tr')
:tag('th')
:wikitext(data.header)
:done()
:tag('td')
:addClass(data.class)
:wikitext(dsp)
:done()
:done()
end
end
infocard_args[1] = tostring(tbl)
infocard_args[2] = tpl_args.stat_text
if not tpl_args.stat_text then
infocard_args[2] = h.format_passive_icon(passive, type_key)
else
infocard_args[3] = h.format_passive_icon(passive, type_key)
end
local out = {
h.infocard(infocard_args),
h.intro_text(tpl_args),
h.stat_box(tpl_args),
}
local cats = {
i18n.categories.data,
}
if tpl_args.debug then
mw.log('Start logging tpl_args.')
mw.logObject(tpl_args)
mw.log('Stop logging tpl_args.')
end
return table.concat(out) .. m_util.misc.add_category(cats, {ignore_blacklist=tpl_args.debug})
end
local function _passive_skill_box(tpl_args)
--[[
Queries a passive skill and displays it.
Examples
--------
= p.passive_skill_box{name='Ghost Reaver'}
]]
tpl_args.name = tpl_args.name or tpl_args[1]
if not tpl_args.q_where and tpl_args.name then
tpl_args.q_where = string.format('passive_skills.name="%s"', tpl_args.name)
elseif not (tpl_args.q_where and not tpl_args.name) then
error('q_where or name must be specified')
end
local results = m_cargo.query(
{'passive_skills'},
{
'passive_skills._pageName',
'passive_skills.name',
'passive_skills.stat_text',
-- TODO: only really need these once, maybe put in extra query
'passive_skills.flavour_text',
'passive_skills.icon',
'passive_skills.is_keystone',
'passive_skills.is_notable',
'passive_skills.ascendancy_class',
'passive_skills.is_atlas_passive',
'passive_skills.id',
},
{
where=tpl_args.q_where,
orderBy='passive_skills.stat_text',
limit=5000,
}
)
if #results == 0 then
error(i18n.errors.no_passives_found)
end
results = h.sort_by_type(results)
local out = {}
local cats = {}
for _, type_key in ipairs(h.type_order) do
local type_results = results[type_key]
if #type_results > 0 then
cats[#cats+1] = i18n.categories[type_key]
local stats, stat_order = h.make_stat_order(type_results)
local passive = type_results[1]
local infocard_args = {}
infocard_args.header = passive['passive_skills.name']
infocard_args.subheader = i18n.passive_box[type_key]
infocard_args[1] = h.format_passive_icon(passive, type_key)
infocard_args[2] = h.stat_page_links(stat_order, stats)
infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text'])
out[#out+1] = h.infocard(infocard_args)
-- Store as main page:
for _, v in ipairs(type_results) do
m_cargo.store({
_table = 'main_pages',
data_page = v['passive_skills._pageName'],
id = v['passive_skills.id'],
name = v['passive_skills.name'],
})
mw.getCurrentFrame():expandTemplate{title = cfg.attach_templates.main_pages}
end
end
end
if tpl_args.cats == nil or m_util.cast.boolean(tpl_args.cats) then
out[#out+1] = m_util.misc.add_category(cats)
end
return table.concat(out)
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
p.table_passive_skills = m_cargo.declare_factory{data=tables.passive_skills}
p.table_passive_skill_stats = m_cargo.declare_factory{data=tables.passive_skill_stats}
--
-- [[Template:Passive skill]]
--
p.passive_skill = m_util.misc.invoker_factory(_passive_skill, {
wrappers = cfg.wrappers.passive_skill,
})
--
-- [[Template:Passive skill box]]
--
p.passive_skill_box = m_util.misc.invoker_factory(_passive_skill_box, {
wrappers = cfg.wrappers.passive_skill_box,
})
return p