Module:Infobox ModStats new

From [N8]
Jump to navigation Jump to search

Documentation for this module may be created at Module:Infobox ModStats new/doc

-- <nowiki>
--[=[
-- Implements [[Template:Infobox ModStats]]
--]=]

local p = {}
local infobox = require('Module:Infobox')
local onmain = require('Module:Mainonly').on_main
local paramtest = require('Module:Paramtest')
local yesno = require('Module:Yesno')
local editbutton = require('Module:Edit button')
local commas = require('Module:Addcommas')._add

-- Accepted slot names
local slots = {
	head = 'head',
	neck = 'neck',
	back = 'back',
	cape = 'back',
	torso = 'torso',
	body = 'torso',
	legs = 'legs',
	hands = 'hands',
	feet = 'feet',
	ammo = 'ammo',
	ring = 'ring',
	aura = 'aura',
	pocket = 'pocket',
	sigil = 'sigil',
	main = 'main hand weapon',
	['main hand'] = 'main hand weapon',
	['main-hand'] = 'main hand weapon',
	mainhand = 'main hand weapon',
	weapon = 'main hand weapon',
	['2h'] = '2h weapon',
	['off-hand'] = 'off-hand',
	offhand = 'off-hand',
	shield = 'off-hand',
	['off-hand weapon'] = 'off-hand weapon',
	['offhand weapon'] = 'off-hand weapon',
	ohw = 'off-hand weapon',
	set = 'e',
	none = 'e'
}

-- Categories for slots
local slot_cats = {
	head = 'Head slot items',
	neck = 'Neck slot items',
	back = 'Back slot items',
	torso = 'Torso slot items',
	legs = 'Legs slot items',
	hands = 'Hand slot items',
	feet = 'Feet slot items',
	ammo = 'Ammunition slot items',
	ring = 'Rings',
	aura = 'Auras',
	pocket = 'Pocket slot items',
	sigil = 'Sigil slot items',
	['main hand weapon'] = 'Main hand slot items',
	['2h weapon'] = 'Two-handed slot items',
	['off-hand'] = 'Off-hand slot items',
	['off-hand weapon'] = 'Off-hand slot weapon',
	e = ''
}

-- Images used for slot display
local slot_images = {
	head = '[[File:Head slot.png|link=Head slot]]',
	ammo = '[[File:Ammo slot.png|link=Ammunition slot]]',
	neck = '[[File:Neck slot.png|link=Neck slot]]',
	back = '[[File:Back slot.png|link=Back slot]]',
	['main hand weapon'] = '[[File:Main hand slot.png|link=Main hand slot]]',
	['2h weapon'] = '[[File:2h slot.png|link=Two-handed slot]]',
	torso = '[[File:Torso slot.png|link=Torso slot]]',
	['off-hand'] = '[[File:Off-hand slot.png|link=Off-hand slot]]',
	['off-hand weapon'] = '[[File:Off-hand slot.png|link=Off-hand slot]]',
	legs = '[[File:Legs slot.png|link=Legs slot]]',
	hands = '[[File:Gloves slot.png|link=Hands slot]]',
	feet = '[[File:Feet slot.png|link=Feet slot]]',
	ring = '[[File:Ring slot.png|link=Ring slot]]',
	aura = '[[File:Aura slot.png|link=Aura slot]]',
	pocket = '[[File:Pocket slot.png|link=Pocket slot]]',
	sigil = '[[File:Sigil slot.png|link=Sigil slot]]',
	e = 'None'
}

-- 'invention slots'
local inv_slots = {
	['main hand weapon'] = 'mh',
	['2h weapon'] = '2h',
	['off-hand'] = 'oh',
	['off-hand weapon'] = 'oh',
	shield = 'shield',
	torso = 'body',
	legs = 'legs',
	tool = 'tool',
	t = 'tool'
}

-- Accepted class names
local classes = {
	melee = 'melee',
	ranged = 'ranged',
	ranging = 'ranged',
	range = 'ranged',
	magic = 'magic',
	mage = 'magic',
	all = 'all',
	hybrid = 'hybrid',
	none = 'none',
	['n/a'] = 'none'
}

-- Classes with images
local class_img = {
	melee = '[[File:Attack.png|x24px|link=Melee]]',
	ranged = '[[File:Ranged.png|x24px|link=Ranged]]',
	magic = '[[File:Magic.png|x24px|link=Magic]]',
	hybrid = '[[File:CombatSwords.png|x24px|link=Armour#Hybrid]]',
	all = '[[File:CombatSwords.png|x24px|link=Armour#All]]',
	none = ''
}

local class_cats = {
	melee = 'Melee',
	magic = 'Magic',
	ranged = 'Ranged',
	hybrid = 'Hybrid',
	all = 'Hybrid',
	none = 'Typeless',
}

-- Accepted style names
local styles = {
	stab = 'stab',
	stabbing = 'stab',
	slash = 'slash',
	slashing = 'slash',
	crush = 'crush',
	crushing = 'crush',
	arrow = 'arrows',
	arrows = 'arrows',
	bolt = 'bolts',
	bolts = 'bolts',
	thrown = 'thrown',
	throwing = 'thrown',
	magic = 'spell-casting',
	spell = 'spell-casting',
	spells = 'spell-casting',
	none = 'none',
	['n/a'] = 'none'
}

