Module:Equipment/Recommended: Difference between revisions
From Melvor Idle
m (change to class) |
m (Allow for an Empty slot to be passed in) |
||
(42 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
-- | -- 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/ | ||
local p = {} | local p = {} | ||
local | local Params = require('Module:Shared/Paramtest') | ||
local | local Shared = require('Module:Shared') | ||
local Icons = require("Module:Icons") | |||
local slots = { | local slots = { | ||
{ placement = 1, name = 'helm', icon = 'Slot_head', txt = ' | { placement = 1, name = 'helm', icon = 'Slot_head', txt = 'Helmet', link = 'Helmets' }, | ||
{ placement = 2, name = 'body', icon = 'Slot_chest', txt = ' | { placement = 2, name = 'body', icon = 'Slot_chest', txt = 'Platebody', link = 'Platebodies' }, | ||
{ placement = 3, name = 'legs', icon = 'Slot_legs', txt = ' | { placement = 3, name = 'legs', icon = 'Slot_legs', txt = 'Platelegs', link = 'Platelegs' }, | ||
{ placement = 4, name = 'boots', icon = 'Slot_feet', txt = 'Boots', link = ' | { placement = 4, name = 'boots', icon = 'Slot_feet', txt = 'Boots', link = 'Boots' }, | ||
{ placement = 5, name = 'gloves', icon = 'Slot_hands', txt = 'Gloves', link = ' | { placement = 5, name = 'gloves', icon = 'Slot_hands', txt = 'Gloves', link = 'Gloves' }, | ||
{ placement = 6, name = 'cape', icon = 'Slot_back', txt = ' | { placement = 6, name = 'cape', icon = 'Slot_back', txt = 'Cape', link = 'Capes' }, | ||
{ placement = 7, name = 'neck', icon = 'Slot_neck', txt = ' | { placement = 7, name = 'neck', icon = 'Slot_neck', txt = 'Amulet', link = 'Amulets' }, | ||
{ placement = 8, name = 'ring', icon = 'Slot_ring', txt = 'Ring', link = ' | { placement = 8, name = 'ring', icon = 'Slot_ring', txt = 'Ring', link = 'Rings' }, | ||
{ placement = 9, name = 'gem', icon = 'Slot_gem', txt = 'Gem', link = 'Gems (Equipment)' }, | |||
{ placement = | { placement = 10, name = 'weapon', icon = 'Slot_weapon', txt = 'Weapon', link = 'Weapons' }, | ||
{ placement = | { placement = 11, name = 'shield', icon = 'Slot_shield', txt = 'Offhand', link = 'Shields' }, | ||
{ placement = | { placement = 12, name = 'ammo', icon = 'Slot_ammo', txt = 'Quiver', link = 'Ammunition' }, | ||
{ placement = | { placement = 13, name = 'passive', icon = 'Slot_passive', txt = 'Passive', link = 'Combat Passive Slot', hasStats = false }, | ||
{ placement = | { placement = 14, name = 'consumable', icon = 'Slot_consumable', txt = 'Consumable', link = 'Consumables' }, | ||
{ placement = | { 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) | function p.main(frame) | ||
local 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 36: | Line 46: | ||
-- Have to sort this table or else the order is messed up when you use it | -- 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) | 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 function make_row(slot, data, compact) | ||
local tr = mw.html.create('tr') | local tr = mw.html.create('tr') | ||
local sloticon = tr:tag('td') | local sloticon = tr:tag('td') | ||
sloticon:wikitext(string.format('[[File:%s.png|%s|link=%s| | 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 | if compact then | ||
sloticon:attr('style', 'padding-bottom: 4px;') | sloticon:attr('style', 'padding-bottom: 4px;') | ||
end | end | ||
for _, v in ipairs(data) do | for _, v in ipairs(data) do | ||
local | local itemIcon = nil | ||
local gearname = tr:tag('td'):wikitext( | 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 | if compact then | ||
gearname:attr('style', 'padding-left: 12px;') | 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 | ||
end | end | ||
Line 55: | Line 105: | ||
if #data < greatest_row_size then | if #data < greatest_row_size then | ||
local difference = greatest_row_size - #data | local difference = greatest_row_size - #data | ||
tr:tag('td'):attr('colspan', difference):class | tr:tag('td'):attr('colspan', difference):attr("class", ".dimmed") | ||
end | end | ||
return tr | return tr | ||
Line 65: | Line 115: | ||
for i = 1, number_of_possible_choices, 1 do | for i = 1, number_of_possible_choices, 1 do | ||
local check = args[v.name .. i] | local check = args[v.name .. i] | ||
if check and | if check and Params.has_content(check) then | ||
grs = grs + 1 | grs = grs + 1 | ||
end | end | ||
end | end | ||
Line 73: | Line 123: | ||
greatest_row_size = grs | greatest_row_size = grs | ||
end | 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 | end | ||
local parent = mw.html.create('table') | local parent = mw.html.create('table') | ||
-- If setname is passed in, apply it above the table | -- If setname is passed in, apply it above the table | ||
if args.setname and | if args.setname and Params.has_content(args.setname) then | ||
parent:tag('caption'):wikitext(string.format('Recommended equipment for %s', args.setname)) | parent:tag('caption'):wikitext(string.format('Recommended equipment for %s', args.setname)) | ||
end | end | ||
Line 83: | Line 138: | ||
local compact = true | local compact = true | ||
if args.noheader == nil then | 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 | compact = false | ||
parent:addClass('wikitable') | parent:addClass('wikitable stickyHeader') | ||
parent:tag('tr') | local trHead = parent:tag('tr') | ||
local lastHeader = nil | |||
trHead:addClass('headerRow-0') | |||
:tag('th'):wikitext('Slot'):done() | :tag('th'):wikitext('Slot'):done() | ||
:tag('th'):attr('colspan', greatest_row_size):wikitext(' | :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 | end | ||
Line 94: | Line 175: | ||
for i = 1, number_of_possible_choices, 1 do | for i = 1, number_of_possible_choices, 1 do | ||
local gear = args[v.name .. i] | local gear = args[v.name .. i] | ||
if gear and | if gear and Params.has_content(gear) then | ||
table.insert(row_data, gear) | table.insert(row_data, gear) | ||
end | end | ||
Line 103: | Line 184: | ||
end | end | ||
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 '') | |||
return | 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 | 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