Module:Equipment/Recommended: Difference between revisions

From Melvor Idle
(main: Modify to support 'showDR' and 'imgSize' parameters)
m (Allow for an Empty slot to be passed in)
 
(17 intermediate revisions by 4 users not shown)
Line 1: Line 1:
-- Adapted from: https://oldschool.runescape.wiki/w/Module:Recommended Equipment
-- Adapted from: https://oldschool.runescape.wiki/w/Module:Recommended_equipment
-- Released under: https://creativecommons.org/licenses/by-nc-sa/3.0/  
-- Released under: https://creativecommons.org/licenses/by-nc-sa/3.0/  


Line 5: Line 5:


local Params = require('Module:Shared/Paramtest')
local Params = require('Module:Shared/Paramtest')
local Shared = require('Module:Shared')
local Icons = require("Module:Icons")
local Icons = require("Module:Icons")


local slots = {
local slots = {
{ placement = 1, name = 'helm', icon = 'Slot_head', txt = 'Helmet', link = 'Equipment#Helmets' },
{ placement = 1, name = 'helm', icon = 'Slot_head', txt = 'Helmet', link = 'Helmets' },
{ placement = 2, name = 'body', icon = 'Slot_chest', txt = 'Platebody', link = 'Equipment#Platebodies' },
{ placement = 2, name = 'body', icon = 'Slot_chest', txt = 'Platebody', link = 'Platebodies' },
{ placement = 3, name = 'legs', icon = 'Slot_legs', txt = 'Platelegs', link = 'Equipment#Platelegs' },
{ placement = 3, name = 'legs', icon = 'Slot_legs', txt = 'Platelegs', link = 'Platelegs' },
{ placement = 4, name = 'boots', icon = 'Slot_feet', txt = 'Boots', link = 'Equipment#Boots' },
{ placement = 4, name = 'boots', icon = 'Slot_feet', txt = 'Boots', link = 'Boots' },
{ placement = 5, name = 'gloves', icon = 'Slot_hands', txt = 'Gloves', link = 'Equipment#Gloves' },
{ placement = 5, name = 'gloves', icon = 'Slot_hands', txt = 'Gloves', link = 'Gloves' },
{ placement = 6, name = 'cape', icon = 'Slot_back', txt = 'Cape', link = 'Equipment#Capes' },
{ placement = 6, name = 'cape', icon = 'Slot_back', txt = 'Cape', link = 'Capes' },
{ placement = 7, name = 'neck', icon = 'Slot_neck', txt = 'Amulet', link = 'Equipment#Amulets' },
{ placement = 7, name = 'neck', icon = 'Slot_neck', txt = 'Amulet', link = 'Amulets' },
{ placement = 8, name = 'ring', icon = 'Slot_ring', txt = 'Ring', link = 'Equipment#Rings' },
{ placement = 8, name = 'ring', icon = 'Slot_ring', txt = 'Ring', link = 'Rings' },
{ placement = 9, name = 'gem', icon = 'Slot_gem', txt = 'Gem', link = 'Gems (Equipment)' },
{ placement = 9, name = 'weapon', icon = 'Slot_weapon', txt = 'Weapon', link = 'Equipment#Weapons' },
{ placement = 10, name = 'weapon', icon = 'Slot_weapon', txt = 'Weapon', link = 'Weapons' },
{ placement = 10, name = 'shield', icon = 'Slot_shield', txt = 'Offhand', link = 'Equipment#Offhand' },
{ placement = 11, name = 'shield', icon = 'Slot_shield', txt = 'Offhand', link = 'Shields' },
{ placement = 11, name = 'ammo', icon = 'Slot_ammo', txt = 'Quiver', link = 'Equipment#Ammunition' },
{ placement = 12, name = 'ammo', icon = 'Slot_ammo', txt = 'Quiver', link = 'Ammunition' },
{ placement = 12, name = 'passive', icon = 'Slot_passive', txt = 'Passive', link = 'Combat Passive Slot', hasStats = false },
{ placement = 13, name = 'passive', icon = 'Slot_passive', txt = 'Passive', link = 'Combat Passive Slot', hasStats = false },
{ placement = 13, name = 'familiar1', icon = 'Slot_summon', txt = 'Familiar Left', link = 'Summoning' },
{ placement = 14, name = 'consumable', icon = 'Slot_consumable', txt = 'Consumable', link = 'Consumables' },
{ placement = 14, name = 'familiar2', icon = 'Slot_summon', txt = 'Familiar Right', link = 'Summoning' },
{ placement = 15, name = 'familiar1', icon = 'Slot_summon', txt = 'Familiar Left', link = 'Summoning' },
{ placement = 16, name = 'familiar2', icon = 'Slot_summon', txt = 'Familiar Right', link = 'Summoning' },
{ placement = 17, name = 'enhC', icon = 'Slot_Enhancement1', txt = 'Enhancement (Circle)', link = 'Enhancement' },
{ placement = 18, name = 'enhT', icon = 'Slot_Enhancement2', txt = 'Enhancement (Triangle)', link = 'Enhancement' },
{ placement = 19, name = 'enhS', icon = 'Slot_Enhancement3', txt = 'Enhancement (Square)', link = 'Enhancement' },
{ placement = 50, name = 'prayer1', icon = 'Slot_prayer', txt = 'Prayer1', link = 'Prayer', icontype = 'prayer' },
{ placement = 51, name = 'prayer2', icon = 'Slot_prayer', txt = 'Prayer2', link = 'Prayer', icontype = 'prayer' },
{ placement = 99, name = 'notes', icon = '', txt = 'Notes', link = '', hasStats = false }
{ placement = 99, name = 'notes', icon = '', txt = 'Notes', link = '', hasStats = false }
Line 30: Line 39:


function p.main(frame)
function p.main(frame)
local args = frame.args --frame:getParent().args
local args = frame:getParent().args
-- Dynamic colspan and N/A generation value
-- Dynamic colspan and N/A generation value
local greatest_row_size = 0
local greatest_row_size = 0
Line 66: Line 75:
-- ! prefix, treat as plain text
-- ! prefix, treat as plain text
itemIcon = string.sub(v, 2, -1)
itemIcon = string.sub(v, 2, -1)
else
elseif v ~= 'Empty' then
-- Otherwise treat as an item icon
-- Otherwise treat as an item icon
itemIcon = Icons.Icon({v, img=v, type='item'})
local icontype = slot.icontype or 'item'
itemIcon = Icons.Icon({v, img=v, type=icontype})
end
end
local gearname = tr:tag('td'):wikitext(itemIcon)
local gearname = tr:tag('td'):wikitext(itemIcon)
Line 106: Line 116:
local check = args[v.name .. i]
local check = args[v.name .. i]
if check and Params.has_content(check) then
if check and Params.has_content(check) then
grs = grs + 1
grs = grs + 1
end
end
end
end
Line 117: Line 127:
if showDR and greatest_row_size > 1 then
if showDR and greatest_row_size > 1 then
-- showDR option is only compatible if a single column of equipment is to be displayed
-- showDR option is only compatible if a single column of equipment is to be displayed
return 'ERROR: showDR option is incompatible with more than one item per slot'
return Shared.printError('showDR option is incompatible with more than one item per slot')
end
end
Line 128: Line 138:
local compact = true
local compact = true
if args.noheader == nil then
if args.noheader == nil then
local itemHeader = (greatest_row_size > 1 and 'Item (most effective → least effective)') or 'Item'
local itemHeader = nil
local colsize = args.header2 ~= nil and 1 or greatest_row_size
if args.header1 ~= nil then
itemHeader = args.header1
else
itemHeader = (greatest_row_size > 1 and 'Item (most effective → least effective)') or 'Item'
end
compact = false
compact = false
parent:addClass('wikitable stickyHeader')
parent:addClass('wikitable stickyHeader')
local trHead = parent:tag('tr')
local trHead = parent:tag('tr')
local lastHeader = nil
trHead:addClass('headerRow-0')
trHead:addClass('headerRow-0')
:tag('th'):wikitext('Slot'):done()
:tag('th'):wikitext('Slot'):done()
:tag('th'):attr('colspan', greatest_row_size):wikitext(itemHeader):done()
:tag('th'):attr('colspan', colsize):wikitext(itemHeader):done()
-- Add any additional header options
for i = 2, number_of_possible_choices, 1 do
local check = args['header' .. i]
if check and Params.has_content(check) and i <= greatest_row_size then
local header = trHead:tag('th')
header:wikitext(check):done()
lastHeader = header
else
if lastHeader ~= nil then
lastHeader:attr('colspan', greatest_row_size - i + 2)
end
break
end
end
if showDR then
if showDR then
trHead:tag('th'):wikitext('DR%'):done()
trHead:tag('th'):wikitext('DR%'):done()
Line 221: Line 252:
table.insert(row, lefties[i])
table.insert(row, lefties[i])
table.insert(row, righties[i])
table.insert(row, righties[i])
table.insert(row, notes[i])
table.insert(row, '| ' .. notes[i])
table.insert(rows, table.concat(row, "\r\n"))
table.insert(rows, table.concat(row, "\r\n"))
Line 236: Line 267:
familiar22 = 'Octopus',
familiar22 = 'Octopus',
familiar13 = 'Crow',
familiar13 = 'Crow',
familiar23 = 'Bear'
familiar23 = 'Bear',
}} )
}} )
end
end