-- Categories for styles
local style_cats = {
	stab = 'Stab weapons',
	slash = 'Slash weapons',
	crush = 'Crush weapons',
	thrown = 'Thrown weapons'
}

local types = {
	power = 'Power armour',
	['power armour'] = 'Power armour',
	tank = 'Tank armour',
	['tank armour'] = 'Tank armour',
	pvp = 'PvP armour',
	['pvp armour'] = 'PvP armour',
	shieldbow = 'Shieldbow',
	shortbow = 'Shortbow',
	defender = 'Defender',
	repriser = 'Repriser',
	rebounder = 'Rebounder',
	halberd = 'Halberd',
	shield = 'Shield',
	chargebow = 'Chargebow',
	cosmetic = 'Cosmetic',
	['prevents attack'] = 'Prevents attack',
	-- weapon diversity
	dagger = 'Dagger',
	spear = 'Spear',
	scimitar = 'Scimitar',
	['2h sword'] = '2h sword',
	['two handed sword'] = '2h sword',
	mace = 'Mace',
	maul = 'Maul',
	['1h crossbow'] = 'Crossbow',
	['one handed crossbow'] = 'Crossbow',
	['crossbow'] = 'Crossbow',
	['2h crossbow'] = '2h crossbow',
	['two handed crossbow'] = '2h crossbow',
	['throwing knife'] = 'Throwing knife',
	['throwing axe'] = 'Throwing axe',
}

local type_cats = {
	['cosmetic'] = { '[[Cosmetic]]' },
	['power armour'] = { '[[Power armour]]', 'Power armour' },
	['tank armour'] = { '[[Tank armour]]', 'Tank armour' },
	['pvp armour'] = { '[[PvP armour]]', 'PvP armour' },
	shieldbow = { '[[Shieldbow (bow type)|Shieldbow]]', 'Shieldbows' },
	shortbow = { '[[Shortbow (bow type)|Shortbow]]','Shortbows' },
	defender = { '[[Defender]]', 'Defenders' },
	repriser = { '[[Repriser]]', 'Defenders' },
	rebounder = { '[[Rebounder]]', 'Defenders' },
	halberd = { '[[Halberd]]', 'Halberds' },
	shield = { '[[Shield]]', 'Shields' },
	chargebow = { '[[Chargebow (bow type)|Chargebow]]', 'Chargebows' },
	['prevents attack'] = { 'Prevents attack', 'Items which prevent attack' },
	dagger = { '[[Dagger]]', 'Daggers' },
	spear = { '[[Spear]]', 'Spears' },
	scimitar = { '[[Scimitar]]', 'Scimitars' },
	['2h sword'] = { '[[Two-handed sword|2h sword]]', 'Two-handed swords'},
	mace = { '[[Mace]]', 'Maces' },
	maul = { '[[Maul]]', 'Mauls' },
	['crossbow'] = { '[[Crossbow (weapon type)|Crossbow]]', 'One-handed crossbows' },
	['2h crossbow'] = { '[[Two-handed crossbow|2h crossbow]]', 'Two-handed crossbows' },
	['throwing knife'] = { '[[Throwing knife]]', 'Throwing knives' },
	['throwing axe'] = { '[[Throwing axes|Throwing axe]]', 'Throwing axes' },
}

local reduction_types = { -- accepts type or class
	['tank armour'] = 'tank',
	['power armour'] = 'other',
	['pvp armour'] = 'pvp',
	['shield'] = 'shield',
	['shieldbow'] = 'shield',
	['hybrid'] = 'other',
	['all'] = 'other'
}

