Module:Sandbox/User:Towelcat/Infobox Monster

From Old School RuneScape Wiki
Jump to: navigation, search
Module documentation
This documentation is transcluded from Template:Module sandbox/doc. [edit] [purge]

This module is a sandbox for Towelcat. It can be used to test changes to existing modules, prototype new modules, or just experimenting with lua features.

Invocations of this sandbox should be kept in userspace; if the module is intended for use in other namespaces, it should be moved out of the sandbox into a normal module and template.

This default documentation can be overridden by creating the /doc subpage of this module, as normal.

--------------------------
-- Module for [[Template:Infobox Monster]]
------------------------
local p = {}

local onmain = require('Module:Mainonly').on_main
local commas = require('Module:Addcommas')._add
local infobox = require('Module:Infobox')
local attack_speed_bar = require('Module:Attack speed bar').make_bar
local signed = require('Module:Format eq stat').signed
VariablesLua = mw.ext.VariablesLua

local slayer_masters = { 'turael', 'krystilia', 'mazchna', 'vannaka', 'chaeldar', 'konar', 'nieve', 'steve', 'duradel' }
local attributes = {
	demon = '[[Demon (attribute)|Demon]]',
	dragon = '[[Draconic (attribute)|Draconic]]',
	fiery = '[[Fiery (attribute)|Fiery]]',
	kalphite = '[[Kalphite (attribute)|Kalphite]]',
	leafy = '[[Leafy (attribute)|Leafy]]',
	penance = '[[Penance (attribute)|Penance]]',
	shade = '[[Shade (attribute)|Shade]]',
	undead = '[[Undead (attribute)|Undead]]',
	vampyre1 = '[[Vampyre (attribute)|Vampyre (tier 1)]]',
	vampyre2 = '[[Vampyre (attribute)|Vampyre (tier 2)]]',
	vampyre3 = '[[Vampyre (attribute)|Vampyre (tier 3)]]',
	xerician = '[[Xerician (attribute)|Xerician]]',
}

