Module:Infobox Bonuses

From Old School RuneScape Wiki
Jump to: navigation, search

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

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

local onmain = require('Module:Mainonly').on_main
local infobox = require('Module:Infobox')
local attack_speed_bar = require('Module:Attack speed bar').make_bar
local signed = require('Module:Format eq stat').signed
local clean = require('Module:Clean image2').clean

local slots = {
	head = 'Head slot items',
	weapon = 'Weapon slot items',
	body = 'Body slot items',
	legs = 'Legs slot items',
	shield = 'Shield slot items',
	cape = 'Cape slot items',
	['2h'] = 'Two-handed slot items',
	hands = 'Hands slot items',
	feet = 'Feet slot items',
	neck = 'Neck slot items',
	ammo = 'Ammunition slot items',
	ring = 'Ring slot items',
}

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

	local signed_numeric_args = {
		'astab', 'aslash', 'acrush', 'amagic', 'arange',
		'dstab', 'dslash', 'dcrush', 'dmagic', 'drange',
		'str', 'rstr', 'prayer'
	}

	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 = numericarg_smw, params = { v } } },
		}
	end

	ret:defineParams{
		{ name = 'mdmg', func = { name = signedpercentnumericarg, params = { 'mdmg', 'mdmg' }, flag = { 'd', 'r' } } },
		{ name = 'mdmg_smw', func = { name = numericarg_smw, params = { 'mdmg' }, flag = { 'p' } } },
		{ name = 'slot', func = 'has_content' },
		{ name = 'slot_image', func = { name = slotarg, params = { 'slot' } } },
		{ name = 'slot_link', func = { name = slot_link_arg, params = { 'slot' } } },
		{ name = 'speed', func = attack_speed_bar },
		{ name = 'speed_smw', func = { name = 'has_content', params = { 'speed' }, flag = { 'p' } } },
		{ name = 'attackrange', func = attackrangearg },
		{ name = 'attackrange_smw', func = { name = 'has_content', params = { 'attackrange' }, flag = { 'p' } } },
		{ name = 'combatstyle', func = 'has_content' },
		{ name = 'image', func = { name = imagearg, params = { 'image', 'caption', 'image' }, flag = { 'd', 'd', 'r' } } },
		{ name = 'image_width', func = { name = image_width_arg, params = { 'image', 'image' }, flag = { 'd', 'r' } } },
		{ name = 'altimage', func = { name = imagearg, params = { 'altimage', 'altcaption', 'altimage' }, flag = { 'd', 'd', 'r' } } },
		{ name = 'altimage_width', func = { name = image_width_arg, params = { 'altimage', 'altimage' }, flag = { 'd', 'r' } } },
	}

	ret:defineLinks({ hide = true })

	local smw_mapping = {
		astab_smw = 'Stab attack bonus',
		aslash_smw = 'Slash attack bonus',
		acrush_smw = 'Crush attack bonus',
		arange_smw = 'Range attack bonus',
		amagic_smw = 'Magic attack bonus',
		dstab_smw = 'Stab defence bonus',
		dslash_smw = 'Slash defence bonus',
		dcrush_smw = 'Crush defence bonus',
		drange_smw = 'Range defence bonus',
		dmagic_smw = 'Magic defence bonus',
		str_smw = 'Strength bonus',
		rstr_smw = 'Ranged Strength bonus',
		prayer_smw = 'Prayer bonus',
		mdmg_smw = 'Magic Damage bonus',
		slot = 'Equipment slot',
		speed_smw = 'Weapon attack speed',
		attackrange_smw = 'Weapon attack range',
		combatstyle = 'Combat style',
	}

	local smw_all_mapping = {}
	for param, smw_name in pairs(smw_mapping) do
		smw_all_mapping[param] = 'All '..smw_name
	end

	ret:useSMWSubobject(smw_mapping)
	ret:useSMWOne(smw_all_mapping)

	ret:create()
	ret:cleanParams()

	local any_is_weapon = ret:paramGrep('slot', function(x) return infobox.isDefined(x) and (string.lower(x) == 'weapon' or string.lower(x) == '2h') end)

	ret:customButtonPlacement(true)
	ret:addButtonsCaption()

	ret:defineName('Infobox Bonuses')
	ret:addClass('infobox-bonuses infobox')

	local image_rowspan = 15
	if any_is_weapon then
		image_rowspan = 19
	end

	local image_td = nil
	local image_defined = ret:paramGrep('image', function(x) return (infobox.isDefined(x) and x or 'N/A') ~= 'N/A' end)
	if image_defined then
		local all_widths = ret:param('image_width', 'f')
		local max_width = max_width(all_widths)
		image_td = { tag = 'argd', content = 'image', class = 'infobox-bonuses-image', css = {width = max_width..'px'}, rowspan = image_rowspan }
	end
	local altimage_td = nil
	local altimage_defined = ret:paramGrep('altimage', function(x) return (infobox.isDefined(x) and x or 'N/A') ~= 'N/A' end)
	if altimage_defined then
		local altall_widths = ret:param('altimage_width', 'f')
		local altmax_width = max_width(altall_widths)
		altimage_td = { tag = 'argd', content = 'altimage', class = 'infobox-bonuses-image', css = {width = altmax_width..'px'}, rowspan = image_rowspan }
	end

	ret:addRow{
		{ tag = 'th', content = '[[File:Attack icon.png|link=Equipment Stats#Attack bonuses]] [[Equipment Stats#Attack bonuses|Attack bonuses]]', colspan = '10', class = 'infobox-subheader' },
		image_td,
		altimage_td
	}
	:pad(10)
	:addRow{
		{ tag = 'th', content = '[[File:White dagger.png|link=Stab]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White scimitar.png|link=Slash]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White warhammer.png|link=Crush]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic icon.png|link=Magic]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged icon.png|link=Ranged]]', colspan = '2', class = 'infobox-nested' }
	}
	:addRow{
		{ tag = 'argd', content = 'astab', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'aslash', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'acrush', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'amagic', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'arange', colspan = '2', class = 'infobox-nested' }
	}
	:pad(10)
	:addRow{
		{ tag = 'th', content = '[[File:Defence icon.png|link=Equipment Stats#Defence bonuses]] [[Equipment Stats#Defence bonuses|Defence bonuses]]', colspan = '10', class = 'infobox-subheader' }
	}
	:pad(10)
	:addRow{
		{ tag = 'th', content = '[[File:White dagger.png|link=Stab]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White scimitar.png|link=Slash]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:White warhammer.png|link=Crush]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic icon.png|link=Magic]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged icon.png|link=Ranged]]', colspan = '2', class = 'infobox-nested' }
	}
	:addRow{
		{ tag = 'argd', content = 'dstab', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dslash', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dcrush', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'dmagic', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'drange', colspan = '2', class = 'infobox-nested' }
	}
	:pad(10)
	:addRow{
		{ tag = 'th', content = '[[File:Melee.png|link=Equipment Stats#Other bonuses]] [[Equipment Stats#Other bonuses|Other bonuses]]', colspan = '8', class = 'infobox-subheader' },
		{ tag = 'th', content = 'Slot', colspan = '2', class = 'infobox-subheader' }
	}
	:pad(10)
	:addRow{
		{ tag = 'th', content = '[[File:Strength icon.png|link=Strength bonus]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Ranged Strength icon.png|link=Ranged Strength]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Magic Damage icon.png|link=Magic damage]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'th', content = '[[File:Prayer icon.png|link=Prayer]]', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argh', content = 'slot_image', colspan = '2', class = 'infobox-nested' }
	}
	ret:addRow{
		{ tag = 'argd', content = 'str', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'rstr', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'mdmg', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'prayer', colspan = '2', class = 'infobox-nested' },
		{ tag = 'argd', content = 'slot_link', colspan = '2', class = 'infobox-nested' }
	}
	:pad(10)

	if any_is_weapon then
		ret:addRow{
			{ tag = 'th', content = '[[Attack speed#Weapon attack speeds|Attack speed]]', colspan = '7', class = 'infobox-subheader' },
			{ tag = 'th', content = '[[Attack range|Range]]', colspan = '3', class = 'infobox-subheader' }
		}
		:pad(10)
		:addRow{
			{ tag = 'argd', content = 'speed', colspan = '7', class = 'infobox-nested' },
			{ tag = 'argd', content = 'attackrange', colspan = '3', class = 'infobox-nested' }
		}
		:pad(10)
	end

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

