Module:Mod
From Path of Exile Wiki
This Lua module is used on a very large number of pages. To avoid large-scale disruption and unnecessary server load, any changes to this module should first be tested in its /sandbox or /testcases subpages. The tested changes can then be added to this page in one single edit. Please consider discussing any changes on the talk page before implementing them. |
Module for handling for Mods with Semantic MediaWiki support.
List of currently implemented templates
-- -- Module for mod related templates -- local m_util = require('Module:Util') local getArgs = require('Module:Arguments').getArgs local game = require('Module:Game') local f_item_link = require('Module:Item link').item_link local cargo = mw.ext.cargo local p = {} -- ---------------------------------------------------------------------------- -- Strings -- ---------------------------------------------------------------------------- -- This section contains strings used by this module. -- Add new strings here instead of in-code directly, this will help other -- people to correct spelling mistakes easier and help with translation to -- other PoE wikis. local i18n = { args = { -- -- Mod template -- -- main id = 'id', name = 'name', mod_group = 'mod_group', mod_type = 'mod_type', domain = 'domain', generation_type = 'generation_type', required_level = 'required_level', stat_text = 'stat_text', granted_buff_id = 'granted_buff_id', granted_buff_value = 'granted_buff_value', granted_skill = 'granted_skill', tags = 'tags', tier_text = 'tier_text', -- sell price sell_price_prefix = 'sell_price', item_name = 'name', amount = 'amount', }, errors = { -- -- Mod template -- sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.', sell_price_missing_argument = 'Both %s and %s must be specified', }, } -- ---------------------------------------------------------------------------- -- m_utility / Helper functions -- ---------------------------------------------------------------------------- local h = {} -- Validate single value properties and set them h.validate = {} function h.validate.not_nil (args) return function (arg) if g_args[arg] == nil then error(string.format('%s must not be nil', arg)) end end end function h.validate.number (args) return function (tpl_args, frame, value) return m_util.cast.number(value, args) end end function h.create_header(row) local stat = mw.html.create('span') local text, nsub = mw.ustring.gsub(row['Has stat text'], '%d+', '?') stat :attr('class', 'mod-table-header-stat') :wikitext(text) :done() local mgroup = mw.html.create('span') mgroup :attr('class', 'mod-table-header-modgroup') :wikitext(row['Has mod group']) :done() local tbl = mw.html.create('table') tbl :attr('class', 'wikitable mw-collapsible mw-collapsed mod-table') :tag('tr') :tag('th') :attr('class', 'mod-table-header') :attr('colspan', g_args.colspan) :tag('span') :attr('class', 'mod-table-header-container') :wikitext(tostring(stat) .. tostring(mgroup)) :done() :done() return tbl end function h.format_mod(tbl, row, tags) local tr = tbl:tag('tr') tr :tag('td') :wikitext(string.format('[[%s|%s]]', row[1], row['Has name'])) :attr('class', 'mod-table-cell-name') :done() :tag('td') :wikitext(row['Has level requirement']) :attr('class', 'mod-table-cell-level') :done() :tag('td') :wikitext(row['Has stat text']) :attr('class', 'mod-table-cell-stat') :done() :tag('td') :wikitext(table.concat(tags, ', ')) :attr('class', 'mod-table-cell-tags') :done() end -- ---------------------------------------------------------------------------- -- Templates -- ---------------------------------------------------------------------------- -- -- Template: Mod -- local mod_map = { main = { table = 'mods', order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'}, parse_order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'}, fields = { id = { name = i18n.args.id, field = i18n.args.id, type = 'String', wikitext = 'Mod Id', }, name = { name = i18n.args.name, field = i18n.args.name, type = 'String', wikitext = 'Name', }, mod_group = { name = i18n.args.mod_group, field = i18n.args.mod_group, type = 'String', wikitext = 'Group', }, mod_type = { name = i18n.args.mod_type, field = i18n.args.mod_type, type = 'String', wikitext = 'Mod type', }, domain = { name = i18n.args.domain, field = i18n.args.domain, type = 'Integer', func = h.validate.number{min=1, max=15}, wikitext = 'Mod domain', display = function (value) return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')' end, }, generation_type = { name = i18n.args.generation_type, field = i18n.args.generation_type, type = 'Integer', func = h.validate.number{min=1, max=13}, wikitext = 'Generation type', display = function (value) return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')' end, }, required_level = { name = i18n.args.required_level, field = i18n.args.required_level, type = 'Integer', func = h.validate.number{min=0, max=100}, wikitext = 'Req. level', }, stat_text = { name = i18n.args.stat_text, field = i18n.args.stat_text, type = 'Text', wikitext = 'Effect', }, stat_text_raw = { name = nil, field = 'stat_text_raw', type = 'Text', func = function(tpl_args, frame) 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 }, granted_buff_id = { name = i18n.args.granted_buff_id, field = i18n.args.granted_buff_id, type = 'String', wikitext = 'Granted Buff Id', }, granted_buff_value = { name = i18n.args.granted_buff_value, field = i18n.args.granted_buff_value, type = 'Integer', wikitext = 'Granted Buff Value', }, granted_skill = { name = i18n.args.granted_skill, field = i18n.args.granted_skill, type = 'String', wikitext = 'Granted Skill', }, tags = { name = i18n.args.tags, field = i18n.args.tags, type = 'List (,) of String', wikitext = 'Tags', func = function (tpl_args, frame, value) if value == nil then return {} else return m_util.string.split(value, ', ') end end, func_cargo = function(tpl_args, frame) return table.concat(tpl_args.tags, ',') end, display = function(value) return table.concat(value, ', ') end, }, tier_text = { name = i18n.args.tier_text, field = 'tier_text', type = 'Text', wikitext = 'Tier Text', }, }, }, mod_sell_prices = { table = 'mod_sell_prices', order = {'name', 'amount'}, fields = { name = { name = i18n.args.item_name, field = i18n.args.item_name, type = 'String', func = function (value) return value end, }, amount = { name = i18n.args.amount, field = i18n.args.amount, type = 'Integer', func = tonumber, }, }, }, } p.table_main = m_util.cargo.declare_factory{data=mod_map.main} p.table_mod_sell_prices = m_util.cargo.declare_factory{data=mod_map.mod_sell_prices} function p.table_mod_stats(frame) m_util.cargo.declare(frame, { _table = 'mod_stats', id = 'String', min = 'Integer', max = 'Integer', }) end -- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_group = "LocalPhysicalDamagePercent", domain = "1", generation_type = "3", required_level = "1", mod_type = "LocalPhysicalDamagePercent", stat_text = "150% increased Physical Damage", stat1_id = "local_physical_damage_+%", stat1_min = "150", stat1_max = "150"} function p.mod(frame) -- Get args tpl_args = getArgs(frame, { parentFirst = true }) frame = m_util.misc.get_frame(frame) -- -- Validation & semantic properties -- -- Validate single value properties and set them local cargo_data = { _table = mod_map.main.table, } for _, key in pairs(mod_map.main.parse_order) do data = mod_map.main.fields[key] local value if data.func ~= nil then if data.name then value = data.func(tpl_args, frame, tpl_args[data.name]) else value = data.func(tpl_args, frame) end else value = tpl_args[data.name] end tpl_args[key] = value if data.field ~= nil then if data.func_cargo then cargo_data[data.field] = data.func_cargo(tpl_args, frame) else cargo_data[data.field] = value end end end m_util.cargo.store(frame, cargo_data) -- Validate % set the stat subobjects m_util.args.stats(tpl_args, {frame=frame}) for _, stat_data in pairs(tpl_args.stats) do m_util.cargo.store(frame, { _table = 'mod_stats', id = stat_data.id, min = stat_data.min, max = stat_data.max, }) end -- Validate & set spawn weight subobjects m_util.args.spawn_weight_list(tpl_args, { frame=frame, }) -- Validate & set generation weight subobjects m_util.args.generation_weight_list(tpl_args, { frame=frame, }) -- Validate & set mod sell values i = 0 local names = {} local sell_prices = {} repeat i = i + 1 local id = {} value = {} for key, data in pairs(mod_map.mod_sell_prices.fields) do id[key] = string.format('%s%s_%s', i18n.args.sell_price_prefix, i, data.name) value[key] = data.func(tpl_args[id[key]]) end if value.name == nil and value.amount == nil then value = nil elseif value.name ~= nil and value.amount ~= nil then if names[value.name] then error(i18n.errors.sell_price_duplicate_name) else names[value.name] = true end local cargo_data = { _table = mod_map.mod_sell_prices.table, } for key, data in pairs(mod_map.mod_sell_prices.fields) do cargo_data[data.field] = value[key] end m_util.cargo.store(frame, cargo_data) sell_prices[#sell_prices+1] = value else error (string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount)) end until value == nil -- -- Display -- local container = mw.html.create('div') container :attr('class', 'modbox') -- core stats local tbl = container:tag('table') tbl :attr('class', 'wikitable') for _, key in ipairs(mod_map.main.order) do local data = mod_map.main.fields[key] local text if data.display == nil then text = tpl_args[key] else text = data.display(tpl_args[key]) end tbl :tag('tr') :tag('th') :wikitext(data.wikitext) :done() :tag('td') :wikitext(text) :done() :done() :done() end tbl :tag('tr') :tag('th') :wikitext('Tags') :done() :tag('td') :wikitext(table.concat(tpl_args['tags'], ', ')) :done() :done() :done() -- stat table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 4) :wikitext('Stats') :done() :done() :tag('tr') :tag('th') :wikitext('#') :done() :tag('th') :wikitext('Stat Id') :done() :tag('th') :wikitext('Min') :done() :tag('th') :wikitext('Max') :done() :done() :done() for i=1, #tpl_args.stats do local value = { id = tpl_args['stat' .. i .. '_id'], min = tpl_args['stat' .. i .. '_min'], max = tpl_args['stat' .. i .. '_max'], } if value.id then tbl :tag('tr') :tag('td') :wikitext(i) :done() :tag('td') :wikitext(value.id) :done() :tag('td') :wikitext(value.min) :done() :tag('td') :wikitext(value.max) :done() :done() :done() end end -- spawn weight table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 3) :wikitext('Spawn Weights') :done() :done() :tag('tr') :tag('th') :wikitext('#') :done() :tag('th') :wikitext('Tag') :done() :tag('th') :wikitext('Weight') :done() :done() :done() i = 0 value = nil repeat i = i + 1 value = { tag = tpl_args[string.format('spawn_weight%s_tag', i)], value = tpl_args[string.format('spawn_weight%s_value', i)], } if value.tag then tbl :tag('tr') :tag('td') :wikitext(i) :done() :tag('td') :wikitext(value.tag) :done() :tag('td') :wikitext(value.value) :done() :done() :done() end until value.tag == nil -- generation weight table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 3) :wikitext('Generation Weights') :done() :done() :tag('tr') :tag('th') :wikitext('#') :done() :tag('th') :wikitext('Tag') :done() :tag('th') :wikitext('Weight') :done() :done() :done() i = 0 value = nil repeat i = i + 1 value = { tag = tpl_args[string.format('generation_weight%s_tag', i)], value = tpl_args[string.format('generation_weight%s_value', i)], } if value.tag then tbl :tag('tr') :tag('td') :wikitext(i) :done() :tag('td') :wikitext(value.tag) :done() :tag('td') :wikitext(value.value) :done() :done() :done() end until value.tag == nil -- Sell prices tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 2) :wikitext('Modifier sell price') :done() :done() :tag('tr') :tag('th') :wikitext('#') :done() :tag('th') :wikitext('Item') :done() :done() :done() for i, value in ipairs(sell_prices) do tbl :tag('tr') :tag('td') :wikitext(value.amount) :done() :tag('td') :wikitext(string.format('[[%s]]', value.name)) :done() :done() end -- Generic messages on the page out = {} if mw.ustring.find(tpl_args['id'], '_') then out[#out+1] = frame:expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n' end if tpl_args['name'] then out[#out+1] = string.format("'''%s''' is the internal id of [[modifier]] '''%s'''.\n", tpl_args['id'], tpl_args['name']) else out[#out+1] = string.format("'''%s''' is the internal id of an unnamed [[modifier]].\n", tpl_args['id'], tpl_args['name']) end -- Categories cats = {'Mods'} -- Done -> output return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out) end return p