Module:Infobox Monster

-- -- -- Module for Template:Infobox Monster new -- Please test changes to this module at Module:Infobox Monster/sandbox first

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') local attack_speed_bar = require('Module:Attack speed bar').monster

-- -- 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 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, ['random'] = 'random', ['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', 'attack', 'strength', 'defence', 'ranged', 'magic', 'max_melee', 'max_ranged', 'max_magic', 'max_spec', 'acc_melee', 'acc_ranged', 'acc_magic', 'armour', 'aff_melee', 'aff_ranged', 'aff_magic' } --aff_weakness has a special func

for _, v in ipairs(yn_args) do		ret:defineParams{ { name = v, func = { name = boolargs, params = { v, v }, flag = { 'p', 'r' } } }} 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 = { name = poisonarg, params = { 'poisonous' }, flag = 'p' } }, { name = 'level', func = combatarg }, { name = 'style', func = stylearg }, { name = 'lifepoints', func = numargcommas }, { name = 'experience', func = numargcommas }, { name = 'xpraw', func = { name = numargraw, params = { 'experience' }, flag = 'p' } }, { name = 'hpxp', func = { name = hpxparg, params = { 'experience' } } }, { name = 'wepxp2h', func = numargcommas }, { name = 'wepxpmhandarmour', func = numargcommas }, { name = 'wepxpoh', func = numargcommas }, { name = 'wepxp', func = { name = wepxparg, params = { 'experience', 'wepxp2h', 'wepxpmhandarmour', 'wepxpoh' } } }, { name = 'weakness', func = weaknessarg }, { name = 'explicit_weakness', func = { name = explicitwkarg, params = { 'weakness' }, flag = 'p' } }, { name = 'aff_weakness', func = { name = aff_weakness_arg, params = { 'aff_weakness', 'aff_weakness', 'weakness' }, flag = { 'p', 'r', '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 }, { name = 'name', func = 'name' }, { name = 'aka', func = 'has_content' }, { name = 'image', func = 'image' }, { name = 'chathead', func = 'image' }, { name = 'slayercat', func = 'has_content' }, -- not used; only for categories { name = 'id', func = 'numbers' }, { name = 'rscid', func = 'numbers' } }

ret:useSMW({		id = 'NPC ID',		level = 'Combat level',		xpraw = 'Combat experience',		lifepoints = 'Life points',		slayxp = 'Slayer experience',		members = 'Is members only'	})

ret:defineLinks({ { 'Template:%s/FAQ', 'FAQ' },			{ 'Template:%s/doc', 'doc' } }) ret:create ret:cleanParams ret:caption ret:addClass('infobox-monster') ret:defineName('Infobox Monster') -- PARAMETER: chathead if ret:paramDefined('chathead', 'all') then ret:addRow{ { tag = 'argd', content = 'chathead', class='infobox-image', colspan = '4' } } end -- PARAMETER: image ret:addRow{ { tag = 'argd', content = 'image', class='infobox-image', colspan = '4' } } -- PARAMETER: release -- (update included automatically by infobox) ret: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', ['max-width'] = '100px' }, 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'] = 'center', ['max-width'] = '250px' }, colspan = '4' } }

-- COMBAT INFO :addRow{ { tag = 'th', content = 'Combat info', css = { ['font-variant'] = 'small-caps' }, colspan = '4', class = 'combat-info-header' } } -- PARAMETER: level | lifepoints | experience | hpxp :addRow{ { tag = 'th', content = 'Level', css = { width = '25%' } }, { tag = 'th', content = 'LP', css = { width = '25%' } }, { tag = 'th', content = ' XP', css = { width = '25%' }, title = 'Combat style experience' }, { tag = 'th', content = ' XP', css = { width = '25%' }, title = 'Constitution experience' } }