function numericarg_smw(arg)
	if not infobox.isDefined(arg) then
		return nil
	elseif tonumber(arg) ~= nil then
		return arg
	end
	return nil
end

function numericarg(arg, arg_name)
	if not infobox.isDefined(arg) then
		return nil
	end
	return arg
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

-- Return the appropriate slot image
function slotarg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end
	local _arg = string.lower(arg)
	for slot, _ in pairs(slots) do
		if _arg == slot then
			return string.format('[[File:%s slot.png|link=%s slot]]', slot, slot)
		end
	end

	return badarg('slot', 'should be a valid slot name.')
end

function slot_link_arg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end
	return '[['..arg..' slot|List]]'
end

function attackrangearg(arg)
	if not infobox.isDefined(arg) then
		return nil
	end
	if string.lower(arg) == 'no' then
		return 'N/A'
	end
	if tonumber(arg) ~= nil then
		if arg == '1' then
			return '1<br>square'
		else
			return arg..'<br>squares'
		end
	end
	return arg
end

function imagearg(image, caption, argname)
	if not infobox.isDefined(image) then
		return nil
	end
	if string.lower(image) == 'no' then
		return 'N/A'
	end
	local clean_result = clean({file=image, maxW = 300, maxH = 350})
	if clean_result == nil then
		return badarg(argname, 'should be a valid file.')
	end
	local image_string, width, height = unpack(clean_result)
	local caption_string = ''
	if infobox.isDefined(caption) and string.lower(caption) ~= 'no' then
		caption_string = '<br /><span class="infobox-bonuses-image-caption">' .. caption .. '</span>'
	end
	return image_string..caption_string
end

function image_width_arg(image, argname)
	if not infobox.isDefined(image) then
		return nil
	end
	local clean_result = clean({file=image, maxW = 300, maxH = 350})
	if clean_result == nil then
		return badarg(argname, 'should be a valid file.')
	end
	local image_string, width, height = unpack(clean_result)
	return width
end

-- Get the max width from all versions, minimum 100
function max_width(all_widths)
	local res = tonumber(all_widths.d) or 100
	for i, width in ipairs(all_widths.switches or {100}) do
		res = math.max(res, tonumber(width) or 100)
	end
	return math.max(res, 100)
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 = {}

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

	if args['slot'] then
		local slot_d = args['slot'].d
		if slots[slot_d] then
			table.insert(ret, slots[slot_d])
		end
		if args['slot'].switches then
			for _, slot_i in ipairs(args['slot'].switches) do
				if slots[slot_i] then
					table.insert(ret, slots[slot_i])
				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