-- <pre>
require('Module:Mw.html extension')
local p = {}
local format = require('Module:Format equipment stat').format --for formatting the stats with + and - symbols on output
require('Module:Mw.html extension')
local p2pIcon = '[[File:Member icon.png|frameless|link=Pay-to-play]]' --these icons are for the later members/f2p stars
local yesNo = require('Module:Yesno')
local f2pIcon = '[[File:Free-to-play icon.png|frameless|link=Free-to-play]]'
local paramTest = require('Module:Paramtest')
local contains = require('Module:Array').contains
local minimum = require('Module:Array').min
local pagesWithCats = require('Module:PageListTools').pageswithcats
local pagesWithConditions = require('Module:PageListTools').pageswithconditions
local editbtn = require('Module:Edit button')
local stats = {'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dmagic', 'drange', 'str', 'rstr', 'mdmg', 'prayer'}
local memberOptions = {'members', 'f2p', 'all'}
local slotOptions = {'head', 'cape', 'neck', 'ammo', 'weapon', 'shield', 'body', 'legs', 'hands', 'feet', 'ring', '2h'}
local statOptions = {'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dmagic', 'drange', 'str', 'rstr', 'mdmg', 'prayer'}
function statFormatp.main(_arg, defaultframe)
local args = frame:getParent().args --get the input from the template
local arg = tonumber(_arg)
local slot = string.lower(tostring(args['slot'])) --take the slot input as the slot we want the table for
if(not arg) then return (default or _arg) end
local mems = args['mems']
if(arg < 0) then return tostring(arg) end
local dmm = args['dmm']
return '+' .. arg
local beta = args['beta']
end
local data = getData(slot,mems,dmm,beta) --this takes the slot and sends it to our getData function (seen below) which gives us back a data table
local restbl = mw.html.create('table') --start making our results table, beginninig with the header
function buildRow(item, attackSpeedColumn, uim)
restbl:addClass('wikitable align-center-1 sortable align-center')
local row = mw.html.create('tr')
:tag('tr')
:tag('td'):wikitext(item.image and '[[' .. item.image .. '|link=|' .. item.name .. ']]' or ''):done()
:tag('tdth'):wikitextaddClass('[[unsortable' .. item.name .. ):wikitext(']]'):done()
:tag('tdth'):wikitext(item.members and '[[File:Member icon.png|link=|Members]]' or '[[File:Free-to-play icon.png|link=|Free-to-play]]Name'):done()
:tag('th'):wikitext('Members'):done()
:tag('th'):wikitext('[[File:White dagger.png|Stab attack]]') :done()
for _, stat in ipairs(statOptions) do
:tag('th'):wikitext('[[File:White scimitar.png|Slash attack]]') :done()
local statNum = tonumber(item[stat])
:tag('th'):wikitext('[[File:White warhammer.png|Crush attack]]') :done()
row:tag('td'):wikitext((statFormat(item[stat]) or editbtn("'''?''' (edit)", item.name)) .. (stat == 'mdmg' and '%' or ''))
:tag('th'):wikitext('[[File:Magic icon.png|Magic attack]]') :done()
:addClassIf(statNum and (statNum > 0), 'table-positive')
:tag('th'):wikitext('[[File:Ranged icon.png|Ranged attack]]') :done()
:addClassIf(statNum and (statNum < 0), 'table-negative')
:tag('th'):wikitext('[[File:White dagger.png|Stab defence]]<sup>[[File:Defence icon.png]]</sup>') :done()
end
:tag('th'):wikitext('[[File:White scimitar.png|Slash defence]]<sup>[[File:Defence icon.png]]</sup>') :done()
:tag('th'):wikitext('[[File:White warhammer.png|Crush defence]]<sup>[[File:Defence icon.png]]</sup>') :done()
local weightNum = tonumber(item.weight)
:tag('th'):wikitext('[[File:Magic icon.png|Magic defence]]<sup>[[File:Defence icon.png]]</sup>') :done()
row:tag('td'):wikitext(item.weight)
:tag('th'):wikitext('[[File:Ranged icon.png|Ranged defence]]<sup>[[File:Defence icon.png]]</sup>') :done()
:tag('th'):wikitext('[[File:Strength icon.png|Strength]]') :done()
:tag('th'):wikitext('[[File:Ranged Strength icon.png|Ranged Strength]]') :done()
:tag('th'):wikitext('[[File:Magic Damage icon.png|Magic Damage]]') :done()
:tag('th'):wikitext('[[File:Prayer icon.png|Prayer]]') :done()
:tag('th'):wikitext('Weight') :done()
:done()
-- Create the rows for the output table
if(attackSpeedColumn) then
for _, item in ipairs(data) do --for each row of data, we take it and split it up, then put it into our table row. We also format them with + and - symbols here
if((item.speed == nil) or (item.speed < 0)) then
local row = restbl:tag('tr')
row:tag('td'):addClass('table-na nohighlight'):css('text-align', 'center'):wikitext('<small>N/A</small>')
:tag('td'):wikitext(item.image):done()
else
row:tag('td'):wikitext(item.speedname):done()
:tag('td'):wikitext(item.members):done()
for _, stat in ipairs(stats) do
local value = item[stat]
local value_num = tonumber(value)
row:tag('td'):wikitext(format(value))
:addClassIf(value_num and (value_num > 0), 'table-positive')
:addClassIf(value_num and (value_num < 0), 'table-negative')
end
row:tag('td'):wikitext(item.weight):done()
end
if(uim) then
local storeability = 'Shop'
if(item.emote) then
storeability = 'STASH'
elseif(item.costume) then
storeability = 'POH'
end
row:tag('td'):wikitext(storeability):done()
end
return tostring(restbl) --returns the table back to the template to be put onto the page
return row
end
function getData(slotName,mems,dmm,beta) --so this is the function that takes the slot name and gets all the data for the items
function createHeader(slotName, attackSpeedColumn, uim)
--first we set up our SMW query
local tabl = mw.html.create('table'):addClass('wikitable lighttable sortable sticky-header align-center-1 align-left-2 align-center-3 align-right-4 align-right-5 align-right-6 align-right-7 align-right-8 align-right-9 align-right-10 align-right-11 align-right-12 align-right-13 align-right-14 align-right-15 align-right-16 align-right-17 align-center-18'):done()
local headerq = tabl:tag('tr'){
'[[Equipment slot::'..slotName..']]', --we want everything that matches the slot we called, and then all the attached data below
header:tag('th'):attr('colspan', '2'):wikitext('Name'):done()
'?Is members only',
:tag('th'):wikitext('[[File:Member icon.png|link=|Members]]'):done()
'?Stab attack bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab attack]]'):done()
'?Slash attack bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White scimitar.png|link=|Slash attack]]'):done()
'?Crush attack bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White warhammer.png|link=|Crush attack]]'):done()
'?Magic attack bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic icon.png|link=|Magic attack]]'):done()
'?Range attack bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged icon.png|link=|Ranged attack]]'):done()
'?Stab defence bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
'?Slash defence bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White scimitar.png|link=|Slash defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
'?Crush defence bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White warhammer.png|link=|Crush defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
'?Magic defence bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic icon.png|link=|Magic defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
'?Range defence bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged icon.png|link=|Ranged defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
'?Strength bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Strength icon.png|link=|Strength]]'):done()
'?Ranged Strength bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged Strength icon.png|link=|Ranged Strength]]'):done()
'?Magic Damage bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic Damage icon.png|link=|Magic Damage]]'):done()
'?Prayer bonus',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Prayer icon.png|link=|Prayer]]'):done()
'?Category',
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Weight icon.png|link=|Weight]]'):done()
'?Image',
'?Weight',
if(attackSpeedColumn) then
limit = 1000,
header:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Watch.png|link=|Speed]]'):done()
order = 'ascending'
end
if(uim) then
header:tag('th'):wikitext('[[File:Marble magic wardrobe icon.png|link=]]Stored/Buy'):done()
end
return tabl
end
function filterData(data, slotName, options)
if(slotName == 'ammo') then slotName = 'Ammunition' end -- hacky shit until ammo/ammunition is resolved
slotCat = '[[Category:' .. paramTest.ucfirst(slotName) .. ' slot items]]'
if slotName == '2h' then
slotCat = '[[Category:Two-handed slot items]]'
end
local exclusionListCategories = {
{ options.beta, slotCat .. '[[Category:Beta items]]' },
{ options.discontinued, slotCat .. '[[Category:Discontinued content]]' },
{ options.dmm, slotCat .. '[[Category:Deadman seasonal items]]' },
{ options.emir, slotCat .. '[[Category:Emir\'s Arena]]' },
{ options.failedPoll, slotCat .. '[[Category:Pages containing information from failed polls]]' },
{ options.gauntlet, slotCat .. '[[Category:The Gauntlet]]' },
{ options.lms, slotCat .. '[[Category:Last Man Standing]]' },
{ options.quest, slotCat .. '[[Category:Quest items]]' },
}
local smwdata = mw.smw.ask(q) --this now asks the smw for all the data, and saves all of it in smwdata
local data = {} --setting this table up to insert processed data into
for _, item in ipairs(smwdata) do --for each item we found with our smw query
local exclusionList = { }
local process = true
for _, excl in ipairs(exclusionListCategories) do
local dmmcat = false
if(not excl[1]) then
local betacat = false
table.insert(exclusionList, excl[2])
if type(item['Category']) == 'table' then
for _, category in ipairs(item['Category']) do
if category == "[[:Category:Deadman seasonal items|Deadman seasonal items]]" then
dmmcat = true
elseif category == "[[:Category:Beta items|Beta items]]" then
betacat = true
end
end
end
if mems == 'Members' then
end
if not item['Is members only'] then
local pagesToExclude = #exclusionList > 0 and pagesWithCats(exclusionList) or {}
process = false
local emotePagesToInclude, costumePagesToInclude
end
if(options.uim) then
emotePagesToInclude = pagesWithCats({ slotCat .. '[[Category:Items needed for an emote clue]]' }) or {}
costumePagesToInclude = pagesWithCats({ slotCat .. '[[Category:Items storable in the costume room]]' }) or {}
end
-- Filter the data
local retData = {}
for _, item in ipairs(data) do
local keep = true
if(((options.members == 'members') and (item['members'] == false)) or
((options.members == 'f2p') and (item['members'] == true)) or
((contains(pagesToExclude, item['variantof'])) or (contains(pagesToExclude, item['name'])))) then
keep = false
end
if mems == 'F2P' then
if(options.uim) item['Is members only'] then
process = false
if((not (contains(emotePagesToInclude, item['variantof']) or (contains(emotePagesToInclude, item['name'])))) and
(not (contains(costumePagesToInclude, item['variantof']) or (contains(costumePagesToInclude, item['name'])))) and
(next(pagesWithConditions('[[Sells item::' .. item.name .. ']]')) == nil)) then
keep = false
elseif(contains(costumePagesToInclude, item['variantof']) or (contains(costumePagesToInclude, item['name']))) then
item.costume = true
elseif(contains(emotePagesToInclude, item['variantof']) or (contains(emotePagesToInclude, item['name']))) then
item.emote = true
end
end
if dmm == 'No' then
if(keep) dmmcat == true then
process = false
table.insert(retData, item)
end
end
if beta == 'No' then
if betacat == true then
end
process = false
mw.log(string.format('Filter: exclusion list size: %i, start size: %i, end size: %i, removed %i.', #pagesToExclude, #data, #retData, #data - #retData))
return retData
end
function loadData(slotName, attackSpeed, members)
local query = {
'[[Equipment slot::' .. slotName .. ']]',
'?=#-',
'?Stab attack bonus#-=astab',
'?Slash attack bonus#-=aslash',
'?Crush attack bonus#-=acrush',
'?Magic attack bonus#-=amagic',
'?Range attack bonus#-=arange',
'?Stab defence bonus#-=dstab',
'?Slash defence bonus#-=dslash',
'?Crush defence bonus#-=dcrush',
'?Magic defence bonus#-=dmagic',
'?Range defence bonus#-=drange',
'?Strength bonus#-=str',
'?Magic Damage bonus#-=mdmg',
'?Ranged Strength bonus#-=rstr',
'?Prayer bonus#-=prayer',
'?Weight#-=weight',
'?Is members only#-=members',
'?Image#-=image',
'?Is variant of#-=variantof',
offset = 0,
limit = 1000,
}
if(attackSpeed) then
table.insert(query, '?Weapon attack speed#-=speed')
end
local t1 = os.clock()
local smwData = mw.smw.ask(query)
local t2 = os.clock()
assert(smwData ~= nil and #smwData > 0, 'SMW query failed')
for _, item in ipairs(smwData) do
-- Rename the first parameter to name for clarity and ease of use
item['name'] = item[1]
item[1] = nil
if(item['image'] == nil) then
local hasDefaultFile = mw.title.new(item['name'] .. '.png', 'File'):getContent()
item['image'] = hasDefaultFile and 'File:' .. item['name'] .. '.png' or ''
elseif(type(item['image']) == 'table') then
item['image'] = item['image'][1]
end
-- Fix members values by defaulting when running into issue
if(type(item.members) == 'boolean') then
-- Short circuit
elseif(item.members == nil) then
item.members = false
elseif(type(item.members) == 'table') then
for _, mems in ipairs(item.members) do
if(mems == false) then
item.members = false
break
end
end
end
if process then
-- Fix weights with multiple values (Max cape), this may do nothing and is precautionary
local dataline = processData(item) --we process it (seen below) and then save it as a line
if(item.weight == nil) then
table.insert(data, dataline) --and then we insert that line into our data table to be returned
item.weight = 0
elseif(type(item.weight) == 'table') then
item.weight, _ = minimum(item.weight)
end
end
return data --and once we've processed all the data we send the data table back up to main for the formatting
mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))
return smwData
end
function processData(item) --this breaks up the smwdata bit into manageable little bites
-- JSON data dump entry-point
local name = item[1] or '' --this gets the item name, which by default is in the first position of the query results (lua starts at 1, not 0)
function p.dumpData(frame)
local args_name = frame:getParentstring.match(name, '|().args-)%]')
return p._dumpData(args)
local members = item['Is members only'] --members we have to do a bit extra for
end
if members == true then --if it is members, we use the members star
members = p2pIcon
function p._dumpData(args)
elseif members == false then --if not, use f2p star
local slot = args.slot
members = f2pIcon
assert(contains(slotOptions, slot), 'Invalid slot specified')
else
members = ''
local data = loadData(slot, true)
-- Tests indicate that JSON pretty-printing increases the size by a factor of 1.78.
-- Removing indenting results in an increase in size by a factor of 1.15.
-- The latter is a very reasonable trade-off to make the files more wiki-friendly.
local prefix = string.format('-- Data for item slot \'%s\' @ %s.\n-- Generated by Module:Slottable, function dumpData()\nreturn mw.text.jsonDecode([=[\n', slot, os.date('%F %T', os.time()))
local rawjson = mw.text.jsonEncode(data, mw.text.JSON_PRETTY)
local jsondata, subst = rawjson:gsub('\n%s+', '\n')
local postfix = '\n]=])\n'
mw.log(string.format('Dumping JSON data for item slot \'%s\'. Raw size: %d bytes, formatted size: %d bytes (factor: %.2f).', slot, rawjson:len(), jsondata:len(), jsondata:len() / rawjson:len()))
return prefix .. jsondata .. postfix
end
-- Turtle data dump entry-point
function p.dumpDataTTL(frame)
local args = frame:getParent().args
return p._dumpDataTTL(args)
end
function p._dumpDataTTL(args)
local slot = args.slot
assert(contains(slotOptions, slot), 'Invalid slot specified')
local data = loadData(slot, true)
local slot_
if(slot == "2h") then
slot_ = "zweihander"
else
slot_ = slot
end
local prefix = string.format('# Data for item slot \'%s\' @ %s.\n# Generated by Module:Slottable, function dumpDataTTL()\n\n', slot, os.date('%F %T', os.time()))
prefix = prefix .. "@prefix " .. slot_ .. ": <http://oldschool.runescape.wiki/rdf/" .. slot_ .. "/> .\n"
prefix = prefix .. "@prefix prop: <http://oldschool.runescape.wiki/rdf/prop/> .\n\n"
local ttlData = ""
for _, i in ipairs(data) do
ttlData = ttlData .. slot_ .. ":" .. _ .. " " .. "prop:slot \"" .. slot_ .. "\"; "
local outl = {}
for k, v in pairs(i) do
if((k == "image") or (k == "name") or (k == "variantof")) then
table.insert(outl, "prop:" .. k .. " \"" .. tostring(v) .. "\"")
else
table.insert(outl, "prop:" .. k .. " " .. tostring(v))
end
end
ttlData = ttlData .. table.concat(outl, "; ") .. " .\n"
end
local postfiximage = item['Image'] or '\n\n'
if type(image) == 'table' then
mw.log(string.format('Dumping Turtle data for item slot \'%s\'. Size: %d bytes.', slot, ttlData:len()))
image = image[1] -- take the first image available
end
return prefix .. ttlData .. postfix
image = string.match(image, '[Ff]ile:.-%.png') or ''
end
if image ~= '' then
image = string.format('[[%s|link=%s]]', image, _name)
function p._main(args)
local slot = string.lower(paramTest.default_to(args.slot, ''))
local members = string.lower(paramTest.default_to(args.members, 'all'))
assert(contains(slotOptions, slot), 'Invalid slot specified')
assert(contains(memberOptions, members), 'Invalid members status specified')
local beta = yesNo(paramTest.default_to(args.beta, false), false)
local discontinued = yesNo(paramTest.default_to(args.discontinued, false), false)
local dmm = yesNo(paramTest.default_to(args.dmm, false), false)
local emir = yesNo(paramTest.default_to(args.emir, false), false)
local failedPoll = yesNo(paramTest.default_to(args.failedpoll, false), false)
local gauntlet = yesNo(paramTest.default_to(args.beta, false), false)
local lms = yesNo(paramTest.default_to(args.lms, false), false)
local quest = yesNo(paramTest.default_to(args.quest, true), true)
-- UIM specific tables, that only display items that can be:
-- purchased, stored in the POH, or stored in STASH untsi
local uim = yesNo(paramTest.default_to(args.uim, false), false)
local attackSpeed = false
if((slot == '2h') or (slot == 'weapon')) then
attackSpeed = true
end
local data = loadData(slot, attackSpeed, members, uim)
data = filterData(data, slot, {members = members, uim = uim, beta = beta, discontinued = discontinued, dmm = dmm, emir = emir, failedPoll = failedPoll, gauntlet = gauntlet, lms = lms, quest = quest })
local keyset=""
local ret = createHeader(slot, attackSpeed, uim)
local n=0
for _, item in ipairs(data) do
ret:node(buildRow(item, attackSpeed, uim))
for k,v in pairs(item) do
n=n+1
keyset = keyset .. k .. " (" .. tostring(v) ..")" .. ":"
end
return { --now we return the processed data for this item back to the getData function, which sends us another item and repeats until we've done them all
return ret
name = name,
image = image,
members = members,
astab = item['Stab attack bonus'] or '',
aslash = item['Slash attack bonus'] or '',
acrush = item['Crush attack bonus'] or '',
amagic = item['Magic attack bonus'] or '',
arange = item['Range attack bonus'] or '',
dstab = item['Stab defence bonus'] or '',
dslash = item['Slash defence bonus'] or '',
dcrush = item['Crush defence bonus'] or '',
dmagic = item['Magic defence bonus'] or '',
drange = item['Range defence bonus'] or '',
str = item['Strength bonus'] or '',
rstr = item['Ranged Strength bonus'] or '',
mdmg = item['Magic Damage bonus'] or keyset,
prayer = item['Prayer bonus'] or '',
weight = item['Weight'] or '',
}
end
p.getData = getData
function p.main(frame)
local args = frame:getParent().args
return p._main(args)
end
--[[ DEBUG
mw.logObject(p.getData('weapon'))
p._main({slot='weapon', members='all'})
p._dumpData({slot='weapon'})
p._dumpDataTTL({slot='weapon'})
--]]
return p
|