return p
return p

Latest revision as of 19:20, 12 October 2024

Documentation for this module may be created at Module:Equipment/Recommended/doc

-- Adapted from: https://oldschool.runescape.wiki/w/Module:Recommended_equipment
-- Released under: https://creativecommons.org/licenses/by-nc-sa/3.0/ 

local p = {}

local Params = require('Module:Shared/Paramtest')
local Shared = require('Module:Shared')
local Icons = require("Module:Icons")

local slots = {
	{ placement = 1, name = 'helm', icon = 'Slot_head', txt = 'Helmet', link = 'Helmets' },
	{ placement = 2, name = 'body', icon = 'Slot_chest', txt = 'Platebody', link = 'Platebodies' },
	{ placement = 3, name = 'legs', icon = 'Slot_legs', txt = 'Platelegs', link = 'Platelegs' },
	{ placement = 4, name = 'boots', icon = 'Slot_feet', txt = 'Boots', link = 'Boots' },
	{ placement = 5, name = 'gloves', icon = 'Slot_hands', txt = 'Gloves', link = 'Gloves' },
	
	{ placement = 6, name = 'cape', icon = 'Slot_back', txt = 'Cape', link = 'Capes' },
	{ placement = 7, name = 'neck', icon = 'Slot_neck', txt = 'Amulet', link = 'Amulets' },
	{ placement = 8, name = 'ring', icon = 'Slot_ring', txt = 'Ring', link = 'Rings' },
	{ placement = 9, name = 'gem', icon = 'Slot_gem', txt = 'Gem', link = 'Gems (Equipment)' },
	
	{ placement = 10, name = 'weapon', icon = 'Slot_weapon', txt = 'Weapon', link = 'Weapons' },
	{ placement = 11, name = 'shield', icon = 'Slot_shield', txt = 'Offhand', link = 'Shields' },
	{ placement = 12, name = 'ammo', icon = 'Slot_ammo', txt = 'Quiver', link = 'Ammunition' },
	
	{ placement = 13, name = 'passive', icon = 'Slot_passive', txt = 'Passive', link = 'Combat Passive Slot', hasStats = false },
	{ placement = 14, name = 'consumable', icon = 'Slot_consumable', txt = 'Consumable', link = 'Consumables' },
	{ placement = 15, name = 'familiar1', icon = 'Slot_summon', txt = 'Familiar Left', link = 'Summoning' },
	{ placement = 16, name = 'familiar2', icon = 'Slot_summon', txt = 'Familiar Right', link = 'Summoning' },
	{ placement = 17, name = 'enhC', icon = 'Slot_Enhancement1', txt = 'Enhancement (Circle)', link = 'Enhancement' },
	{ placement = 18, name = 'enhT', icon = 'Slot_Enhancement2', txt = 'Enhancement (Triangle)', link = 'Enhancement' },
	{ placement = 19, name = 'enhS', icon = 'Slot_Enhancement3', txt = 'Enhancement (Square)', link = 'Enhancement' },
	
	{ placement = 50, name = 'prayer1', icon = 'Slot_prayer', txt = 'Prayer1', link = 'Prayer', icontype = 'prayer' },
	{ placement = 51, name = 'prayer2', icon = 'Slot_prayer', txt = 'Prayer2', link = 'Prayer', icontype = 'prayer' },
	
	{ placement = 99, name = 'notes', icon = '', txt = 'Notes', link = '', hasStats = false }
}