function p.main(frame)
	local args = frame:getParent().args
	local ret = infobox.new(args)

	local numeric_args = {
		'att', 'str', 'def', 'range', 'mage',
	}
	for _, v in ipairs(numeric_args) do
		ret:defineParams{
			{ name = v, func = { name = numericarg, params = { v, v }, flag = { 'd', 'r' } } },
			{ name = v..'_smw', func = { name = tonumber, params = { v }, flag = { 'd' } } },
		}
	end

	local numeric_args_commas = {
		'combat', 'hitpoints', 'max hit',
	}
	for _, v in ipairs(numeric_args_commas) do
		ret:defineParams{
			{ name = v, func = { name = numericarg_commas, params = { v, v }, flag = { 'd', 'r' } } },
			{ name = v..'_smw', func = { name = tonumber, params = { v }, flag = { 'p' } } },
		}
	end

	local signed_percent_numeric_args = {}
	
	for _, v in ipairs(signed_percent_numeric_args) do
		ret:defineParams{
			{ name = v, func = { name = signedpercentnumericarg, params = { v, v }, flag = { 'd', 'r' } } },
			{ name = v..'_smw', func = { name = tonumber, params = { v }, flag = { 'p' } } },
		}
	end

	local signed_numeric_args = {
		'amagic', 'arange',
		'dstab', 'dslash', 'dcrush', 'dmagic', 'drange',
		'attbns', 'strbns', 'mbns', 'rngbns'
	}

	for _, v in ipairs(signed_numeric_args) do
		ret:defineParams{
			{ name = v, func = { name = signednumericarg, params = { v, v }, flag = { 'd', 'r' } } },
			{ name = v..'_smw', func = { name = tonumber, params = { v }, flag = { 'd' } } },
		}
	end

	ret:defineParams{
		{ name = 'name', func = 'name'},
		{ name = 'image', func = 'image' },
		{ name = 'image_smw', func = { name = image_smw, params = { 'image' }, flag = 'p' } },

		{ name = 'release', func = 'release' },
		{ name = 'removal', func = 'removal' },
		{ name = 'aka', func = 'has_content' },

		{ name = 'size', func = sizeparam },
		{ name = 'size_smw', func = { name = 'has_content', params = {'size'}, flag = 'p'} },
		{ name = 'members', func = 'has_content' },
		{ name = 'examine', func = 'has_content' },

		{ name = 'aggressive', func = 'has_content' },
		{ name = 'poisonous', func = 'has_content' },
		{ name = 'attributes', func = attributesarg },
		{ name = 'attributes_smw', func = { name = attributes_smw, params = { 'attributes' }, flag = 'p' } },
		{ name = 'attack style', func = 'has_content' },
		{ name = 'attack style_smw', func = { name = attack_style_smw, params = { 'attack style' }, flag = 'd' } },
		{ name = 'attack speed', func = attackspeedarg },
		{ name = 'attack speed_smw', func = { name = attackspeed_smw, params = { 'attack speed' }, flag = 'p' } },

		{ name = 'xpbonus', func = { name = signedpercentnumericarg_skipzero, params = { 'xpbonus', 'xpbonus' }, flag = { 'd', 'r' } } },
		{ name = 'xpbonus_smw', func = { name = tonumber, params = { 'xpbonus' }, flag = { 'p' } } },

		{ name = 'cat', func = 'has_content' },
		{ name = 'slaylvl', func = { name = 'has_content', params = {'slaylvl', 'None' }, flag = { 'd', 'r' } } },
		{ name = 'assignedby', func = assignedbyarg },
		{ name = 'slayxp', func = exp_arg },
		{ name = 'slayxp_smw', func = { name = tonumber, params = { 'slayxp' }, flag = { 'p' } } },

		{ name = 'immunepoison', func = { name = immunearg, params = {'immunepoison', 'poison', 'immunepoison'}, flag = { 'd', 'r', 'r' } } },
		{ name = 'immunepoison_smw', func = { name = immunearg_smw, params = {'immunepoison', 'immunepoison'}, flag = { 'p', 'r' } } },
		{ name = 'immunevenom', func = { name = immunearg, params = {'immunevenom', 'venom', 'immunevenom'}, flag = { 'd', 'r', 'r' } } },
		{ name = 'immunevenom_smw', func = { name = immunearg_smw, params = {'immunevenom', 'immunevenom'}, flag = { 'p', 'r' } } },

		-- not used; only for categories
		{ name = 'id', func = 'has_content' },
		{ name = 'version', func = 'has_content' },
		{ name = 'id_smw', func = { name = idsmw, params = { 'id' }, flag = 'p' } },
		{ name = 'monsterlabel', func = { name = returnmonster, params = {  } } },
		{ name = 'twisted', func = 'has_content' },
		{ name = 'twisteddisp', func = { name = twistedarg, params = { 'twisted' }, flag = 'p' } },
	}

	ret:defineLinks({ hide = true })

	ret:useSMWSubobject{
		members = 'Is members only',
		id_smw = 'NPC ID',
		image_smw = 'Image',
		combat_smw = 'Combat level',
		attributes_smw = 'Monster attribute',
		hitpoints_smw = 'Hitpoints',
		['max hit_smw'] = 'Max hit',
		slaylvl_smw = 'Slayer level',
		slayxp_smw = 'Slayer experience',
		att_smw = 'Attack level',
		str_smw = 'Strength level',
		def_smw = 'Defence level',
		range_smw = 'Ranged level',
		mage_smw = 'Magic level',
		amagic_smw = 'Magic attack bonus',
		arange_smw = 'Range attack bonus',
		dstab_smw = 'Stab defence bonus',
		dslash_smw = 'Slash defence bonus',
		dcrush_smw = 'Crush defence bonus',
		dmagic_smw = 'Magic defence bonus',
		drange_smw = 'Range defence bonus',
		attbns_smw = 'Attack bonus',
		strbns_smw = 'Strength bonus',
		rngbns_smw = 'Ranged Strength bonus',
		mbns_smw = 'Magic Damage bonus',
		version = 'Version anchor',
		name = 'Name',
		cat = 'Slayer category',
		immunepoison_smw = 'Immune to poison',
		immunevenom_smw = 'Immune to venom',
		['attack style_smw'] = 'Attack style',
		['attack speed_smw'] = 'Attack speed',
		xpbonus_smw = 'Experience bonus',
		monsterlabel = 'Page type',
		size_smw = 'Size',
	}

	ret:useSMWOne{
		members = 'All Is members only',
		id_smw = 'All NPC ID',
		image_smw = 'All Image',
		combat_smw = 'All Combat level',
		attributes_smw = 'All Monster attribute',
		hitpoints_smw = 'All Hitpoints',
		['max hit_smw'] = 'All Max hit',
		slaylvl_smw = 'All Slayer level',
		slayxp_smw = 'All Slayer experience',
		att_smw = 'All Attack level',
		str_smw = 'All Strength level',
		def_smw = 'All Defence level',
		range_smw = 'All Ranged level',
		mage_smw = 'All Magic level',
		amagic_smw = 'All Magic attack bonus',
		arange_smw = 'All Range attack bonus',
		dstab_smw = 'All Stab defence bonus',
		dslash_smw = 'All Slash defence bonus',
		dcrush_smw = 'All Crush defence bonus',
		dmagic_smw = 'All Magic defence bonus',
		drange_smw = 'All Range defence bonus',
		attbns_smw = 'All Attack bonus',
		strbns_smw = 'All Strength bonus',
		rngbns_smw = 'All Ranged Strength bonus',
		mbns_smw = 'All Magic Damage bonus',
		version = 'All Version anchor',
		name = 'All Name',
		cat = 'All Slayer category',
		immunepoison_smw = 'All Immune to poison',
		immunevenom_smw = 'All Immune to venom',
		['attack style_smw'] = 'All Attack style',
		['attack speed_smw'] = 'All Attack speed',
		xpbonus_smw = 'All Experience bonus',
		monsterlabel = 'All Page type',
		size_smw = 'All Size',
	}

	ret:customButtonPlacement(true)
	ret:create()
	ret:cleanParams()

	ret:addButtonsCaption()

	ret:defineName('Infobox Monster')
	ret:addClass('infobox-monster')

	ret:addRow{
		{ tag = 'argh', content = 'name', class='infobox-header', colspan = '30' }
	}

	:pad(30)
	:addRow{
		{ tag = 'argd', content = 'image', class='infobox-image infobox-full-width-content', colspan = '30' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = 'Released', colspan = '10' },
		{ tag = 'argd', content = 'release', colspan = '20' }
	}

	if ret:paramDefined('removal') then
		ret:addRow{
			{ tag = 'th', content = 'Removal', colspan = '10' },
			{ tag = 'argd', content = 'removal', colspan = '20' }
		}
	end

	if ret:paramDefined('aka') then
		ret:addRow{
			{ tag = 'th', content = 'Also called', colspan = '10' },
			{ tag = 'argd', content = 'aka', colspan = '20' }
		}
	end

	ret:addRow{
		{ tag = 'th', content = 'Members', colspan = '10' },
		{ tag = 'argd', content = 'members', colspan = '20' }
	}

	:addRow{
		{ tag = 'th', content = '[[Combat level]]', colspan = '10' },
		{ tag = 'argd', content = 'combat', colspan = '20' }
	}

	if ret:paramDefined('size') then
		ret:addRow{
			{ tag = 'th', content = '[[Size]]', colspan = '10' },
			{ tag = 'argd', content = 'size', colspan = '20' }
		}
	end

	ret:addRow{
		{ tag = 'th', content = '[[Examine]]', colspan= '10' },
		{ tag = 'argd', content = 'examine', colspan = '20' }
	}

	:pad(30)
	:addRow{
		{ tag = 'th', content = 'Combat info', colspan = '30', class = 'infobox-subheader' }
	}
	:pad(30)

	if ret:paramDefined('attributes', 'all') then
		ret:addRow{
			{ tag = 'th', content = '[[Monster attribute|Attribute]]', colspan = '10' },
			{ tag = 'argd', content = 'attributes', colspan = '20' }
		}
	end

	if ret:paramDefined('xpbonus', 'all') then
		ret:addRow{
			{ tag = 'th', content = '[[Experience bonus|XP bonus]]', colspan = '10' },
			{ tag = 'argd', content = 'xpbonus', colspan = '20' }
		}
	end

	ret:addRow{
		{ tag = 'th', content = '[[Monster maximum hit|Max hit]]', colspan = '10' },
		{ tag = 'argd', content = 'max hit', colspan = '20' }
	}

	:addRow{
		{ tag = 'th', content = '[[Aggressiveness|Aggressive]]', colspan = '10' },
		{ tag = 'argd', content = 'aggressive', colspan = '20' }
	}

	:addRow{
		{ tag = 'th', content = '[[Poisonous|Poison]]', colspan = '10' },
		{ tag = 'argd', content = 'poisonous', colspan = '20' }
	}

	:addRow{
		{ tag = 'th', content = '[[Combat Options|Attack style]]', colspan = '10' },
		{ tag = 'argd', content = 'attack style', colspan = '20' }
	}

	:addRow{
		{ tag = 'th', content = '[[Monster attack speed|Attack speed]]', colspan = '10' },
		{ tag = 'argd', content = 'attack speed', colspan = '20' }
	}

	:pad(30)

	-- If a monster is assigned or has a slayer level, include slayer info
	local slaylvl_defined = ret:paramGrep('slaylvl', function(x) return string.lower(x or 'none') ~= 'none' end)
	if ret:paramDefined('assignedby', 'all') or slaylvl_defined then
		ret:addRow{
			{ tag = 'th', content = '[[File:Slayer icon.png|link=Slayer]] [[Slayer|Slayer info]]', colspan = '30', class = 'infobox-subheader' }
		}
		:pad(30)
		:addRow{
			{ tag = 'th', content = '[[Slayer|Slayer level]]', colspan = '10' },
			{ tag = 'argd', content = 'slaylvl', colspan = '20' }
		}

		-- If a monster is assigned, include assignment info
		if ret:paramDefined('assignedby', 'all') then
			ret:addRow{
				{ tag = 'th', content = '[[Slayer|Slayer XP]]', colspan = '10' },
				{ tag = 'argd', content = 'slayxp', colspan = '20' }
			}

			:addRow{
				{ tag = 'th', content = '[[Slayer task#List of assignments|Category]]', colspan = '10' },
				{ tag = 'argd', content = 'cat', colspan = '20' }
			}

			:addRow{
				{ tag = 'th', content = '[[Slayer master|Assigned by]]', colspan = '10' },
				{ tag = 'argd', content = 'assignedby', colspan = '20' }
			}
		else
			ret:addRow{
				{ tag = 'th', content = '[[Slayer master|Assigned by]]', colspan = '10' },
				{ tag = 'td', content = 'Not assigned', colspan = '20' }
			}
		end

		ret:pad(30)
	end

	ret:addRow{
		{ tag = 'th', content = '[[File:Combat icon.png|link=Combat]] [[Combat|Combat stats]]', colspan = '30', class = 'infobox-subheader' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = '[[File:Hitpoints icon.png|link=Hitpoints]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Attack icon.png|link=Attack]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Strength icon.png|link=Strength]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Defence icon.png|link=Defence]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic icon.png|link=Magic]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged icon.png|link=Ranged]]', colspan = '5', class = 'infobox-nested' }
	}

	:addRow{
		{ tag = 'argd', content = 'hitpoints', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'att', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'str', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'def', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'mage', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'range', colspan = '5', class = 'infobox-nested' }
	}

	:pad(30)
	:addRow{
		{ tag = 'th', content = '[[File:Attack icon.png|link=Attack]] [[Attack|Aggressive stats]]', colspan = '30', class = 'infobox-subheader' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = '[[File:Attack icon.png|link=Attack|Monster attack bonus]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Strength icon.png|link=Strength|Monster strength bonus]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic icon.png|link=Magic]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic Damage icon.png|link=Magic damage|Monster magic strength bonus]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged icon.png|link=Ranged]]', colspan = '5', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged Strength icon.png|link=Ranged Strength|Monster ranged strength bonus]]', colspan = '5', class = 'infobox-nested' },
	}

	:addRow{
		{ tag = 'argd', content = 'attbns', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'strbns', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'amagic', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'mbns', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'arange', colspan = '5', class = 'infobox-nested' },
		{ tag = 'argd', content = 'rngbns', colspan = '5', class = 'infobox-nested' },
	}

	:pad(30)
	:addRow{
		{ tag = 'th', content = '[[File:Defence icon.png|link=Defence]] [[Defence|Defensive stats]]', colspan = '30', class = 'infobox-subheader' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = '[[File:White dagger.png|link=Stab]]', colspan = '6', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White scimitar.png|link=Slash]]', colspan = '6', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White warhammer.png|link=Crush]]', colspan = '6', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic icon.png|link=Magic]]', colspan = '6', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged icon.png|link=Ranged]]', colspan = '6', class = 'infobox-nested' }
	}

	:addRow{
		{ tag = 'argd', content = 'dstab', colspan = '6', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dslash', colspan = '6', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dcrush', colspan = '6', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dmagic', colspan = '6', class = 'infobox-nested' },
		{ tag = 'argd', content = 'drange', colspan = '6', class = 'infobox-nested' }
	}

	:pad(30)
	:addRow{
		{ tag = 'th', content = 'Immunities', colspan = '30', class = 'infobox-subheader' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = '[[File:Poison hitsplat.png|link=Poison]]', colspan = '15', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Venom hitsplat.png|link=Venom]]', colspan = '15', class = 'infobox-nested' }
	}

	:addRow{
		{ tag = 'argd', content = 'immunepoison', colspan = '15', class = 'infobox-nested' },
		{ tag = 'argd', content = 'immunevenom', colspan = '15', class = 'infobox-nested' }
	}
	:pad(30)

	:addRow{
		{ tag = 'th', content = 'Advanced data', class = 'infobox-subheader', colspan = '30' },
		meta = {addClass = 'advanced-data'}
	}
	:pad(30, 'advanced-data')
	:addRow{
		{ tag = 'th', content = 'Monster ID', colspan = '10' },
		{ tag = 'argd', content = 'id',  colspan = '20' },
		meta = {addClass = 'advanced-data'}
	}
	if ret:paramDefined('twisteddisp', 'all') then
		ret:addRow{
			{ tag = 'th', content = '[[Twisted League]]', colspan = '10' },
			{ tag = 'argd', content = 'twisteddisp', colspan = '20' },
			meta = {addClass = 'advanced-data'}
		}
	end
	ret:pad(30, 'advanced-data')

	if onmain() then
		local a1 = ret:param('all')
		local a2 = ret:categoryData()
		ret:wikitext(addcategories(a1, a2))
	end
	return ret:tostring()