{ tag = 'argd', content = 'lifepoints' }, { tag = 'argd', class='mob-cb-xp', content = 'experience' }, { tag = 'argd', class='mob-hp-xp', content = 'hpxp' } } -- PARAMETER: wepxp :addRow{ { tag = 'th', content = 'Equipment XP (2H/MH & Armour/OH)', colspan = '4' } } :addRow{ { tag = 'argd', content = 'wepxp', class='mob-eq-xp', colspan = '4' } } -- PARAMETER: aggressive | poisonous :addRow{ { tag = 'th', content = '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', class='mob-slay-xp', 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 = 'Max hit', class = 'offensive-subheader', colspan = '4' } } -- PARAMETER: max_melee | max_ranged | max_magic | max_spec :addRow{ { tag = 'th', content = '', title = 'Maximum melee hit' }, { tag = 'th', content = '', title = 'Maximum ranged hit' }, { tag = 'th', content = '', title = 'Maximum magic hit' }, { tag = 'th', content = '', 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 = 'Speed', class = 'offensive-subheader', colspan = '2' } }

{ tag = 'argd', content = 'speed', colspan = '2' } }

-- PARAMETER: acc_melee | acc_ranged | acc_magic :addRow{ { tag = 'th', content = 'Combat levels', 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') local lv_attack, lv_ranged, lv_magic = ret:param('attack','r') , ret:param('ranged','r'), ret:param('magic','r') local levels_disc = "This is the monster's level in this combat skill. It functions in the same way a player's does - it affects the accuracy or armour in the same way (but not damage)." ret:tag('tr') :addClass('nestedinfo') :tag('td') :attr('colspan','4') :tag('table') :addClass('wikitable') :tag('tr') :tag('th') :attr('title','Attack level') :css('width','33%') :wikitext('') :done :tag('th') :attr('title','Ranged level') :css('width','33%') :wikitext('') :done :tag('th') :attr('title','Magic level') :css('width','33%') :wikitext('') :done :done :tag('tr') -- PARAMETER: attack level :tag('td') :css('width','33%') :attr('title', levels_disc) :wikitext(lv_attack) :attr('data-attr-param','attack') :done -- PARAMETER: ranged level :tag('td') :css('width','33%') :attr('title', levels_disc) :wikitext(lv_ranged) :attr('data-attr-param','ranged') :done -- PARAMETER: magic level :tag('td') :css('width','33%') :attr('title', levels_disc) :wikitext(lv_magic) :attr('data-attr-param','magic') :done :done :tag('tr') :tag('th') :attr('colspan', 3) :addClass('offensive-subheader') :wikitext('Accuracy') :done :done :tag('tr') :tag('th') :attr('title','Melee accuracy as a function of 2.5 * f(x)') :css('width','33%') :wikitext('') :done :tag('th') :attr('title','Ranged accuracy as a function of 2.5 * f(x)') :css('width','33%') :wikitext('') :done :tag('th') :attr('title','Magic accuracy as a function of 2.5 * f(x)') :css('width','33%') :wikitext('') :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 | defence | weakness :addRow{ { tag = 'th', content = 'Armour', class = 'defensive-subheader' }, { tag = 'th', content = '', class = 'defensive-subheader', title = 'Defence level' }, { tag = 'th', content = 'Weakness', class = 'defensive-subheader', colspan = '2' } } :addRow{ { tag = 'argd', content = 'armour' }, { tag = 'argd', content = 'defence', title = levels_disc }, { tag = 'argd', content = 'weakness', colspan = '2' } }

-- PARAMETER: explicit_weakness -- PARAMETER: aff_weakness | aff_melee | aff_ranged | aff_magic :addRow{ { tag = 'th', content = 'Affinities', class = 'defensive-subheader', colspan = '4' } } :addRow{ { tag = 'argh', content = 'explicit_weakness', title = 'Affinity value of the monster\'s explicit weakness' }, { tag = 'th', content = '', title = 'Affinity value of the monster against melee attacks' }, { tag = 'th', content = '', title = 'Affinity value of the monster against ranged attacks' }, { tag = 'th', content = '', 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 = '', title = 'Immune to poison' }, { tag = 'th', content = '', title = 'Immune to deflect' }, { tag = 'th', content = '', title = 'Immune to stun' }, { tag = 'th', content = '', 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 numbers function numargraw(arg) return tonumber(arg) or 0 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(combatxp, wepxp2h, wepxpmhandarmour, wepxpoh) local xp = string.gsub(combatxp or ,',',) local xp2h = string.gsub(wepxp2h or ,',',) local xpmh = string.gsub(wepxpmhandarmour or ,',',) local xpoh = string.gsub(wepxpoh or ,',',) if string.lower(xp) == 'n/a' then return 'N/A' else xp = tonumber(xp) end if string.lower(xp2h) ~= '' then xp2h = tonumber(xp2h) end if string.lower(xpmh) ~= '' then xpmh = tonumber(xpmh) end if string.lower(xpoh) ~= '' then xpoh = tonumber(xpoh) end if type(xp) == 'number' then local th,mh,oh if type(xp2h) == 'number' then th = xp2h else th = math.floor(xp * .06) end if type(xpmh) == 'number' then mh = xpmh else mh = math.floor(xp * .04) end if type(xpoh) == 'number' then oh = xpoh else oh = math.floor(xp * .02) end return string.format('%s / %s / %s',th,mh,oh) else return nil end end -- For true/false function boolargs(arg,argr) local arg_v = (arg or ''):find('%S') and arg or -1

local arg_ret

if arg_v == -1 then arg_ret = nil else arg_v = yesno(arg_v) local argf = ' %s ' local f1,f3 local f2 = string.gsub(argr,'_',' ')

if arg_v then f1 = '' f3 = '' else f1 = ' not' f3 = '' end

arg_ret = string.format(argf,f1,f2,f3) end

return arg_ret end

-- Poison function poisonarg(arg) arg = string.gsub(arg or ,',',) arg = string.lower(arg)

if arg == 'yes' then arg = ' ??? '	elseif tonumber(arg) then arg = ' '..tonumber(arg)..' ' else arg = ' ' 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('',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('',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('',_wk.image) else return nil end end

function aff_weakness_arg(aff,aff_r,weakness) -- split by commas local wk = mw.text.split(string.lower(weakness or ''),'%s*,%s*') local _wk = weaknesses[wk[1]] if _wk and _wk.text == 'Nothing' then return '-' else return numberargs(aff,aff_r) 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('',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 = tostring(attack_speed_bar(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 ''.. 'ERR ' 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', experience = 'Needs XP per kill', immune_to_stun = 'Missing immunity information', immune_to_poison = 'Missing immunity information', immune_to_deflect = 'Missing immunity information', immune_to_drain = 'Missing immunity information', attack = 'Missing combat skill levels', magic = 'Missing combat skill levels', ranged = 'Missing combat skill levels', defence = 'Missing combat skill levels', id = 'Needs ID' },		-- regex grep = { poisonous = { ['yes check%.svg'] = 'Poisonous monsters' }, aggressive = { ['yes check%.svg'] = 'Aggressive monsters' }, immune_to_stun = { ['yes check%.svg'] = 'Stun-immune monsters', ['x mark%.svg'] = 'Stun-susceptible monsters' }, immune_to_poison = { ['yes check%.svg'] = 'Poison-immune monsters', ['x mark%.svg'] = 'Poison-susceptible monsters' }, immune_to_deflect = { ['yes check%.svg'] = 'Deflect-immune monsters', ['x mark%.svg'] = 'Deflect-susceptible monsters' }, immune_to_drain = { ['yes check%.svg'] = 'Drain-immune monsters', ['x mark%.svg'] = 'Drain-susceptible monsters' }, },	}	-- 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

-- searches for n, v in pairs(cat_map.grep) do		for m, w in pairs(v) do			if args[n] then if string.find(string.lower(tostring(args[n].d) or ''),m) then table.insert(ret,w) end if args[n].switches then for _, x in ipairs(args[n].switches) do						if string.find(string.lower(tostring(x)),m) then table.insert(ret,w) end end end end end end

local levels = { args.level.d } if args.level.switches then for _, v in ipairs(args.level.switches) do			if v ~= infobox.nilParam 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.members.d:find('[Yy]es') then table.insert(ret,'P2P bestiary') elseif args.members.switches then for _, v in ipairs(args.members.switches) do			if v:find('[Yy]es') then table.insert(ret,'P2P 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		if (v ~= '') then ret[i] = string.format('',v) end end

return table.concat(ret,'') end

return p