function p.main(frame)
	local args = frame:getParent().args
	-- Dynamic colspan and N/A generation value
	local greatest_row_size = 0
	-- Number of choices each slot can have
	local number_of_possible_choices = 5
	-- Have to sort this table or else the order is messed up when you use it
	table.sort(slots, function(a, b) return a.placement < b.placement end)
	
	local showDR = args.showDR ~= nil and string.lower(args.showDR) == 'true'
	local Items, statTotal = nil, 0
	if showDR then
		-- Include the Items module here if necessary, avoids loading the data
		-- if the template does not require it
		Items = require('Module:Items')
	end
	local imgSize = 36
	if args.imgSize ~= nil and tonumber(args.imgSize) ~= nil then
		imgSize = math.max(0, tonumber(args.imgSize))
	elseif showDR then
		imgSize = 25
	end
	
	local function make_row(slot, data, compact)
		local tr = mw.html.create('tr')
		local sloticon = tr:tag('td')
		sloticon:attr('style', 'text-align: center')
			:wikitext(string.format('[[File:%s.png|%s|link=%s|%spx]]', slot.icon, slot.txt, slot.link, imgSize))
		if compact then
			sloticon:attr('style', 'padding-bottom: 4px;')
		end
		for _, v in ipairs(data) do
			local itemIcon = nil
			local isPlain = type(string.find(v, '!', 1, true)) == 'number'
			if isPlain then
				-- ! prefix, treat as plain text
				itemIcon = string.sub(v, 2, -1)
			elseif v ~= 'Empty' then
				-- Otherwise treat as an item icon
				local icontype = slot.icontype or 'item'
				itemIcon = Icons.Icon({v, img=v, type=icontype})
			end
			local gearname = tr:tag('td'):wikitext(itemIcon)
			if compact then
				gearname:attr('style', 'padding-left: 12px;')
			end
			-- Attempt to obtain DR value if requested
			if showDR then
				local hasStats = true
				if slot.hasStats ~= nil then
					hasStats = slot.hasStats
				end
				local statValue = 0
				if hasStats and not isPlain and Items ~= nil then
					local item = Items.getItem(v)
					if item ~= nil then
						statValue = Items._getItemStat(item, 'damageReduction', true)
					end
				end
				statTotal = statTotal + statValue
				tr:tag('td'):wikitext(statValue .. '%')
			end
		end
		
		-- If the data size is smaller than GRS, then we need to fill up the remaining td's with N/As
		if #data < greatest_row_size then
			local difference = greatest_row_size - #data
			tr:tag('td'):attr('colspan', difference):attr("class", ".dimmed")
		end
		return tr
	end
	
	-- Find the greatest row count
	for _, v in next, slots, nil do
		local grs = 0
		for i = 1, number_of_possible_choices, 1 do
			local check = args[v.name .. i]
			if check and Params.has_content(check) then
				grs = grs + 1
			end
		end
		
		if greatest_row_size < grs then
			greatest_row_size = grs	
		end
	end
	
	if showDR and greatest_row_size > 1 then
		-- showDR option is only compatible if a single column of equipment is to be displayed
		return Shared.printError('showDR option is incompatible with more than one item per slot')
	end
	
	local parent = mw.html.create('table')
	-- If setname is passed in, apply it above the table
	if args.setname and Params.has_content(args.setname) then
		parent:tag('caption'):wikitext(string.format('Recommended equipment for %s', args.setname))
	end

	local compact = true
	if args.noheader == nil then
		local itemHeader = nil
		local colsize = args.header2 ~= nil and 1 or greatest_row_size
		if args.header1 ~= nil then
			itemHeader = args.header1
		else
			itemHeader = (greatest_row_size > 1 and 'Item (most effective → least effective)') or 'Item'
		end
		compact = false
		parent:addClass('wikitable stickyHeader')
		local trHead = parent:tag('tr')
		local lastHeader = nil
		trHead:addClass('headerRow-0')
			:tag('th'):wikitext('Slot'):done()
			:tag('th'):attr('colspan', colsize):wikitext(itemHeader):done()
		-- Add any additional header options
		for i = 2, number_of_possible_choices, 1 do
			local check = args['header' .. i]
			if check and Params.has_content(check) and i <= greatest_row_size then
				local header = trHead:tag('th')
				header:wikitext(check):done()
				lastHeader = header
			else
				if lastHeader ~= nil then
					lastHeader:attr('colspan', greatest_row_size - i + 2)
				end
				break
			end
		end
		if showDR then
			trHead:tag('th'):wikitext('DR%'):done()
		end
	end
		
	for _, v in next, slots, nil do
		local row_data = {}
		for i = 1, number_of_possible_choices, 1 do
			local gear = args[v.name .. i]
			if gear and Params.has_content(gear) then
				table.insert(row_data, gear)
			end
		end

		if #row_data > 0 or args.showall then
			parent:node(make_row(v, row_data, compact))
		end
	end
	
	if showDR then
		-- Total row for displayed stat
		parent:tag('tr')
			:tag('th'):attr('colspan', 1 + greatest_row_size):wikitext('Total'):done()
			:tag('td'):wikitext(statTotal .. '%'):done()
			:done()
	end
	
	return tostring(parent) .. '\r\n[[Category:Pages with Equipment Recommendations]]'