local reductions = { -- type -> slot -> pvm, pvp multipliers
	tank = {
		head = {pvm = 0.02, pvp = 0.0375},
		torso = {pvm = 0.02, pvp = 0.06},
		legs = {pvm = 0.02, pvp = 0.0525},
		hands = {pvm = 0.02, pvp = 0},
		feet = {pvm = 0.02, pvp = 0}
	},
	pvp = {
		head = {pvm = 0, pvp = 0.0375},
		torso = {pvm = 0, pvp = 0.06},
		legs = {pvm = 0, pvp = 0.0525}
	},
	other = { --power, hybrid, all
		head = {pvm = 0, pvp = 0.01875},
		torso = {pvm = 0, pvp = 0.03},
		legs = {pvm = 0, pvp = 0.02625}
	},
	shield = { --because hybrid shields are a thing
		 -- for consistency to continue allowing reduction.type.slot.pvx
		['off-hand'] = {pvm = 0.1, pvp = 0},
		['2h weapon'] = {pvm = 0.1, pvp = 0}, --shieldbows pls
	}
}


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

	ret:defineParams{
		{ name = 'image', func = { name = imagearg, params = { 'image' }, flag = 'p' } },
		{ name = 'altimage', func = { name = imagearg, params = { 'altimage' }, flag = 'p' } },
		{ name = 'noimgcat', func = { name = noimgcatarg, params = { 'noimgcat', 'image' }, flag='p' } },
		{ name = 'requirements', func = requirementsarg },
		{ name = 'class', func = { name = lookuparg, params = { classes, 'class' }, flag = { 'r', 'd' } } },
		{ name = 'classimg', func = { name = lookuparg, params = { class_img, 'class' }, flag = { 'r', 'd' } } },
		{ name = 'classstr', func = { name = classstrarg, params = {'class', 'classimg' }, flag = 'd' } },
		{ name = 'class_smw', func = { name = classsmwarg, params = { 'class' }, flag = { 'd' } } },
		{ name = 'tier', func = tierarg },
		{ name = 'type', func = { name = lookupmultiarg, params = { types, 'type' }, flag = { 'r', 'd' } } },
		{ name = 'type_disp', func = { name = type_display, params = { 'type' }, flag = { 'd' } } },
		{ name = 'slot', func = { name = lookuparg, params = { slots, 'slot' }, flag = { 'r', 'd' } } },
		{ name = 'slotimg', func = { name = lookuparg, params = { slot_images, 'slot' }, flag = { 'r', 'd' } } },
		{ name = 'isweapon', func = { name = isweaponarg, params = { 'slot', 'type' }, flag = d } },
		{ name = 'style', func = { name = stylearg, params = { 'style', 'isweapon' }, flag = 'd' } },
		{ name = 'attackrange', func = { name = attackrangearg, params = { 'isweapon', 'attack_range', 'attack range', 'attackrange' }, flag = { 'd', 'p', 'p', 'p' } } },
		{ name = 'attackrangesmw', func = { name = attackrangesmwarg, params = { 'attackrange' }, flag = 'd' } },
		{ name = 'damage', func = fnumbers },
		{ name = 'accuracy', func = fnumbers },
		{ name = 'maindamage', func = { name = mainoffdamarg, params = { 'damage', 'slot', 'damage', {'main hand weapon', '2h weapon', 'ammo'} }, flag = { 'd', 'd', 'p', 'r' } } },
		{ name = 'mainaccuracy', func = { name = mainoffaccarg, params = { 'accuracy', 'slot', {'main hand weapon', '2h weapon'} }, flag = { 'd', 'd', 'r' } } },
		{ name = 'offdamage', func = { name = mainoffdamarg, params = { 'damage', 'slot', 'damage', {'off-hand weapon'} }, flag = { 'd', 'd', 'p', 'r' } } },
		{ name = 'offaccuracy', func = { name = mainoffaccarg, params = { 'accuracy', 'slot', {'off-hand weapon'} }, flag = { 'd', 'd', 'r' } } },
		{ name = 'armour', func = armourarg },
		{ name = 'life', func = fnumbers2 },
		{ name = 'prayer', func = fnumbers2 },
		{ name = 'magic', func = fnumbers },
		{ name = 'strength', func = fnumbers },
		{ name = 'ranged', func = fnumbers },
		{ name = 'magicstr', func = { name = stylebonusesarg, params = { 'magic', 'Magic', 'magic' }, flag = { 'd', 'r', 'p' } } },
		{ name = 'strengthstr', func = { name = stylebonusesarg, params = { 'strength', 'Strength', 'strength' }, flag = { 'd', 'r', 'p' } } },
		{ name = 'rangedstr', func = { name = stylebonusesarg, params = { 'ranged', 'Ranged', 'ranged' }, flag = { 'd', 'r', 'p' } } },
		{ name = 'reductionlevel', func = { name = reductionlevelarg, params = { 'reductionlevel', 'tier', 'requirements' }, flag = { 'p', 'd', 'd' } } },
		{ name = 'pvmreduction', func = { name = reductionarg, params = { 'reductionlevel', 'pvmReduction', 'type', 'slot', 'class', 'pvm' }, flag = { 'd', 'p', 'd', 'd', 'd', 'r' } } },
		{ name = 'pvpreduction', func = { name = reductionarg, params = { 'reductionlevel', 'pvpReduction', 'type', 'slot', 'class', 'pvp' }, flag = { 'd', 'p', 'd', 'd', 'd', 'r' } } },
		{ name = 'pvmreductionstr', func = { name = reductionstrarg, params = { 'pvmreduction', "'''PvM: '''" }, flag = { 'd', 'r' } } },
		{ name = 'pvpreductionstr', func = { name = reductionstrarg, params = { 'pvpreduction',  "'''PvP: '''" }, flag = { 'd', 'r' } } },
		{ name = 'speedraw', func = { name = speedrawarg, params = { 'isweapon', 'speed', 'aspeed' }, flag = { 'd', 'p', 'p' } } },
		{ name = 'speed', func = { name = speedarg, params = { 'speedraw' }, flag = 'd' } },
		{ name = 'layouttype', func = { name = layoutarg, params = { 'image', 'altimage' }, flag ='d' } },
		{ name = 'isrecolour', func = { name = recolourarg, params = { 'isrecolour' }, flag = 'd' } },
		
		{ name = 'intbonus', func = { name = intbonusarg, params = { 'armour', 'damage', 'strength', 'ranged', 'magic' }, flag ='p' } },
		
		{ name = 'tier_smw', func = { name = smwtierarg, params = { 'tier' }, flag = 'd' } },
		{ name = 'style_smw', func = { name = smwstylearg, params = { 'style' }, flag = 'd' } },
		{ name = 'damage_smw', func = { name = smwnumbers, params = { 'damage' }, flag = 'd' } },
		{ name = 'accuracy_smw', func = { name = smwnumbers, params = { 'accuracy' }, flag = 'd' } },
		{ name = 'armour_smw', func = { name = smwnumbers2, params = { 'armour' }, flag = 'd' } },
		{ name = 'life_smw', func = { name = smwnumbers2, params = { 'life' }, flag = 'd' } },
		{ name = 'prayer_smw', func = { name = smwnumbers2, params = { 'prayer' }, flag = 'd' } },
		{ name = 'magic_smw', func = { name = smwnumbers, params = { 'magic' }, flag = 'd' } },
		{ name = 'strength_smw', func = { name = smwnumbers, params = { 'strength' }, flag = 'd' } },
		{ name = 'ranged_smw', func = { name = smwnumbers, params = { 'ranged' }, flag = 'd' } },
	}

	ret:setMaxButtons(7)
	ret:setAddRSWInfoboxClass(false)
	ret:create()
	ret:cleanParams()
	ret:customButtonPlacement(true)
	
	ret:defineLinks({
		colspan = 12,
		links = { 
			{ 'Template:Infobox ModStats/FAQ', 'FAQ' },
			{ 'Template:Infobox ModStats', 'docs' }
		}
	})
	ret:css({
		width = '450px',
		float = 'none',
		margin = '0.8em 0'
	})
	
	--smw here later
	ret:useSMWSubobject({
		smwJSON = 'Equipment JSON',
		class_smw = 'Combat class',
		slot = 'Equipment slot',
		type = 'Equipment type',
		damage_smw = 'Weapon damage',
		accuracy_smw = 'Weapon accuracy',
		style_smw = 'Attack style',
		attackrangesmw = 'Attack range',
		armour_smw = 'Equipment armour',
		life_smw = 'Equipment life points',
		speedraw = 'Weapon attack speed',
		prayer_smw = 'Prayer bonus',
		strength_smw = 'Strength bonus',
		ranged_smw = 'Ranged bonus',
		magic_smw = 'Magic bonus',
		tier_smw = 'Equipment tier',
		charges = 'Degradation charges',
		invtier = 'Invention tier',
		isrecolour = 'Is cosmetic recolour',
		intbonus = 'Has integer bonuses'
	})
	
	ret:addButtonsCaption()

	ret:defineName('Infobox ModStats')
	ret:addClass('infobox-modstats wikitable')
	ret:tag('caption'):wikitext("'''[[Combat Stats]]'''")
	
	local layout = ret:param('layouttype', 'r')
	p['_main'..layout](ret)

	if onmain() then
		local a1 = ret:param('all')
		local a2 = ret:categoryData()
		local cats = addcategories(ret,a1,a2)
		ret._categories_test = cats
		ret:wikitext(cats)
	end

	return ret:tostring()

