Module:EmberFantasy/sandbox/Slottable: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(Created page with "-- <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 local p2pIcon = 'frameless|link=Pay-to-play' --these icons are for the later members/f2p stars local f2pIcon = 'frameless|link=Free-to-play' local stats = {'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dm...") |
No edit summary Tag: Reverted |
||
Line 1: | Line 1: | ||
-- <pre> |
|||
require('Module:Mw.html extension') |
|||
local p = {} |
local p = {} |
||
require('Module:Mw.html extension') |
|||
local format = require('Module:Format equipment stat').format --for formatting the stats with + and - symbols on output |
|||
local yesNo = require('Module:Yesno') |
|||
local p2pIcon = '[[File:Member icon.png|frameless|link=Pay-to-play]]' --these icons are for the later members/f2p stars |
|||
local paramTest = require('Module:Paramtest') |
|||
local f2pIcon = '[[File:Free-to-play icon.png|frameless|link=Free-to-play]]' |
|||
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 memberOptions = {'members', 'f2p', 'all'} |
|||
local stats = {'astab', 'aslash', 'acrush', 'amagic', 'arange', 'dstab', 'dslash', 'dcrush', 'dmagic', 'drange', 'str', 'rstr', 'mdmg', 'prayer'} |
|||
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 |
function statFormat(_arg, default) |
||
local arg = tonumber(_arg) |
|||
local args = frame:getParent().args --get the input from the template |
|||
if(not arg) then return (default or _arg) end |
|||
local slot = string.lower(tostring(args['slot'])) --take the slot input as the slot we want the table for |
|||
if(arg < 0) then return tostring(arg) end |
|||
local mems = args['mems'] |
|||
return '+' .. arg |
|||
local dmm = args['dmm'] |
|||
end |
|||
local beta = args['beta'] |
|||
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 |
|||
function buildRow(item, attackSpeedColumn, uim) |
|||
local restbl = mw.html.create('table') --start making our results table, beginninig with the header |
|||
local row = mw.html.create('tr') |
|||
restbl:addClass('wikitable align-center-1 sortable align-center') |
|||
:tag('td'):wikitext(item.image and '[[' .. item.image .. '|link=|' .. item.name .. ']]' or ''):done() |
|||
:tag('tr') |
|||
:tag('td'):wikitext('[[' .. item.name .. ']]'):done() |
|||
:tag('td'):wikitext(item.members and '[[File:Member icon.png|link=|Members]]' or '[[File:Free-to-play icon.png|link=|Free-to-play]]'):done() |
|||
:tag('th'):wikitext('Members'):done() |
|||
for _, stat in ipairs(statOptions) do |
|||
:tag('th'):wikitext('[[File:White dagger.png|Stab attack]]') :done() |
|||
local statNum = tonumber(item[stat]) |
|||
:tag('th'):wikitext('[[File:White scimitar.png|Slash attack]]') :done() |
|||
row:tag('td'):wikitext((statFormat(item[stat]) or editbtn("'''?''' (edit)", item.name)) .. (stat == 'mdmg' and '%' or '')) |
|||
:tag('th'):wikitext('[[File:White warhammer.png|Crush attack]]') :done() |
|||
:addClassIf(statNum and (statNum > 0), 'table-positive') |
|||
:tag('th'):wikitext('[[File:Magic icon.png|Magic attack]]') :done() |
|||
:addClassIf(statNum and (statNum < 0), 'table-negative') |
|||
:tag('th'):wikitext('[[File:Ranged icon.png|Ranged attack]]') :done() |
|||
end |
|||
:tag('th'):wikitext('[[File:White dagger.png|Stab defence]]<sup>[[File:Defence icon.png]]</sup>') :done() |
|||
:tag('th'):wikitext('[[File:White scimitar.png|Slash defence]]<sup>[[File:Defence icon.png]]</sup>') :done() |
|||
local weightNum = tonumber(item.weight) |
|||
:tag('th'):wikitext('[[File:White warhammer.png|Crush defence]]<sup>[[File:Defence icon.png]]</sup>') :done() |
|||
row:tag('td'):wikitext(item.weight) |
|||
:tag('th'):wikitext('[[File:Magic icon.png|Magic defence]]<sup>[[File:Defence icon.png]]</sup>') :done() |
|||
: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() |
|||
if(attackSpeedColumn) then |
|||
-- Create the rows for the output table |
|||
if((item.speed == nil) or (item.speed < 0)) 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 |
|||
row:tag('td'):addClass('table-na nohighlight'):css('text-align', 'center'):wikitext('<small>N/A</small>') |
|||
local row = restbl:tag('tr') |
|||
else |
|||
:tag('td'):wikitext(item.image):done() |
|||
:tag('td'):wikitext(item. |
row:tag('td'):wikitext(item.speed):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 |
end |
||
end |
|||
row:tag('td'):wikitext(item.weight):done() |
|||
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 |
end |
||
return row |
|||
return tostring(restbl) --returns the table back to the template to be put onto the page |
|||
end |
end |
||
function createHeader(slotName, attackSpeedColumn, uim) |
|||
function getData(slotName,mems,dmm,beta) --so this is the function that takes the slot name and gets all the data for the items |
|||
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() |
|||
--first we set up our SMW query |
|||
local |
local header = tabl:tag('tr') |
||
header:tag('th'):attr('colspan', '2'):wikitext('Name'):done() |
|||
'[[Equipment slot::'..slotName..']]', --we want everything that matches the slot we called, and then all the attached data below |
|||
:tag('th'):wikitext('[[File:Member icon.png|link=|Members]]'):done() |
|||
'?Is members only', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab attack]]'):done() |
|||
'?Stab attack bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White scimitar.png|link=|Slash attack]]'):done() |
|||
'?Slash attack bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White warhammer.png|link=|Crush attack]]'):done() |
|||
'?Crush attack bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic icon.png|link=|Magic attack]]'):done() |
|||
'?Magic attack bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged icon.png|link=|Ranged attack]]'):done() |
|||
'?Range attack bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done() |
|||
'?Stab 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() |
|||
'?Slash 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() |
|||
'?Crush 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() |
|||
'?Magic 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() |
|||
'?Range defence bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Strength icon.png|link=|Strength]]'):done() |
|||
'?Strength bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged Strength icon.png|link=|Ranged Strength]]'):done() |
|||
'?Ranged Strength bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic Damage icon.png|link=|Magic Damage]]'):done() |
|||
'?Magic Damage bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Prayer icon.png|link=|Prayer]]'):done() |
|||
'?Prayer bonus', |
|||
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Weight icon.png|link=|Weight]]'):done() |
|||
'?Category', |
|||
'?Image', |
|||
if(attackSpeedColumn) then |
|||
'?Weight', |
|||
header:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Watch.png|link=|Speed]]'):done() |
|||
limit = 1000, |
|||
end |
|||
order = 'ascending' |
|||
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 |
|||
local exclusionList = { } |
|||
for _, item in ipairs(smwdata) do --for each item we found with our smw query |
|||
for _, excl in ipairs(exclusionListCategories) do |
|||
local process = true |
|||
if(not excl[1]) then |
|||
local dmmcat = false |
|||
table.insert(exclusionList, excl[2]) |
|||
local betacat = false |
|||
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 |
end |
||
end |
|||
if mems == 'Members' then |
|||
local pagesToExclude = #exclusionList > 0 and pagesWithCats(exclusionList) or {} |
|||
if not item['Is members only'] then |
|||
local emotePagesToInclude, costumePagesToInclude |
|||
process = false |
|||
if(options.uim) then |
|||
end |
|||
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 |
end |
||
if mems == 'F2P' then |
|||
if(options.uim) then |
|||
if((not (contains(emotePagesToInclude, item['variantof']) or (contains(emotePagesToInclude, item['name'])))) and |
|||
process = false |
|||
(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 |
||
end |
end |
||
if dmm == 'No' then |
|||
if(keep) then |
|||
table.insert(retData, item) |
|||
process = false |
|||
end |
|||
end |
end |
||
if beta == 'No' then |
|||
end |
|||
if betacat == true then |
|||
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 |
||
end |
end |
||
-- Fix weights with multiple values (Max cape), this may do nothing and is precautionary |
|||
if process then |
|||
if(item.weight == nil) then |
|||
local dataline = processData(item) --we process it (seen below) and then save it as a line |
|||
item.weight = 0 |
|||
table.insert(data, dataline) --and then we insert that line into our data table to be returned |
|||
elseif(type(item.weight) == 'table') then |
|||
item.weight, _ = minimum(item.weight) |
|||
end |
end |
||
end |
end |
||
mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000)) |
|||
return data --and once we've processed all the data we send the data table back up to main for the formatting |
|||
return smwData |
|||
end |
end |
||
-- JSON data dump entry-point |
|||
function processData(item) --this breaks up the smwdata bit into manageable little bites |
|||
function p.dumpData(frame) |
|||
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) |
|||
local |
local args = frame:getParent().args |
||
return p._dumpData(args) |
|||
end |
|||
local members = item['Is members only'] --members we have to do a bit extra for |
|||
if members == true then --if it is members, we use the members star |
|||
function p._dumpData(args) |
|||
members = p2pIcon |
|||
local slot = args.slot |
|||
elseif members == false then --if not, use f2p star |
|||
assert(contains(slotOptions, slot), 'Invalid slot specified') |
|||
members = f2pIcon |
|||
else |
|||
local data = loadData(slot, true) |
|||
members = '' |
|||
-- 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 |
end |
||
local |
local postfix = '\n\n' |
||
mw.log(string.format('Dumping Turtle data for item slot \'%s\'. Size: %d bytes.', slot, ttlData:len())) |
|||
if type(image) == 'table' then |
|||
image = image[1] -- take the first image available |
|||
return prefix .. ttlData .. postfix |
|||
end |
|||
end |
|||
image = string.match(image, '[Ff]ile:.-%.png') or '' |
|||
if image ~= '' then |
|||
function p._main(args) |
|||
image = string.format('[[%s|link=%s]]', image, _name) |
|||
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 |
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 ret = createHeader(slot, attackSpeed, uim) |
|||
local keyset="" |
|||
for _, item in ipairs(data) do |
|||
local n=0 |
|||
ret:node(buildRow(item, attackSpeed, uim)) |
|||
for k,v in pairs(item) do |
|||
n=n+1 |
|||
keyset = keyset .. k .. " (" .. tostring(v) ..")" .. ":" |
|||
end |
end |
||
return ret |
|||
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 |
|||
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 |
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 |
return p |
Revision as of 00:13, 17 October 2024
Documentation for this module may be created at Module:EmberFantasy/sandbox/Slottable/doc
local p = {}
require('Module:Mw.html extension')
local yesNo = require('Module:Yesno')
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 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 statFormat(_arg, default)
local arg = tonumber(_arg)
if(not arg) then return (default or _arg) end
if(arg < 0) then return tostring(arg) end
return '+' .. arg
end
function buildRow(item, attackSpeedColumn, uim)
local row = mw.html.create('tr')
:tag('td'):wikitext(item.image and '[[' .. item.image .. '|link=|' .. item.name .. ']]' or ''):done()
:tag('td'):wikitext('[[' .. item.name .. ']]'):done()
:tag('td'):wikitext(item.members and '[[File:Member icon.png|link=|Members]]' or '[[File:Free-to-play icon.png|link=|Free-to-play]]'):done()
for _, stat in ipairs(statOptions) do
local statNum = tonumber(item[stat])
row:tag('td'):wikitext((statFormat(item[stat]) or editbtn("'''?''' (edit)", item.name)) .. (stat == 'mdmg' and '%' or ''))
:addClassIf(statNum and (statNum > 0), 'table-positive')
:addClassIf(statNum and (statNum < 0), 'table-negative')
end
local weightNum = tonumber(item.weight)
row:tag('td'):wikitext(item.weight)
if(attackSpeedColumn) then
if((item.speed == nil) or (item.speed < 0)) then
row:tag('td'):addClass('table-na nohighlight'):css('text-align', 'center'):wikitext('<small>N/A</small>')
else
row:tag('td'):wikitext(item.speed):done()
end
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 row
end
function createHeader(slotName, attackSpeedColumn, uim)
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 header = tabl:tag('tr')
header:tag('th'):attr('colspan', '2'):wikitext('Name'):done()
:tag('th'):wikitext('[[File:Member icon.png|link=|Members]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab attack]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White scimitar.png|link=|Slash attack]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White warhammer.png|link=|Crush attack]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic icon.png|link=|Magic attack]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged icon.png|link=|Ranged attack]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White dagger.png|link=|Stab defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White scimitar.png|link=|Slash defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:White warhammer.png|link=|Crush defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic icon.png|link=|Magic defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged icon.png|link=|Ranged defence]]<sup>[[File:Defence icon.png|link=]]</sup>'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Strength icon.png|link=|Strength]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Ranged Strength icon.png|link=|Ranged Strength]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Magic Damage icon.png|link=|Magic Damage]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Prayer icon.png|link=|Prayer]]'):done()
:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Weight icon.png|link=|Weight]]'):done()
if(attackSpeedColumn) then
header:tag('th'):attr('data-sort-type', 'number'):wikitext('[[File:Watch.png|link=|Speed]]'):done()
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 exclusionList = { }
for _, excl in ipairs(exclusionListCategories) do
if(not excl[1]) then
table.insert(exclusionList, excl[2])
end
end
local pagesToExclude = #exclusionList > 0 and pagesWithCats(exclusionList) or {}
local emotePagesToInclude, costumePagesToInclude
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(options.uim) then
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(keep) then
table.insert(retData, item)
end
end
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
-- Fix weights with multiple values (Max cape), this may do nothing and is precautionary
if(item.weight == nil) then
item.weight = 0
elseif(type(item.weight) == 'table') then
item.weight, _ = minimum(item.weight)
end
end
mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))
return smwData
end
-- JSON data dump entry-point
function p.dumpData(frame)
local args = frame:getParent().args
return p._dumpData(args)
end
function p._dumpData(args)
local slot = args.slot
assert(contains(slotOptions, slot), 'Invalid slot specified')
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 postfix = '\n\n'
mw.log(string.format('Dumping Turtle data for item slot \'%s\'. Size: %d bytes.', slot, ttlData:len()))
return prefix .. ttlData .. postfix
end
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 ret = createHeader(slot, attackSpeed, uim)
for _, item in ipairs(data) do
ret:node(buildRow(item, attackSpeed, uim))
end
return ret
end
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