Module:Sandbox/Cqm/IM
Documentation for this module may be created at Module:Sandbox/Cqm/IM/doc
-- <pre>
-- ( ͡° ͜ʖ ͡°)
local p = {}
-- "imports"
local onmain = require('Module:Mainonly').on_main
local paramtest = require('Module:Paramtest')
local yesno = require('Module:Yesno')
local commas = require('Module:Addcommas')._add
local infobox = require('Module:Infobox')
--[[
-- Mapping
--]]
local attack_styles = {
melee = { image = 'Attack-icon', link = 'Melee' },
ranged = { image = 'Ranged-icon', link = 'Ranged' },
magic = { image = 'Magic-icon', link = 'Magic' },
dragonfire = { image = 'Dragonfire icon', link = 'Dragonfire' },
na = { image = 'Zero weakness icon', link = '' },
typeless = { image = 'Zero weakness icon', link = '' }
}
local styles_map = {
melee = 'melee',
magic = 'magic',
mage = 'magic',
range = 'ranged',
ranged = 'ranged',
ranging = 'ranged',
dragonfire = 'dragonfire',
['dragon fire'] = 'dragonfire',
dragonbreath = 'dragonfire',
['dragon breath'] = 'dragonfire',
['n/a'] = 'na',
na = 'na',
none = 'na'
}
local slay_masters = {
turael = { text = '[[Turael]] or [[Spria]]', chathead = 'turael chathead', category = 'Monsters assigned by Turael or Spria' },
mazchna = { text = '[[Mazchna]] or [[Achtryn]]', chathead = 'mazchna chathead', category = 'Monsters assigned by Mazchna or Achtryn' },
chaeldar = { text = '[[Chaeldar]]', chathead = 'chaeldar chathead', category = 'Monsters assigned by Chaeldar' },
sumona = { text = '[[Sumona]]', chathead = 'sumona chathead', category = 'Monsters assigned by Sumona' },
vannaka = { text = '[[Vannaka]]', chathead = 'vannaka chathead', category = 'Monsters assigned by Vannaka' },
duradel = { text = '[[Duradel]] or [[Lapalok]]', chathead = 'duradel chathead', category = 'Monsters assigned by Duradel or Lapalok' },
kuradal = { text = '[[Kuradal]]', chathead = 'kuradal chathead', category = 'Monsters assigned by Kuradal' },
morvran = { text = '[[Morvran]]', chathead = 'morvran chathead', category = 'Monsters assigned by Morvran' },
}
local slay_masters_map = {
turael = 'turael',
spria = 'turael',
mazchna = 'mazchna',
achtryn = 'mazchna',
chaeldar = 'chaeldar',
sumona = 'sumona',
vannaka = 'vannaka',
duradel = 'duradel',
lapalok = 'duradel',
kuradal = 'kuradal',
morvran = 'morvran',
no = 'none',
none = 'none',
['n/a'] = 'none'
}
local speeds = {
[0] = 'N/A',
[1] = '1 tick (0.6s)',
[2] = '2 ticks (1.2s)',
[3] = '3 ticks (1.8s)',
[4] = '4 ticks (2.4s)',
[5] = '5 ticks (3.0s)',
[6] = '6 ticks (3.6s)',
[7] = '7 ticks (4.2s)',
[8] = '8 ticks (4.8s)',
[9] = '9 ticks (5.4s)',
[10] = '10 ticks (6s)'
}
local speed_map = {
['1'] = 1,
['2'] = 2,
['3'] = 3,
['4'] = 4,
['5'] = 5,
['6'] = 6,
['7'] = 7,
['8'] = 8,
['9'] = 9,
['10'] = 10,
['n/a'] = 0,
['0'] = 0
}
-- load weaknesses instead of remapping
local weaknesses = mw.loadData('Module:Weakness clickpic/data')
-- Main function called with invokes
function p.main(frame)
local args = frame:getParent().args
local ret = infobox.new(args)
local yn_args = {
'aggressive', 'immune_to_poison', 'immune_to_deflect',
'immune_to_stun', 'immune_to_drain'
}
local num_args = {
'slaylvl', 'slayxp',
'max_melee', 'max_ranged', 'max_magic', 'max_spec',
'acc_melee', 'acc_ranged', 'acc_magic', 'armour',
'aff_weakness', 'aff_melee', 'aff_ranged', 'aff_magic' }
for _, v in ipairs(yn_args) do
ret:defineParams{ { name = v, func = boolargs } }
end
for _, v in ipairs(num_args) do
ret:defineParams{ { name = v, func = { name = numberargs, params = { v, v }, flag = { 'p', 'r' } } }}
end
ret:defineParams{
{ name = 'poisonous', func = poisonarg },
{ name = 'level', func = combatarg },
{ name = 'style', func = stylearg },
{ name = 'lifepoints', func = numargcommas },
{ name = 'experience', func = numargcommas },
{ name = 'hpxp', func = { name = hpxparg, params = { 'experience' } } },
{ name = 'wepxp', func = { name = wepxparg, params = { 'experience' } } },
{ name = 'weakness', func = weaknessarg },
{ name = 'explicit_weakness', func = { name = explicitwkarg, params = { 'weakness' }, flag = 'p' } },
{ name = 'abilities', func = abilarg },
{ name = 'assigned_by', func = slayerarg },
{ name = 'not_assigned', func = { name = notassarg, params = { 'assigned_by' }, flag = 'p' } },
{ name = 'release', func = 'release' },
{ name = 'removal', func = 'removal' },
{ name = 'members', func = 'has_content' },
{ name = 'examine', func = 'has_content' },
{ name = 'speed', func = speedarg }
}
ret:defineParams {
{ name = 'name', func = 'name' },
{ name = 'aka',
func = function(arg) return paramtest.has_content(arg) and arg or false end },
{ name = 'image', func = 'image' },
{ name = 'slayercat',
func = function(arg) return paramtest.has_content(arg) and arg or 'N/A' end }
}
ret:create()
ret:cleanParams()
ret:caption()
ret:addClass('infobox-monster')
ret:defineName('Infobox Monster')
-- PARAMETER: image
ret:addRow{
{ tag = 'argd', content = 'image', class='infobox-image', colspan = '4' } }
-- PARAMETER: release
-- (update included automatically by infobox)
:addRow{ { tag = 'th', content = 'Release' },
{ tag = 'argd', content = 'release', css = { ['text-align'] = 'left' }, colspan = '3' } }
-- PARAMETER: removal
if ret:paramDefined('removal') then
ret:addRow{ { tag = 'th', content = 'Removal' },
{ tag = 'argd', content = 'removal', css = { ['text-align'] = 'left' }, colspan = '3' } }
end
-- PARAMETER: aka
-- add only if it exists
if ret:paramDefined('aka') then
ret:addRow{ { tag = 'th', content = 'AKA' },
{ tag = 'argd', content = 'aka', css = { ['text-align'] = 'left' }, colspan = '3' } }
end
-- PARAMETER: members
ret:addRow{ { tag = 'th', content = 'Members' },
{ tag = 'argd', content = 'members', css = { ['text-align'] = 'left' }, colspan = '3' } }
:addRow{ { tag = 'th', content = 'Examine', colspan = '4' } }
-- PARAMETER: examine
:addRow{ { tag = 'argd', content = 'examine', css = { ['text-align'] = 'left', ['max-width'] = '250px' }, colspan = '4' } }
-- COMBAT INFO
:addRow{ { tag = 'th', content = 'Combat info', css = { ['font-variant'] = 'small-caps' }, colspan = '4' } }
-- PARAMETER: level | lifepoints | experience | hpxp
:addRow{ { tag = 'th', content = '[[Combat level|Level]]', css = { width = '25%' } },
{ tag = 'th', content = '[[Life points|LP]]', css = { width = '25%' } },
{ tag = 'th', content = '[[File:Multicombat.png|link=|20px]] XP', css = { width = '25%' }, title = 'Combat style experience' },
{ tag = 'th', content = '[[File:Constitution-icon.png|link=|20px]] XP', css = { width = '25%' }, title = 'Constitution experience' } }
:addRow{ { tag = 'argd', content = 'level' },
{ tag = 'argd', content = 'lifepoints' },
{ tag = 'argd', content = 'experience' },
{ tag = 'argd', content = 'hpxp' } }
-- PARAMETER: wepxp
:addRow{ { tag = 'th', content = '[[Equipment level|Equipment XP]] (2H/MH & Armour/OH)', colspan = '4' } }
:addRow{ { tag = 'argd', content = 'wepxp', colspan = '4' } }
-- PARAMETER: aggressive | poisonous
:addRow{ { tag = 'th', content = '[[Aggressiveness|Aggressive]]', colspan = '2' },
{ tag = 'th', content = 'Poisonous', colspan = '2' } }
:addRow{ { tag = 'argd', content = 'aggressive', colspan = '2' },
{ tag = 'argd', content = 'poisonous', colspan = '2' } }
-- Slayer information
-- dynamic size, dependent on whether or not the monster is assigned
local slayer_level = ret:paramDefined('slaylvl','all')
local not_assigned = ret:param('not_assigned','f')
local _not_assigned = true
if not_assigned.d == false then
_not_assigned = false
elseif not_assigned.switches then
for _, v in ipairs(not_assigned.switches) do
if v == false then
_not_assigned = false
break
end
end
end
-- do slayer row if a monster is assigned, or has a required level
if _not_assigned == false or slayer_level == true then
-- PARAMETER: slaylvl | slayxp | slayercat
ret:addRow{ { tag = 'th', content = 'Slayer', class = 'slayer-header', colspan = '4' } }
:addRow{ { tag = 'th', content = 'Level' },
{ tag = 'th', content = 'XP' },
{ tag = 'th', content = 'Category', colspan = '2' } }
:addRow{ { tag = 'argd', content = 'slaylvl' },
{ tag = 'argd', content = 'slayxp' },
{ tag = 'argd', content = 'slayercat', colspan = '2' } }
:addRow{ { tag = 'th', content = 'Assigned by', colspan = '4' } }
:addRow{ { tag = 'argd', content = 'assigned_by',colspan = '4' } }
end
----------------
-- OFFENSIVE STATS
local max_disc = 'This value is the standard, non-legacy value. The number reflects the BASE maximum hit used by the monster in damage calculations. Damage-boosting mechanics such as enrage are not taken into consideration.'
ret:addRow{ { tag = 'th', content = 'Offensive', class = 'offensive-header', colspan = '4' } }
:addRow{ { tag = 'th', content = '[[Maximum hit|Max hit]]', class = 'offensive-subheader', colspan = '4' } }
-- PARAMETER: max_melee | max_ranged | max_magic | max_spec
:addRow{ { tag = 'th', content = '[[File:Attack-icon.png|20px|link=]]', title = 'Maximum melee hit' },
{ tag = 'th', content = '[[File:Ranged-icon.png|20px|link=]]', title = 'Maximum ranged hit' },
{ tag = 'th', content = '[[File:Magic-icon.png|20px|link=]]', title = 'Maximum magic hit' },
{ tag = 'th', content = '[[File:Special attack icon.png|20px|link=]]', title = 'Maximum typeless/special hit' } }
:addRow{ { tag = 'argd', content = 'max_melee', title = max_disc },
{ tag = 'argd', content = 'max_ranged', title = max_disc },
{ tag = 'argd', content = 'max_magic', title = max_disc },
{ tag = 'argd', content = 'max_spec', title = max_disc } }
-- PARAMETER: style | speed
:addRow{ { tag = 'th', content = 'Style', class = 'offensive-subheader', colspan = '2' },
{ tag = 'th', content = '[[Attack speed|Speed]]', class = 'offensive-subheader', colspan = '2' } }
:addRow{ { tag = 'argd', content = 'style', colspan = '2' },
{ tag = 'argd', content = 'speed', colspan = '2' } }
-- PARAMETER: acc_melee | acc_ranged | acc_magic
:addRow{ { tag = 'th', content = '[[Hit chance|Accuracy]]', class = 'offensive-subheader', colspan = '4' } }
--[[
Accuracy parameters
It's a big job because we need to have a nested table, so it's very hacky
--]]
local acc_melee,
acc_ranged,
acc_magic = ret:param('acc_melee','r') ,
ret:param('acc_ranged','r'),
ret:param('acc_magic','r')
ret:tag('tr')
:addClass('nestedinfo')
:tag('td')
:attr('colspan','4')
:tag('table')
:addClass('wikitable')
:tag('tr')
:tag('th')
:attr('title','Melee accuracy as a function of 2.5 * f(x)')
:css('width','33%')
:wikitext('[[File:Attack-icon.png|20px|link=]]')
:done()
:tag('th')
:attr('title','Ranged accuracy as a function of 2.5 * f(x)')
:css('width','33%')
:wikitext('[[File:Ranged-icon.png|20px|link=]]')
:done()
:tag('th')
:attr('title','Magic accuracy as a function of 2.5 * f(x)')
:css('width','33%')
:wikitext('[[File:Magic-icon.png|20px|link=]]')
:done()
:done()
:tag('tr')
-- PARAMETER: melee accuracy
:tag('td')
:css('width','33%')
:wikitext(acc_melee)
:attr('data-attr-param','acc_melee')
:done()
-- PARAMETER: ranged accuracy
:tag('td')
:css('width','33%')
:wikitext(acc_ranged)
:attr('data-attr-param','acc_ranged')
:done()
-- PARAMETER: magic accuracy
:tag('td')
:css('width','33%')
:wikitext(acc_magic)
:attr('data-attr-param','acc_magic')
:done()
:done()
:done()
:done()
:done()
-- ACCURACY DONE (thank god!)
-- PARAMETER: abilities
-- only add if they exist
local abilities = ret:param('abilities','f')
local _abilities = false
if abilities.d and abilities.d ~= 'None' then
_abilities = true
end
if abilities.switches then
for _, v in ipairs(abilities.switches) do
if v ~= _nil then
_abilities = true
break
end
end
end
if _abilities then
ret:addRow{ { tag = 'th', content = 'Abilities used', class = 'offensive-subheader', colspan = '4' } }
:addRow{ { tag = 'argd', content = 'abilities', colspan = '4' } }
end
-- DEFENSIVE STATS
ret:addRow{ { tag = 'th', content = 'Defensive', class = 'defensive-header', colspan = '4' } }
-- PARAMETER: armour | weakness
:addRow{ { tag = 'th', content = 'Armour', class = 'defensive-subheader', colspan = '2' },
{ tag = 'th', content = '[[Weakness]]', class = 'defensive-subheader', colspan = '2' } }
:addRow{ { tag = 'argd', content = 'armour', colspan = '2' },
{ tag = 'argd', content = 'weakness', colspan = '2' } }
-- PARAMETER: explicit_weakness
-- PARAMETER: aff_weakness | aff_melee | aff_ranged | aff_magic
:addRow{ { tag = 'th', content = '[[Affinity|Affinities]]', class = 'defensive-subheader', colspan = '4' } }
:addRow{ { tag = 'argh', content = 'explicit_weakness', title = 'Affinity value of the monster\'s explicit weakness' },
{ tag = 'th', content = '[[File:Attack-icon.png|20px|link=]]', title = 'Affinity value of the monster against melee attacks' },
{ tag = 'th', content = '[[File:Ranged-icon.png|20px|link=]]', title = 'Affinity value of the monster against ranged attacks' },
{ tag = 'th', content = '[[File:Magic-icon.png|20px|link=]]', title = 'Affinity value of the monster against magic attacks' } }
:addRow{ { tag = 'argd', content = 'aff_weakness' },
{ tag = 'argd', content = 'aff_melee' },
{ tag = 'argd', content = 'aff_ranged' },
{ tag = 'argd', content = 'aff_magic' } }
-- PARAMETER: immune_to_poison | immune_to_deflect | immune_to_stun | immune_to_drain
:addRow{ { tag = 'th', content = 'Immunities', class = 'defensive-subheader', colspan = '4' } }
:addRow{ { tag = 'th', content = '[[File:Immune to poison.png|link=]]', title = 'Immune to poison' },
{ tag = 'th', content = '[[File:Immune to deflect.png|link=]]', title = 'Immune to deflect' },
{ tag = 'th', content = '[[File:Immune to stun.png|link=]]', title = 'Immune to stun' },
{ tag = 'th', content = '[[File:Immune to drain.png|link=]]', title = 'Immune to stat drain' } }
:addRow{ { tag = 'argd', content = 'immune_to_poison' },
{ tag = 'argd', content = 'immune_to_deflect' },
{ tag = 'argd', content = 'immune_to_stun' },
{ tag = 'argd', content = 'immune_to_drain' } }
ret:finish()
if onmain() then
local a1 = ret:param('all')
local a2 = ret:categoryData()
ret:wikitext(addcategories(a1,a2))
end
return ret:tostring()
end
-- For numerical args
function numberargs(arg,v)
local arg_v = (arg or ''):find('%S') and string.gsub(arg,',','') or -1
local arg_i
if arg_v == -1 then
arg_i = nil
elseif string.lower(arg_v) == 'n/a' then
arg_i = 'N/A'
elseif string.lower(arg_v) == 'varies' then
arg_i = 'Varies'
else
arg_i = tonumber(arg_v:gsub(',',''),10)
if not arg_i then
arg_i = badarg(v,'should be a single numerical value.')
end
end
return arg_i
end
-- For combat level
function combatarg(arg)
local arg_v = (arg or ''):find('%S') and string.gsub(arg,',','') or -1
local arg_i
if arg_v == -1 then
arg_i = nil
elseif string.lower(arg_v) == 'n/a' or tonumber(arg_v) == 0 then
arg_i = 'N/A'
else
arg_i = tonumber(arg_v:gsub(',',''),10)
if not arg_i then
arg_i = badarg('level','should be a single numerical value.')
end
end
return arg_i
end
-- For numbers (adds commas)
function numargcommas(arg)
local ret = numberargs(arg)
if type(ret) == 'number' then
return commas(ret)
else
return ret
end
end
-- For hp xp
function hpxparg(arg)
local xp = string.gsub(arg or '',',','')
if string.lower(xp) == 'n/a' then
return 'N/A'
else
xp = tonumber(xp)
end
if type(xp) == 'number' then
return commas(math.floor(xp*3.3)/10)
else
return nil
end
end
-- weapon xp
function wepxparg(arg)
local xp = string.gsub(arg or '',',','')
if string.lower(xp) == 'n/a' then
return 'N/A'
else
xp = tonumber(xp)
end
if type(xp) == 'number' then
local th,mh,oh
th = math.floor(xp * .06)
mh = math.floor(xp * .04)
oh = math.floor(xp * .02)
return string.format('%s / %s / %s',th,mh,oh)
else
return nil
end
end
-- For true/false
function boolargs(arg)
local arg_v = (arg or ''):find('%S') and arg or -1
local arg_i
if arg_v == -1 then
arg_i = nil
else
arg_v = yesno(arg_v)
if arg_v then
arg_i = '[[File:Yes check.svg|20px|alt=Yes|link=]]'
else
arg_i = '[[File:X mark.svg|20px|alt=No|link=]]'
end
end
return arg_i
end
-- Poison
function poisonarg(arg)
arg = string.gsub(arg or '',',','')
arg = string.lower(arg)
if arg == 'yes' then
arg = '<span title="This monster is poisonous but has no base value defined. Change the |poisonous parameter to a number">[[File:Yes check.svg|20px|alt=Yes|link=]] ???</span>'
elseif tonumber(arg) then
arg = '<span title="Base value at which poison damage starts">[[File:Yes check.svg|20px|alt=Yes|link=]] '..tonumber(arg)..'</span>'
else
arg = '[[File:X mark.svg|20px|alt=No|link=]]'
end
return arg
end
-- style
function stylearg(arg)
-- split by commas
local atts = mw.text.split(string.lower(arg or ''),'%s*,%s*')
local _atts = {}
-- remake the list as a table, remove anything that's blank/doesn't exist
for _, v in ipairs(atts) do
local att_x = styles_map[v]
if att_x then
table.insert(_atts,attack_styles[att_x])
end
end
local p_att
if #_atts == 0 then
p_att = nil
else
p_att = {}
for _, v in ipairs(_atts) do
table.insert(p_att,string.format('[[File:%s.png|25px|link=%s]]',v.image,v.link))
end
p_att = table.concat(p_att,' ')
end
return p_att
end
-- weakness
function weaknessarg(arg)
-- split by commas
local wk = mw.text.split(string.lower(arg or ''),'%s*,%s*')
local _wk = {}
-- remake the list as a table, remove anything that's blank/doesn't exist
for _, v in ipairs(wk) do
local wk_x = weaknesses[v]
if wk_x then
table.insert(_wk,wk_x)
end
end
local p_wk
if #_wk == 0 then
p_wk = nil
else
p_wk = {}
for _, v in ipairs(_wk) do
table.insert(p_wk,string.format('[[File:%s|25px|link=%s]]',v.image,v.link))
end
p_wk = table.concat(p_wk,' ')
end
return p_wk
end
-- Explicit weakness
-- weakness / explicit weakness
function explicitwkarg(arg)
-- split by commas
local wk = mw.text.split(string.lower(arg or ''),'%s*,%s*')
local _wk = weaknesses[wk[1]]
if _wk then
return string.format('[[File:%s|20px|link=]]',_wk.image)
else
return nil
end
end
function wkcats(arg,tbl)
for _, v in pairs(weaknesses) do
if arg:find(v.image) then
if v.category then
table.insert(tbl,v.category)
end
end
end
end
function slaycats(arg,tbl)
for _, v in pairs(slay_masters) do
if arg:find(v.chathead) then
if v.category then
table.insert(tbl,v.category)
end
end
end
end
-- Slayer assigners
function slayerarg(_arg)
local arg = _arg
if arg then
arg = mw.text.split(string.lower(arg),'%s*,%s*')
local slayer_master_list = {
turael = false,
spria = false,
mazchna = false,
achtryn = false,
vannaka = false,
chaeldar = false,
sumona = false,
duradel = false,
lapalok = false,
kuradal = false,
morvran = false,
none = false
}
for _, v in ipairs(arg) do
if slay_masters_map[v] then
slayer_master_list[slay_masters_map[v]] = true
end
end
if slayer_master_list.none then
arg = 'Not assigned'
else
arg = {}
for n, v in pairs(slayer_master_list) do
if v then
--table.insert(params.assigned_by, slay_masters[n].text)
table.insert(arg, 1, string.format('[[File:%s.png|30px|link=%s]]',slay_masters[n].chathead,n))
end
end
arg = table.concat(arg,' ')
end
else
arg = nil
end
return arg
end
-- Not assigned
function notassarg(arg)
if arg then
if arg:find('%?action=edit') or not arg:find('%S') then
return true
end
arg = mw.text.split(string.lower(arg),'%s*,%s*')
for _, v in ipairs(arg) do
if slay_masters_map[v] == 'none' then
return true
end
end
end
return false
end
-- Abilities used
function abilarg(arg)
arg = paramtest.default_to(arg,false)
if not arg then
arg = 'None'
elseif arg:find('clickpic') then
arg = mw.getCurrentFrame():preprocess(arg)
else
arg = 'None'
end
return arg
end
-- Attacks speed
function speedarg(arg)
if paramtest.is_empty(arg) then
return nil
end
_,_,arg = string.find(arg:lower()..' ','^(.-)%s')
arg = speeds[speed_map[arg]]
or badarg('speed',' is not a valid attack speed')
return arg
end
-- red ERR span with title hover for explanation
function badarg(argname, argmessage)
return '<span '..
'title="The parameter «'..argname..'» '..argmessage..'" '..
'style="color:red; font-weight:bold; cursor:help; border-bottom:1px dotted red;">'..
'ERR</span>'
end
function addcategories(args,catargs)
local ret = { 'Bestiary' }
local cat_map = {
-- Added if the parameter has content
defined = {
aka = 'Pages with AKA'
},
-- Added if the parameter has no content
notdefined = {
image = 'Needs image',
members = 'Needs members status',
release = 'Needs release date',
examine = 'Needs examine added',
level = 'Needs combat level',
immune_to_stun = 'Missing immunity information',
immune_to_poison = 'Missing immunity information',
immune_to_deflect = 'Missing immunity information',
immune_to_drain = 'Missing immunity information'
},
-- Parameters that are either true or false
-- map a category to a boolean value
yesno = {
members = { [true] = '', [false] = 'F2P bestiary' },
abilities = { [true] = 'Monsters that use abilities', [false] = '' },
immune_to_stun = { [true] = 'Stun-immune', [false] = '' },
}
}
-- Run and add mapped categories
for n, v in pairs(cat_map.defined) do
if catargs[n] and catargs[n].one_defined then
table.insert(ret,v)
end
end
for n, v in pairs(cat_map.notdefined) do
if catargs[n] and catargs[n].all_defined == false then
table.insert(ret,v)
end
end
local levels = { args.level.d }
if args.level.switches then
for _, v in ipairs(args.level.switches) do
if v ~= infobox.nil_param() then
table.insert(levels,v)
end
end
end
for _, v in ipairs(levels) do
if v == 'N/A' then
table.insert(ret,'Monsters with no combat level')
elseif tonumber(v) then
table.insert(ret,string.format('Combat level %s monsters',v))
end
end
if args.members.d:find('[Nn]o') then
table.insert(ret,'F2P bestiary')
elseif args.members.switches then
for _, v in ipairs(args.members.switches) do
if v:find('[Nn]o') then
table.insert(ret,'F2P bestiary')
break
end
end
end
if args.abilities.d:find('clickpic') then
table.insert(ret,'Monsters that use abilities')
elseif args.abilities.switches then
for _, v in ipairs(args.abilities.switches) do
if v:find('clickpic') then
table.insert(ret,'Monsters that use abilities')
break
end
end
end
if args.weakness.d then
wkcats(args.weakness.d,ret)
end
if args.weakness.switches then
for _, v in ipairs(args.weakness.switches) do
if v ~= _nil then
wkcats(v,ret)
end
end
end
if args.assigned_by.d then
slaycats(args.assigned_by.d,ret)
end
if args.assigned_by.switches then
for _, v in ipairs(args.assigned_by.switches) do
if v ~= _nil then
slaycats(v,ret)
end
end
end
-- combine table and format category wikicode
for i, v in ipairs(ret) do
ret[i] = string.format('[[Category:%s]]',v)
end
return table.concat(ret,'')
end
return p