end

function p.synergy(frame)
	local args = frame:getParent().args
	-- local args = frame.args
	-- mw.logObject(args)
	local left_summon = slots[13]
	local right_summon = slots[14]
	local summon_icon = string.format('[[File:%s.png|Summon|24px]]', right_summon.icon)
	local notes = slots[15]
	local number_of_possible_choices = 5
	
	local rows = {}
	local header_row = {}
	local header_style = '!style="padding-right: 3em; padding-left: 3em; text-align: center;" |'
	table.insert(header_row, summon_icon .. ' Left')
	table.insert(header_row, summon_icon .. ' Right')
	table.insert(header_row, 'Notes')
	
	table.insert(rows, '{| class="wikitable col-1-center col-2-center"')
	table.insert(rows, header_style .. table.concat(header_row, '\r\n'..header_style))
	
	local function make_gear_cell(name)
		if name then
			return '| ' .. Icons.Icon({name, img=name, type='item'})
		else
			return '| ' .. summon_icon
		end
	end
	
	-- first we create tables for left and right side making sure left[i] and right[i] correspond
	-- could generate the rows in one go, but eh
	local lefties = {}
	local righties = {}
	local notes = {}
	
	local number_rows = 0
	
	for i = 1, number_of_possible_choices, 1 do
		local left = args[left_summon.name .. i]
		table.insert(lefties, make_gear_cell(left))
		
		local right = args[right_summon.name .. i]
		table.insert(righties, make_gear_cell(right))
		
		local note = args['notes' .. i]
		table.insert(notes, note or '')
		
		if left or right or note then
			number_rows = number_rows + 1
		end
	end
	
	-- then we create all the rows by formatting them
	for i = 1, number_rows, 1 do
		local row = {}
		table.insert(row, lefties[i])
		table.insert(row, righties[i])
		table.insert(row, '| ' .. notes[i])
		
		table.insert(rows, table.concat(row, "\r\n"))
	end
	
	return table.concat(rows, '\r\n|-\r\n') .. '\r\n|}\r\n[[Category:Pages with Equipment Recommendations]]'

end

function p.test()
	return p.synergy({ args = { familiar11 = 'Crow',
								familiar21 = 'Devil',
								familiar12 = 'Crow',
								familiar22 = 'Octopus',
								familiar13 = 'Crow',
								familiar23 = 'Bear',
						}} )
end

return p