end

-- 1 image
function p._mainA(ret)
	local row1 = {
		{ tag = 'th', content = 'Requirements', colspan = 6, class = 'combat-requirements' },
		{ tag = 'argd', content = 'image', colspan = 6, rowspan = 11, class = 'infobox-image bordered-image' }
	}
	local row2 = {
		{ tag = 'argd', content = 'requirements', colspan = 6, css = { ['max-width'] = '240px' } } -- stop skill reqs getting too wide
	}
	if ret:param('speed', 'r') ~= 'no' then
		row1[2].rowspan = 12
	end
	
	if infobox.isDefined(ret:param('degradetype', 'r')) then
		row1[1].colspan = 3
		row2[1].colspan = 3
		table.insert(row1, 2, { tag = 'argh', content = 'degradeheader', colspan = 3 })
		table.insert(row2, 2, { tag = 'argd', content = 'degradestr', colspan = 3 })
		if ret:param('degradetype', 'r') == 'invention' then
			ret:append(tostring(ret:param('invdegrade_div', 'r')))
		end
	end
	ret:addRow(row1):addRow(row2)

	-- class, slot
	ret:addRow{
		{ tag = 'th', content = 'Class', colspan = 3 },
		{ tag = 'th', content = 'Slot', colspan = 3 }
	}
	:addRow{
		{ tag = 'argd', content = 'classstr', colspan = 3 },
		{ tag = 'argd', content = 'slotimg', colspan = 3 }
	}

	if ret:paramDefined('type') then
		-- tier, type
		ret:addRow{
			{ tag = 'th', content = '[[Equipment tier|Tier]]', colspan = 3 },
			{ tag = 'th', content = 'Type', colspan = 3 }
		}
		:addRow{
			{ tag = 'argd', content = 'tier', colspan = 3 },
			{ tag = 'argd', content = 'type_disp', colspan = 3 }
		}
	else
		ret:addRow{
			{ tag = 'th', content = '[[Equipment tier|Tier]]', colspan = 6 }
		}
		:addRow{
			{ tag = 'argd', content = 'tier', colspan = 6 }
		}
	end

	-- weapons section
	ret:addRow{ --headers
		{ tag = 'th', content = 'Weapons', colspan = 2, css = { width = '33%' } },
		{ tag = 'th', content = 'Main', colspan = 2, css = { width = '33%' } },
		{ tag = 'th', content = 'Off', colspan = 2, css = { width = '33%' } }
	}
	:addRow{ -- damage
		{ tag = 'th', content = '[[Ability damage|Damage]]', colspan = 2 },
		{ tag = 'argd', content = 'maindamage', colspan = 2 },
		{ tag = 'argd', content = 'offdamage', colspan = 2 }
	}
	:addRow{ -- accuracy
		{ tag = 'th', content = '[[Hit chance|Accuracy]]', colspan = 2 },
		{ tag = 'argd', content = 'mainaccuracy', colspan = 2 },
		{ tag = 'argd', content = 'offaccuracy', colspan = 2 }
	}
	:addRow{ -- style
		{ tag = 'th', content = '[[Attack types|Style]]', colspan = 2 },
		{ tag = 'argd', content = 'style', colspan = 4 }
	}
	:addRow{ -- range
		{ tag = 'th', content = '[[Attack range|Range]]', colspan = 2 },
		{ tag = 'argd', content = 'attackrange', colspan = 4 }
	}
	
	if ret:param('speed', 'r') ~= 'no' then
		ret:addRow{ -- rate (formely speed)
			{ tag = 'th', content = '[[Attack rate|Rate]]', colspan = 2 },
			{ tag = 'argd', content = 'speed', colspan = 4, css = { padding = '0.2em' } }
		}
	end

	-- attributes / bottom section
	ret:addRow{ -- attribute & reduction headers
		{ tag = 'th', content = 'Attributes', colspan = 6 },
		{ tag = 'th', content = '[[Damage reduction]]', colspan = 6, css = { ['min-width'] = '225px' } }, -- force as wide as other side (3x th = 210 from css, + paddings and borders = 225)
	}:addRow{ -- armour, reductions
		{ tag = 'th', content = skillpic('Defence') .. ' Armour', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'armour', colspan = 2, css = { ['text-align'] = 'right' } },
		{ tag = 'argd', content = 'pvmreductionstr', colspan = 3 },
		{ tag = 'argd', content = 'pvpreductionstr', colspan = 3 }
	}:addRow{ -- life points, style header
		{ tag = 'th', content = skillpic('Constitution') .. ' Life points', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'life', colspan = 2, css = { ['text-align'] = 'right' } },
		{ tag = 'th', content = '[[Damage bonus|Style bonuses]]', colspan = 6 },
	}:addRow{ -- prayer, style bonuses
		{ tag = 'th', content = skillpic('Prayer') .. ' Prayer', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'prayer', colspan = 2, css = { ['text-align'] = 'right' } },
		{ tag = 'argd', content = 'strengthstr', colspan = 2 },
		{ tag = 'argd', content = 'rangedstr', colspan = 2 },
		{ tag = 'argd', content = 'magicstr', colspan = 2 },
	}

	-- bottom row
	-- kinda special to accomodate the compare button
	--[=[ret	:tag('tr')
			:addClass('infobox-bonuses-bottomrow')
			:css('border', 'none')
			:tag('td')
				:attr('colspan', 12)
				:css({
					background = 'transparent !important',
					border = 'none !important',
					['text-align'] = 'left',
					['font-size'] = 'smaller'
				})
				:wikitext('[[Template:Infobox ModStats/FAQ|&#91;FAQ&#93;]] &bull; [[Template:Infobox ModStats|&#91;doc&#93;]]')
	--]=]