end

function numericarg(arg, arg_name)
	if not infobox.isDefined(arg) then
		return nil
	end
	return arg
end

function numericarg_commas(arg, arg_name)
	if not infobox.isDefined(arg) then
		return nil
	end
	local n = tonumber(arg)
	if n == nil then
		return arg
	else
		return commas(tonumber(arg))
	end
end

-- If the arg is numeric, return the signed version (starts with + or -)
function signednumericarg(arg, arg_name)
	local _arg = numericarg(arg, arg_name)
	if tonumber(_arg) ~= nil then
		return signed(_arg)
	end
	return nil
end

-- Sign the arg and append a percent sign
function signedpercentnumericarg(arg, arg_name)
	local _arg = signednumericarg(arg, arg_name)
	if _arg ~= nil then
		return _arg..'%'
	end
	return nil
end

-- Sign the arg and append a percent sign, except skip if 0
function signedpercentnumericarg_skipzero(arg, arg_name)
	if arg == "0" then
		return nil
	end
	return signedpercentnumericarg(arg, arg_name)
end

function returnmonster()
	return "Monster"
end

function attributesarg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' then
		return 'None'
	end

	local result = {}
	for attribute_i in string.gmatch(arg, "[^,]+") do
		local trimmed = attribute_i:gsub("^%s*(.-)%s*$", "%1")
		if attributes[trimmed] then
			table.insert(result, attributes[trimmed])
		end
	end

	if #result > 0 then
		return table.concat(result, ', ')
	else
		return 'None'
	end
