Module:Skill info
Documentation for this module may be created at Module:Skill info/doc
--------------------------
-- Module for [[Template:Skill info]]
--------------------------
local p = {}
local onmain = require('Module:Mainonly').on_main
local paramtest = require('Module:Paramtest')
local infobox = require('Module:Infobox')
local yesno = require('Module:Yesno')
local scp = require('Module:SCP')._main
local qty = require('Module:Quantity box')._main
local editbutton = require('Module:Edit button')
local edit = editbutton("'''?''' (edit)")
local presets = require('Module:Skill info/presets')
local MAXSKILL = 5 -- maximum amount of skills to iterate over; when changing this also change the `skillnames` param definition.
local SKILLS = {
'Attack', 'Hitpoints', 'Mining',
'Strength', 'Agility', 'Smithing',
'Defence', 'Herblore', 'Fishing',
'Ranged', 'Thieving', 'Cooking',
'Prayer', 'Crafting', 'Firemaking',
'Magic', 'Fletching', 'Woodcutting',
'Runecraft', 'Slayer', 'Farming',
'Construction', 'Hunter',
}
-- array contains val
function contains(tbl, val)
for _, item in ipairs(tbl) do
if item == val then
return true
end
end
return false
end
function p.main(frame)
local args = frame:getParent().args
preset = frame.args[1] and string.lower(frame.args[1]) or args.preset
local mand = {}
if preset and presets[preset] then
args, mand = presets[preset](args)
end
return p._main(args, mand)
end
function p._main(args, mand)
table.insert(mand, 'skill1exp') -- Experience is a mandatory parameter
local ret = infobox.new(args)
ret:defineParams{
{ name = 'name', func = 'name' },
{ name = 'itemname', func = { name = paramtest.default_to, params = { 'itemname', "Uses item" }, flag = { 'p', 'r' } } },
{ name = 'item', func = 'hasContent' },
{ name = 'item_smw', func = { name = csv_to_multi, params = { 'item', true }, flag = { 'p', 'r' } } },
{ name = 'locname', func = { name = paramtest.default_to, params = { 'locname', "Location" }, flag = { 'p', 'r' } } },
{ name = 'loc', func = 'hasContent' },
{ name = 'loc_smw', func = { name = csv_to_multi, params = { 'loc', true }, flag = { 'p', 'r' } } },
{ name = 'facilityname', func = { name = paramtest.default_to, params = { 'facilityname', "Facility" }, flag = { 'p', 'r' } } },
{ name = 'facility', func = 'hasContent' },
{ name = 'facility_smw', func = { name = csv_to_multi, params = { 'facility', true }, flag = { 'p', 'r' } } },
{ name = 'typename', func = { name = paramtest.default_to, params = { 'typename', "Type" }, flag = { 'p', 'r' } } },
{ name = 'type', func = 'hasContent' },
{ name = 'type_smw', func = { name = csv_to_multi, params = { 'type', true }, flag = { 'p', 'r' } } },
{ name = 'toolname', func = { name = paramtest.default_to, params = { 'toolname', "Required tool" }, flag = { 'p', 'r' } } },
{ name = 'tool', func = 'hasContent' },
{ name = 'tool_smw', func = { name = csv_to_multi, params = { 'tool', true }, flag = { 'p', 'r' } } },
{ name = 'timename', func = { name = paramtest.default_to, params = { 'timename', "Respawn time" }, flag = { 'p', 'r' } } },
{ name = 'time', func = 'hasContent' },
{ name = 'time_smw', func = { name = csv_to_multi, params = { 'time', true }, flag = { 'p', 'r' } } },
{ name = 'damage', func = 'hasContent' },
{ name = 'damagename', func = { name = paramtest.default_to, params = { 'damagename', "Damage" }, flag = { 'p', 'r' } } },
{ name = 'damage_smw', func = { name = csv_to_multi, params = { 'damage', true }, flag = { 'p', 'r' } } },
{ name = 'members', func = yesno },
{ name = 'dropversion', func = 'hasContent' },
}
-- Generate skill-related arguments for each skill, similar to [[Template:Recipe]]
for i = 1, MAXSKILL do
local s = 'skill'..tostring(i)
ret:defineParams{
{ name = s..'name', func = 'hasContent' },
{ name = s..'boostable', func = yesno },
{ name = s..'lvl', func = { name = numericarg, params = { s..'lvl' }, flag = 'd' } },
{ name = s..'disp', func = { name = expdisp, params = { s..'exp' }, flag = 'p' }, dupes = true },
{ name = s..'exp', func = { name = exparg, params = { s..'exp' }, flag = 'd' }, dupes = true },
{ name = s..'exp_def', func = { name = 'hasContent', params = { s..'exp' }, flag = 'p' } }, -- used for category, to check if the experience is actually defined
{ name = s..'exp_smw', func = { name = tonumber, params = { s..'exp' }, flag = 'p' } },
{ name = s..'label', func = { name = labelarg, params = { s..'label', s..'name', s..'note', s..'exp_smw' }, flag = 'd' } },
}
end
ret:defineParams{
{ name = 'skillnames', func = { name = tolist, params = { 'skill1name', 'skill2name', 'skill3name', 'skill4name', 'skill5name' }, flag = 'p' } },
{ name = 'skillnames_str', func = { name = table.concat, params = { 'skillnames', ',' }, flag = { 'd', 'r' } } },
{ name = 'skillnames_smw', func = { name = csv_to_multi, params = { 'skillnames_str', false }, flag = { 'd', 'r' } } },
{ name = 'levels', func = { name = tolist, params = { 'skill1lvl', 'skill2lvl', 'skill3lvl', 'skill4lvl', 'skill5lvl' }, flag = 'p' } },
{ name = 'clickpics', func = { name = clickpicsarg, params = { 'skillnames', 'levels' }, flag = 'd' } },
}
local smw_mapping = {
name = 'Name',
item_smw = 'Uses material',
facility_smw = 'Uses facility',
tool_smw = 'Uses tool',
members = 'Is members only',
skillnames_smw = 'Uses skill',
}
for _, s in ipairs(SKILLS) do
local lcs = string.lower(s)
-- These functions take in all lvl/xp info and poop out only the lvl/xp for the skill `lcs`.
ret:defineParams{
{ name = lcs..'lvl', func = { name = lvlsmw, params = { 'skillnames', 'levels', lcs }, flag = { 'd', 'd', 'r' } } },
{ name = lcs..'exp', func = {
name = expsmw,
params = { 'skillnames', 'skill1exp_smw', 'skill2exp_smw', 'skill3exp_smw', 'skill4exp_smw', 'skill5exp_smw', lcs },
flag = { 'd', 'd', 'd', 'd', 'd', 'd', 'r' }
} },
}
smw_mapping[lcs..'lvl'] = s..' level'
smw_mapping[lcs..'exp'] = s..' experience'
end
ret:create()
ret:cleanParams()
for i = 1, MAXSKILL do
local s = 'skill'..tostring(i)
-- link exp box to the respective class stored in disp
ret:linkParams{
{ s..'exp', s..'disp' },
}
end
ret:customButtonPlacement(true)
ret:setDefaultVersionSMW(true)
ret:addButtonsCaption()
ret:defineLinks({ hide = true })
-- Apply SMW properties
local smw_all_mapping = {}
for param, property_name in pairs(smw_mapping) do
smw_all_mapping[param] = 'All '..property_name
end
ret:useSMWSubobject(smw_mapping)
ret:useSMWOne(smw_all_mapping)
ret:defineName('Skill info')
ret:addClass('skill-info')
local tblwidth = 10
ret:addRow{
{ tag = 'argh', content = 'name', class='infobox-header', colspan = tblwidth }
}
:pad(tblwidth)
:addRow{
{ tag = 'th', content = 'Level required', colspan = math.ceil(tblwidth/2) },
{ tag = 'argd', content = 'clickpics', colspan = math.floor(tblwidth/2) }
}
for i = 1, MAXSKILL do
local s = 'skill'..tostring(i)
addNamedRow(ret, s..'exp', s..'label', tblwidth, mand)
end
addNamedRow(ret, 'item', 'itemname', tblwidth, mand)
addNamedRow(ret, 'loc', 'locname', tblwidth, mand)
addNamedRow(ret, 'facility', 'facilityname', tblwidth, mand)
addNamedRow(ret, 'type', 'typename', tblwidth, mand)
addNamedRow(ret, 'tool', 'toolname', tblwidth, mand)
addNamedRow(ret, 'time', 'timename', tblwidth, mand)
addNamedRow(ret, 'damage', 'damagename', tblwidth, mand)
ret:pad(tblwidth)
if onmain() then
local a1 = ret:param('all')
local a2 = ret:categoryData()
ret:wikitext(addcategories(a1, a2, mand))
end
local dropKey = string.lower(args.skill1name or '')
local dropLevel = dropKey .. 'lvl'
ret:addDropLevelVars(dropKey, dropLevel)
return ret:tostring()
end
function addNamedRow(ret, name, label, tblwidth, mand)
if ret:paramDefined(name) or contains(mand, name) then
ret:addRow{
{ tag = 'argh', content = label, colspan = math.ceil(tblwidth/2) },
{ tag = 'argd', content = name, colspan = math.floor(tblwidth/2) }
}
end
end
function labelarg(lbl, skill, note, xp)
local ref = ''
if paramtest.has_content(note) then
ref = mw.getCurrentFrame():extensionTag{ name = 'ref', content = note, args = { group = 'i' } }
end
if paramtest.has_content(lbl) then
return lbl .. ref
elseif not infobox.isDefined(skill) then
if (tonumber(xp) or 0) < 0 then
return "[PLACEHOLDER]"
else
return nil
end
else
return string.format('[[%s|%s XP]]', skill, skill) .. ref
end
end
function exparg(xp)
if xp ~= '' then
return qty(xp)
else
return qty(0) .. edit
end
end
function expdisp(xp)
-- default: show (needs explicit -1 to be hidden)
if (tonumber(xp) or 1) < 0 then
return 'infobox-cell-hidden'
else
return 'infobox-cell-shown'
end
end
function tolist(...)
return arg
end
-- Make list of clickpics for output
function clickpicsarg(names, lvls)
local ret = ''
for i, lvl in ipairs(lvls) do
local name = names[i]
if paramtest.has_content(name) and paramtest.has_content(lvl) then
ret = ret .. scp(name, lvl)
end
end
if ret ~= '' then
return ret
else
return edit
end
end
function numericarg(arg)
if not infobox.isDefined(arg) then
return edit
end
return arg
end
function lvlsmw(skills, lvls, name)
for i, s in ipairs(skills) do
if string.lower(s) == name then
return lvls[i]
end
end
return nil
end
function expsmw(skills, xp1, xp2, xp3, xp4, xp5, name)
local xp = {xp1, xp2, xp3, xp4, xp5}
for i, s in ipairs(skills) do
if string.lower(s) == name then
return xp[i]
end
end
return nil
end
-- Take the link targets from any list of links and turn it into a plain csv
function linkcsv(str)
if type(str) ~= 'string' then return str end
str = string.gsub(str, '%[%[File:[^%]]+%]%]', '')
local ret = {}
for m in string.gmatch(str, '%[%[([^%]|]+)') do
table.insert(ret, m)
end
for m in string.gmatch(str, '%{%{[Pp]link|([^%}|]+)') do
table.insert(ret, m)
end
return table.concat(ret, '&&SPLITPOINT&&')
end
function csv_to_multi(raw, links)
assert(type(links) == 'boolean')
local r = string.gsub(raw, "'\"`UNIQ[^`]*QINU`\"'", '') -- UNIQ QINU typically means unparsed content, such as <ref></ref>. Remove this from SMW
if infobox.isDefined(raw) then
if links then
r = linkcsv(r)
else
r = string.gsub(r, '%s*,%s*', '&&SPLITPOINT&&')
end
return r
end
return nil
end
function addcategories(args, catargs, mand)
local ret = {}
-- mandatory arguments missing
for _, v in ipairs(mand) do
if catargs[v] and not catargs[v].all_defined then
if v ~= 'skill1exp' then -- skill1exp is handled individually for a more specific category
table.insert(ret, 'Missing skill info values')
mw.log(v .. " is not defined (missing skill info values)")
end
end
end
-- Experience missing for one of the versions, or for any skill1 version
for i = 1, 5 do
local stri = tostring(i)
-- Any skill# that has experience defined at least once, should specify it for every version. Also skill1 must be filled out fully.
if (catargs['skill'..stri..'exp_def'].one_defined or i == 1) then
-- No experience rate given for at least one version
if not catargs['skill'..stri..'exp_def'].all_defined then
table.insert(ret, 'Needs experience info')
elseif not catargs['skill'..stri..'exp_smw'].all_defined then
table.insert(ret, 'Pages with non-numeric experience quantity')
end
-- No label specified for at least one version
if not catargs['skill'..stri..'label'].all_defined then
table.insert(ret, 'Missing skill info values')
end
end
end
-- Determine skill-related categories to add
local skills = {}
-- Get list of all `skillnames` params, which in turn consist of a full list of skill names for each switch version
local skillstr = args['skillnames_str']
local skillnames = skillstr.switches
-- for non-switched boxes, just use the default; otherwise, append the default values
if not skillnames then
skillnames = {skillstr.d}
else
table.insert(skillnames, skillstr.d)
end
-- iterate over all skills and toggle associative array for listed skills
for _, ver in ipairs(skillnames) do
for s in mw.text.gsplit(ver, ',') do
if paramtest.has_content(s) then
skills[s] = true
end
end
end
-- add actual skill categories
for skill, _ in pairs(skills) do
table.insert(ret, skill)
end
-- combine table and format category wikicode
for i, v in ipairs(ret) do
if v ~= '' then
ret[i] = string.format('[[Category:%s]]', v)
end
end
return table.concat(ret, '')
end
return p