end

-- 2 images or no images
function p._mainB(ret, hasImages)
	if hasImages then
		ret:addRow{
			{ tag = 'argd', content = 'image', colspan = 6 },
			{ tag = 'argd', content = 'altimage', colspan = 6 }
		}
	end

	local row1 = {
		{ tag = 'th', content = 'Requirements', colspan = 6, class = 'combat-requirements' },
		{ tag = 'th', content = '[[Equipment tier|Tier]]', colspan = 6 }
	}
	local row2 = {
		{ tag = 'argd', content = 'requirements', colspan = 6, css = { ['max-width'] = '240px' } }, -- stop skill reqs getting too wide
		{ tag = 'argd', content = 'tier', colspan = 6 }
	}
	local row1tier = row1[2]
	local row2tier = row2[2]

	if infobox.isDefined(ret:param('degradetype', 'r')) then
		row1[1].colspan = 3
		row2[1].colspan = 3
		table.insert(row1, 2, { tag = 'argh', content = 'degradeheader', colspan = 3 })
		table.insert(row2, 2, { tag = 'argd', content = 'degradestr', colspan = 3 })
		if ret:param('degradetype', 'r') == 'invention' then
			ret:append(tostring(ret:param('invdegrade_div', 'r')))
		end
	end
	
	if ret:paramDefined('type') then
		row1tier.colspan = 3
		row2tier.colspan = 3
		table.insert(row1, { tag = 'th', content = 'Type', colspan = 3 })
		table.insert(row2, { tag = 'argd', content = 'type_disp', colspan = 3 })
	end
	ret:addRow(row1):addRow(row2)
	local strSpan = 1
	if infobox.isDefined(ret:param('speed', 'r')) and ret:param('speed', 'r') ~= 'no' then
		strSpan = 2
	end
	ret:addRow{
		-- class, slot, attr headers
		{ tag = 'th', content = 'Class', colspan = 3, css = { width = '50%' } },
		{ tag = 'th', content = 'Slot', colspan = 3, css = { width = '50%' } },
		{ tag = 'th', content = 'Attributes', colspan = 6 },
	}
	:addRow{
		-- class, slot, armour
		{ tag = 'argd', content = 'classstr', rowspan = 2, colspan = 3 },
		{ tag = 'argd', content = 'slotimg', rowspan = 2, colspan = 3 },
		{ tag = 'th', content = skillpic('Defence') .. ' Armour', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'armour', colspan = 2, css = { ['text-align'] = 'right' } }
	}
	:addRow{
		-- [class second row from rowspan]
		-- [slot second row from rowspan]
		-- life points
		{ tag = 'th', content = skillpic('Constitution') .. ' Life points', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'life', colspan = 2, css = { ['text-align'] = 'right' } }
	}
	:addRow{
		-- weapons header, prayer
		{ tag = 'th', content = 'Weapons', colspan = 2, css = { width = '33%' } },
		{ tag = 'th', content = 'Main', colspan = 2, css = { width = '33%' } },
		{ tag = 'th', content = 'Off', colspan = 2, css = { width = '33%' } },
		{ tag = 'th', content = skillpic('Prayer') .. ' Prayer', colspan = 4, class = 'combat-attributes' },
		{ tag = 'argd', content = 'prayer', colspan = 2, css = { ['text-align'] = 'right' } }
	}
	:addRow{
		-- damage, reduction header
		{ tag = 'th', content = '[[Ability damage|Damage]]', colspan = 2 },
		{ tag = 'argd', content = 'maindamage', colspan = 2 },
		{ tag = 'argd', content = 'offdamage', colspan = 2 },
		{ tag = 'th', content = '[[Damage reduction]]', colspan = 6 }
	}
	:addRow{
		-- accuracy, reductions
		{ tag = 'th', content = '[[Hit chance|Accuracy]]', colspan = 2 },
		{ tag = 'argd', content = 'mainaccuracy', colspan = 2 },
		{ tag = 'argd', content = 'offaccuracy', colspan = 2 },
		{ tag = 'argd', content = 'pvmreductionstr', colspan = 3 },
		{ tag = 'argd', content = 'pvpreductionstr', colspan = 3 }
	}
	:addRow{
		-- style, style bonuses header
		{ tag = 'th', content = '[[Attack types|Style]]', colspan = 2 },
		{ tag = 'argd', content = 'style', colspan = 4 },
		{ tag = 'th', content = '[[Damage bonus|Style bonuses]]', colspan = 6 }
	}
	:addRow{
		-- range
		{ tag = 'th', content = '[[Attack range|Range]]', colspan = 2 },
		{ tag = 'argd', content = 'attackrange', colspan = 4 },
		{ tag = 'argd', content = 'strengthstr', colspan = 2, rowspan = strSpan },
		{ tag = 'argd', content = 'rangedstr', colspan = 2, rowspan = strSpan },
		{ tag = 'argd', content = 'magicstr', colspan = 2, rowspan = strSpan }
	}
	if ret:param('speed', 'r') ~= 'no' then
		ret:addRow{ --(formely speed)
			{ tag = 'th', content = '[[Attack rate|Rate]]', colspan = 2 },
			{ tag = 'argd', content = 'speed', colspan = 4, css = { padding = '0.2em' } }
		}
	end