end

-- Returns list of types in smw format
function attributes_smw(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' then
		return nil
	end

	local result = {}
	for attribute_i in string.gmatch(arg, "[^,]+") do
		local trimmed = attribute_i:gsub("^%s*(.-)%s*$", "%1")
		if attributes[trimmed] then
			table.insert(result, trimmed)
		end
	end

	if #result > 0 then
		return table.concat(result, '&&SPLITPOINT&&')
	else
		return nil
	end
end

-- Generate pics for defined slayer masters, or return nil if undefined
function assignedbyarg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' then
		return 'Not assigned'
	end

	local result = {}
	for i, slayer_master in ipairs(slayer_masters) do
		if string.match(arg, slayer_master) then
			table.insert(result, string.format('[[File:%s chathead.png|x40px|link=%s]]', slayer_master, slayer_master))
		end
	end

	if #result > 0 then
		return table.concat(result, ' ')
	else
		return nil
	end
end

function exp_arg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	if not tonumber(arg) then
		return arg
	end
	return string.format('<span class="infobox-quantity" data-val-each="%s"><span class="infobox-quantity-replace">%s</span> xp</span>', arg, commas(arg))
end

function immunearg_smw(arg, arg_name)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' or arg == 'not immune' then
		return 'Not immune'
	elseif arg == 'yes' or arg == 'immune' then
		return 'Immune'
	elseif arg:sub(1, #'poison') == 'poison' then
		return 'Poisons'
	else
		return badarg(arg_name, "should be 'yes' or 'no'.")
	end
end

function immunearg(arg, immunity, arg_name)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' or arg == 'not immune' then
		return 'Not immune to '..immunity
	elseif arg == 'yes' or arg == 'immune' then
		return 'Immune to '..immunity
	elseif arg:sub(1, #'poison') == 'poison' then
		return '<span '..
			'title="This monster will be poisoned instead of envenomed." '..
			'style="cursor:help; border-bottom:1px dotted;">'..
			'Poison</span>'
	else
		return badarg(arg_name, "should be 'yes' or 'no'.")
	end
end

function image_smw(arg)
	local _img = string.match(arg, "File:.-%.png")
	return _img
end

function attack_style_smw(arg)
	if infobox.isDefined(arg) then
		local _arg = string.gsub(arg,'[%[%]]', '')
		return _arg
	end
	return nil
end

function attackspeedarg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' then
		return 'Does not attack'
	end
	return attack_speed_bar(arg)
end

function attackspeed_smw(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	local arg = string.lower(arg)
	if arg == 'no' then
		return -1
	end
	return arg
end

function idsmw(id)
	if infobox.isDefined(id) then
		local r = string.gsub(id, ',', '&&SPLITPOINT&&')
		return r
	end
	return nil
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 sizeparam(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	return string.format('%sx%s', arg, arg)
end

function twistedarg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end

	if string.lower(arg or '') == 'yes' then
		return "[[File:Twisted League icon.png|20x20px|This content is present in Twisted League.|link=Twisted League]] Yes"
	else
		return 'No'
	end
end

function addcategories(args, catargs)
	local ret = { 'Monsters' }

 	-- Add the associated category if the parameter has content
	local defined_args = {
		aka = 'Pages with AKA',
		aspeed = 'Pages with aspeed'
	}
	for n, v in pairs(defined_args) do
		if catargs[n] and catargs[n].one_defined then
			table.insert(ret, v)
		end
	end

 	-- Add the associated category if the parameter doesn't have content
 	local notdefined_args = {
 		image = 'Needs image',
 		members = 'Needs members status',
 		release = 'Needs release date',
 		examine = 'Needs examine added',
 		combat = 'Needs combat level',
 		id = 'Needs ID'
 	}
	for n, v in pairs(notdefined_args) do
		if catargs[n] and catargs[n].all_defined == false then
			table.insert(ret, v)
		end
	end

	-- Adds Category:Needs Monster Examine if any of these are not defined
	local monster_examine_args = {
		'att', 'str', 'def', 'range', 'mage',
		'amagic', 'arange',
		'dstab', 'dslash', 'dcrush', 'dmagic', 'drange',
		'attbns', 'strbns', 'rngbns', 'mbns',
		'immunepoison', 'immunevenom', 'attack speed'
	}
	for _, arg in ipairs(monster_examine_args) do
		if not catargs[arg] or not catargs[arg].all_defined then
			table.insert(ret, 'Needs Monster Examine')
			break
		end
	end

	-- Adds Category:Needs slayer information if slayer info is required
	-- but not all args are defined
	local slayer_args = {
		'slaylvl', 'slayxp', 'cat'
	}
	if catargs['assignedby'].one_defined then
		table.insert(ret, 'Slayer monsters')
		for i, arg in ipairs(slayer_args) do
			if not catargs[arg] or not catargs[arg].all_defined then
				table.insert(ret, 'Needs slayer information')
				break
			end
		end
	end

	if args['twisted'] then
		if string.lower(args['twisted'].d or '') == 'yes' then
			table.insert(ret, 'Monsters accessible in Twisted League')
			VariablesLua.vardefine('is_twisted', 'Yes')
		end
		if args['twisted'].switches then
			for i, twisted_i in ipairs(args['twisted'].switches) do
				if string.lower(twisted_i or '') == 'yes' then
					table.insert(ret, 'Monsters accessible in Twisted League')
					if args['version'].switches then
						local suffix = args['version'].switches[i] or i
						VariablesLua.vardefine('is_twisted_'..suffix, 'Yes')
						VariablesLua.vardefine('is_twisted', 'Yes')
					end
				end
			end
		end
	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