end
function p._mainB0(ret)
	return p._mainB(ret, false)
end
function p._mainB2(ret)
	return p._mainB(ret, true)
end

-- images
function imagearg(f)
	local height, width = 280, 220
	if paramtest.is_empty(f) then
		return nil
	end
	f = tostring(f)
	if string.lower(f) == 'no' then
		return nil
	end
	f = f:gsub('[Ff]ile:',''):gsub('{{!}}','|')
	f = mw.text.split(f, '|')
	f = f[1]

	return mw.ustring.format('[[File:%s|%sx%spx|frameless|alt=%s: %s equipped by a player]]', f, width, height, f, mw.title.getCurrentTitle().text)
end
function noimgcatarg(nocat, img)
	if infobox.isDefined(nocat) then
		return 'true'
	end
	if infobox.isDefined(img) then
		if string.lower(img) == 'no' then
			return 'true'
		end
	end
	return nil
end

--requirements
function requirementsarg(r)
	if infobox.isDefined(r) then
		return mw.getCurrentFrame():preprocess(r)
	end
	return nil
end

-- class style type slot
function lookuparg(t, c)
	return t[string.lower(c or '')]
end
function lookupmultiarg(t, c)
	local out = {}
	if not infobox.isDefined(c) then
		return nil
	end
	c = string.lower(c)
	for i in mw.text.gsplit(c, ',') do
		table.insert(out, t[mw.text.trim(i)])
	end
	if #out == 0 then
		return nil
	end
	return table.concat(out, infobox.splitpoint)
end
function type_display(a)
	if a == nil then
		return nil
	end
	local out = {}
	for i in mw.text.gsplit(a, infobox.splitpoint) do
		i = i:lower()
		if type_cats[i] then
			table.insert(out, type_cats[i][1])
		end
	end
	return table.concat(out, ', ')
end
function stylearg(s, w)
	if w == 'true' then
		s = styles[string.lower(s or '')]
		if s then
			return paramtest.ucfirst(s)
		end
		return s
	end
	return '-'
end
function smwstylearg(s)
	if s ~= '-' then
		return string.lower(tostring(s))
	end
end
function classstrarg(c, ci)
	if c == 'none' then
		return 'None'
	end
	if infobox.isDefined(c) and infobox.isDefined(ci) then
		return ci .. ' ' .. paramtest.ucfirst(c)
	end
	return nil
end
function classsmwarg(c)
	if c == 'all' then
		return 'hybrid'
	end
	return c
end

-- tier
function tierarg(t)
	t = tostring(t)
	t = t:lower()
	if t == 'n/a' or t == 'no' or t == 'none' then
		t = 0
	end
	t = clean(t)
	if t then
		if t == 0 then
			return "''None''"
		end
		return t
	end
	return nil
end
function smwtierarg(t)
	if t == "''None''" then
		return 0
	end
	if type(t) == 'number' then
		return t
	end
	return nil
end

-- numerical args
function fnumbers(x)
	x = clean(x)
	if x then
		return x
	end
	return '-'
end
function fnumbers2(x)
	x = clean(x)
	if x then
		return x
	end
	return 0
end
function smwnumbers(x)
	if tonumber(x) then
		return x
	end
	return 0
end
function smwnumbers2(x)
	if tonumber(x) then
		return x
	end
	return 0
end
function armourarg(pas)
	local x = fnumbers(pas)
	if x == '-' then --nil
		return '0.0'
	elseif pas:find('%d%.%d') then --has decimal
		return string.format('%.1f', x)
	end
	return x --no decimal
end
function mainoffaccarg(x, s, slots)
	for i,v in ipairs(slots) do
		if s == v then
			return tonumber(x)
		end
	end
	return '-'
end
function mainoffdamarg(x, s, pas, slots)
	local r = nil
	local found = false
	for i,v in ipairs(slots) do
		if s == v then
			r = tonumber(x)
			found = true
			break
		end
	end
	if r then
		if pas:find('%d%.%d') then
			r = string.format('%.1f', r)
		end
		return r
	end
	if found then
		return nil
	end
	return '-'
end

function stylebonusesarg(x, t, passed)
	local s = tostring(x)
	if x == '-' then
		s = '0.0'
	elseif passed:find('%d%.%d') then
		s  = string.format('%.1f', x)
	end
	return mw.ustring.format('<span style="float:left;">%s</span><span style="float:right;">%s</span>', skillpic(t), s)
end

-- isWeapon boolean
function isweaponarg(s,t)
	local weapon_slots = {
		['main hand weapon'] = 'true',
		['2h weapon'] = 'true',
		['off-hand weapon'] = 'true'

	}
	local non_weapon_types = {
		['Prevents attack'] = 'true'
	}
	if non_weapon_types[t] then
		return 'false'
	end
	return weapon_slots[s] or 'false'
end
--smw
function chargesarg(dt, c)
	if dt == 'normal' then
		if type(c) == 'number' then
			return c
		end
	end
	return nil
end
function invtierarg(dt, t)
	if dt == 'invention' then
		t = clean(t)
		if t then
			return t
		end
	end
	return nil
end

-- attack range
function attackrangearg(w,r1,r2,r3)
	if w == 'true' then
		return clean(r1) or clean(r2) or clean(r3)
	end
	return '-'
end
function attackrangesmwarg(a)
	if a == '-' then
		return nil
	end
	return a
end

-- reductions
function reductionlevelarg(r, tier, reqs)
	if r then
		r = clean(r)
		if r then
			return r
		end
	end
	local ret = tonumber(tier) or 0
	if infobox.isDefined(reqs) then
		local maxR = 0
		for v in string.gmatch(reqs, '(%d+) %[%[') do
			if tonumber(v) and tonumber(v) > maxR then
				maxR = tonumber(v)
			end
		end
		if maxR > 0 then
			ret = maxR
		end
	end
	return ret
end
function reductionarg(rl, over, t, s, c, pvx)
	over = clean(over)
	if over then
		--overridden
		return over .. '%'
	end
	if not infobox.isDefined(rl) then
		--tier/rl not defined
		return '0%'
	end
	local reduction_table, reduction_type = nil,nil
	if infobox.isDefined(t) then
		-- check if we have a reduction for this type+slot
		reduction_type = reduction_types[t:lower()]
		if reduction_type then
			reduction_table = reductions[reduction_type][s]
		end
	end
	if not reduction_table and infobox.isDefined(c) then
		-- couldn't find type+slot, so class+slot
		reduction_type = reduction_types[c:lower()]
		if reduction_type then
			reduction_table = reductions[reduction_type][s]
		end
	end
	if reduction_table then
		return (reduction_table[pvx] or 0) * rl .. '%'
	end
	-- didn't find anything
	return '0%'
end
function reductionstrarg(r, ty)
	return mw.ustring.format('<span style="float:left;">%s</span><span style="float:right;">%s</span>', ty, r)
end

-- speed
function speedrawarg(iswep, s1, s2)
	if iswep =='true' then
		local s
		if infobox.isDefined(s1) then
			s = string.lower(s1)
		elseif infobox.isDefined(s2) then
			s = string.lower(s2)
		end
		return s or ''
	else
		return 'no'
	end
end
function speedarg(s)
	if infobox.isDefined(s) then
		if s == 'no' then
			return 'no'
		else
			return tostring(attack_speed_bar(s))
		end
	end
	return nil
end

-- layout
function layoutarg(img, alt)
	if infobox.isDefined(img) then
		if infobox.isDefined(alt) then
			-- 2 images
			return 'B2'
		end
		-- 1 image
		return 'A'
	end
	-- no images
	return 'B0'
end

function intbonusarg(...)
	for _,v in ipairs({...}) do
		if paramtest.has_content(v) then
			if not v:match('%d%.%d') then
				return 'true'
			end
		end
	end
	return nil
end

function recolourarg(arg)
	-- string to ensure it correctly passes through infobox
	return tostring(infobox.isDefined(arg) and yesno(arg, false))
end

-- Removes all plus signs, commas, and percent signs
function clean(number)
	if not number then
		return nil
	else
		number = tostring(number)
		number = number:gsub('[+,%%]','')
		return tonumber(number,10)
	end
end

-- legacy SMW JSON
function smwjsonarg(class, slot, style, itype, damage, accuracy, attack_range, armour, life, speed, prayer, strength, ranged, magic, tier, charges, invtier)
	local json = {
		class = class,
		slot = slot,
		style = style,
		type = itype,
		damage = damage,
		accuracy = accuracy,
		style = style,
		attack_range = attack_range,
		armour = armour,
		lp = life,
		speed = speed,
		prayer = prayer,
		strength = strength,
		ranged = ranged,
		magic = magic,
		tier = tier,
		charges = charges,
		invention = invtier
	}
	for k,v in pairs(json) do
		if not infobox.isDefined(v) then
			json[k] = nil
		end
	end
	local jsonGood, encoded = pcall(mw.text.jsonEncode,json)
	if jsonGood then
		return encoded
	end
	return nil
end

-- categories
function addcategories(ibox, args, catargs)
	local cats = {'Equipment'}
	local versions = ibox.versions
	function forAllSwitches(func, param1, param2, param3)
		local r = {}
		param2 = param2 or {}
		param3 = param3 or {}
		local p1,p2,p3
		p1 = param1.d
		p2 = param2.d
		p3 = param3.d
		table.insert(r, func(p1,p2,p3))
		if versions > 0 and (param1.switches or param2.switches or param3.switches) then
			local p1s,p2s,p3s
			for i = 1,versions,1 do
				p1s = p1
				if param1.switches then
					p1s = param1.switches[i]
					if p1s == infobox.nil_param then
						p1s = p1
					end
				end
				p2s = p2
				if param2.switches then
					p2s = param2.switches[i]
					if p2s == infobox.nil_param then
						p2s = p2
					end
				end
				p3s = p3
				if param3.switches then
					p3s = param3.switches[i]
					if p3s == infobox.nil_param then
						p3s = p3
					end
				end
				local ret = func(p1s, p2s, p3s)
				if type(ret) == 'table' then
					for ir,vr in ipairs(ret) do
						table.insert(r, vr)
					end
				else
					table.insert(r, ret)
				end
			end
		end
		return r
	end
	function append(a)
		if type(a) == 'table' then
			for i,v in ipairs(a) do
				table.insert(cats, v)
			end
		else
			table.insert(cats,a)
		end
	end
	
	-- slot missing
	if not catargs.slot.all_defined then
		append('Missing slot information')
	end
	
	-- tiers
	append(forAllSwitches(function(v)
		if type(v) == 'number' then
			if v == 0 then
				return 'Tierless equipment'
			elseif v > 0 then
				return 'Tier '..v..' equipment'
			end
		end
		return 'Missing equipment tier'
	end, args.tier_smw))
	
	-- type
	append(forAllSwitches(function(v)
		local out = {}
		if type(v) == 'string' then
			for i in mw.text.gsplit(v, infobox.splitpoint) do
				i = i:lower()
				if type_cats[i] then
					table.insert(out, type_cats[i][2])
				end
			end
		end
		return out
	end, args.type))
	
	-- slots
	append(forAllSwitches(function(v)
		v = string.lower(tostring(v))
		if v == 'off-hand weapon' then
			local k = mw.title.getCurrentTitle().fullText:gsub('[Oo]ff[%- ]?hand ?', '')
			return 'Off-hand slot weapons|'..k
		elseif slot_cats[v] then
			return slot_cats[v]
		end
		return 'Missing slot information'
	end, args.slot))
	
	-- style
	append(forAllSwitches(function(v)
		return style_cats[v]
	end, args.style))
	
	-- degrades
	if catargs.normdegrades.one_defined then
		append('Degrading equipment')
	end
	
	-- class
	append(forAllSwitches(function(c,w,s)
		local cat = class_cats[c]
		if cat then
			if s == 'ammo' then
				return nil
			end
			if w == 'true' then
				if cat == 'Hybrid' then
					cat = 'Typeless'
				end
				return cat .. ' weapons'
			end
			return cat .. ' armour'
		end
		return nil
	end, args.class, args.isweapon, args.slot))
	
	-- prayer
	append(forAllSwitches(function(p)
		if tonumber(p) then
			if tonumber(p) > 0 then
				return 'Items with a prayer bonus'
			end
		end
		return nil
	end, args.prayer_smw))
	
	-- range
	if not catargs.attackrange.all_defined then
		append('Missing attack range')
	end
	
	-- speed
	append(forAllSwitches(function(s)
		if s == '' then
			return 'Missing attack rate'
		end
		return nil
	end, args.speedraw))
	
	-- integer equipment bonuses
	if catargs.intbonus.one_defined then
		append('Integer equipment bonus')
	end
	
	-- equipped image
	append(forAllSwitches(function(i, s, n)
		if infobox.isDefined(n) then
			return nil
		end
		if s == 'ammo' or s == 'pocket' or s == 'ring' or s == 'sigil' then
			return nil
		end
		if not infobox.isDefined(i) then
			return 'Needs equipped image'
		end
		return nil
	end, args.image, args.slot, args.noimgcat))
	
	local _cats = {}
	for i,v in ipairs(cats) do
		if type(v) == 'table' then
			for j,u in ipairs(v) do
				if paramtest.has_content(u) then
					table.insert(_cats, string.format('[[Category:%s]]', u))
				end
			end
		elseif paramtest.has_content(v) then
			table.insert(_cats, string.format('[[Category:%s]]', v))
		end
	end
	return table.concat(_cats, '')
end

return p
-- </nowiki>