Module:Uses tool list

From RuneRealm Wiki
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Module:Uses tool list/doc. [edit] [history] [purge]
Module:Uses tool list's function main is invoked by Template:Sandbox/User:Wolaznik/Uses tool list.
Module:Uses tool list's function main is invoked by Template:Uses tool list.
Module:Uses tool list requires Module:Addcommas.
Module:Uses tool list requires Module:SCP.
Module:Uses tool list requires Module:Yesno.

Generates {{Uses tool list}}.


local p = {}

local commas = require('Module:Addcommas')
local skillPic = require('Module:SCP')._main
local yesNo = require('Module:Yesno')
local lang = mw.getContentLanguage()
local trim = mw.text.trim
local split = mw.text.split
local jsonDecode = mw.text.jsonDecode

function buildRow(recipe, tool)
	local ret = mw.html.create('tr')
	
	-- Outputs
	ret:tag('td'):wikitext(recipe.output.image)
	ret:tag('td'):attr('data-sort-value', recipe.output.name):wikitext(commas._add(recipe.output.quantity) .. ' × [[' .. recipe.output.name .. ']]' .. (recipe.output.subtxt ~= nil and '<br/><small>(' .. recipe.output.subtxt .. ')</small>' or ''))

	-- Members
	if(yesNo(recipe.members)) then
		ret:tag('td'):wikitext("[[File:Member icon.png|center|link=Members|alt=Members]]")
	elseif(not yesNo(recipe.members)) then
		ret:tag('td'):wikitext("[[File:Free-to-play icon.png|center|link=Free-to-play|alt=Free-to-play]]")
	end
	
	--Tools
	local toolList = mw.html.create('ul'):addClass('products-materials')
	local toolSortValue = nil
	local tools = split(recipe.tools, ",")
	for i, toolV in ipairs(tools) do
		toolList:tag('li'):attr('data-sort-value', i == 1 and tooV or ''):wikitext(toolV) -- For highlighting _all_ tools --- :addClass('production-selected')
	end
	ret:tag('td'):addClass('plainlist'):node(toolList)
	
	-- Skills (level)
	-- Skills (xp)
	local skillList = mw.html.create('ul'):addClass('skills-list')
	local xpList = mw.html.create('ul'):addClass('skills-list')
	
	if(#recipe.skills == 0) then
		skillList:tag('li'):wikitext('None')
		xpList:tag('li'):wikitext('None')
		ret:tag('td'):addClass('table-na plainlist'):node(skillList)
		ret:tag('td'):addClass('table-na plainlist'):node(xpList)
	else
		for i, skill in ipairs(recipe.skills) do
			skillList:tag('li'):attr('data-sort-value', i == 1 and skill.level or ''):wikitext(skillPic(lang:ucfirst(skill.name), skill.level))
			xpList:tag('li'):attr('data-sort-value', i == 1 and skill.experience or ''):wikitext(commas._strip(skill.experience) ~= nil and skillPic(lang:ucfirst(skill.name), skill.experience) or skill.experience)
		end
		ret:tag('td'):addClass('plainlist'):node(skillList)
		ret:tag('td'):addClass('plainlist'):node(xpList)
	end

	-- Inputs
	local matList = mw.html.create('ul')
	local materialSortValue = nil
	for _, mat in ipairs(recipe.materials) do
		local quantity = string.gsub(mat.quantity, '%-', '–')
		if(materialSortValue == nil) then
			materialSortValue = quantity
		end
		matList:tag('li'):wikitext(string.format('%s × [[%s]]', commas._add(quantity), mat.name))
	end
	ret:tag('td'):addClass('plainlist'):attr('data-sort-value', materialSortValue):node(matList)
	
	return ret
end

function createHeader()
	local header = mw.html.create('table'):addClass('wikitable sortable products-list align-center-1 align-left-2 align-center-3'):done()
		header:tag('tr'):tag('th'):attr('colspan', '2'):wikitext('Product'):done()
			:tag('th'):wikitext('Members'):done()
			:tag('th'):wikitext('Tool'):done()
			:tag('th'):attr('data-sort-type', 'number'):wikitext('Skills'):done()
			:tag('th'):attr('data-sort-type', 'number'):wikitext('XP'):done()
			:tag('th'):wikitext('Materials'):done()
	return header
end

-- If both variables contain the same element as part of a list (or single string)
function bothContain(argOne, argTwo)
	if(argOne == nil) or (argOne == '') or (argTwo == nil) or (argTwo == '') then
		return false
	elseif((type(argOne) == 'string') and (string.find(argOne, ',') == nil)) and ((type(argTwo) == 'string') and (string.find(argTwo, ',') == nil)) then
		return trim(argOne) == trim(argTwo)
	else
		if(type(argOne) == 'string') then
			argOne = split(argOne, "%s*,%s*")
		end
		if(type(argTwo) == 'string') then
			argTwo = split(argTwo, "%s*,%s*")
		end
		for i, v in ipairs(argOne) do
			for j, w in ipairs(argTwo) do
				if(trim(w) == trim(v)) then
					return true
				end
			end
		end
		return false
	end
end

function p.loadData(tools, limit, offset)
	local query = {
		'[[Uses tool::'.. table.concat(tools, '||') ..']]',
		'[[Production JSON::+]]',
		'?=#-',
		'?Production JSON = json',
		limit = limit or 500,
		offset = offset or 0,
	}
	local t1 = os.clock()
	local smwData = mw.smw.ask(query)
	local t2 = os.clock()

	if(smwData == nil) then
		return nil
	end
	mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))

	data = {}
	for i, v in ipairs(smwData) do
		if type(v['json']) == 'string' then
			table.insert(data, jsonDecode(v['json']))
		elseif type(v['json']) == 'table' then
			for _, w in ipairs(v['json']) do
				table.insert(data, jsonDecode(w))
			end
		end
	end

	-- Remove recipes that do not use at least one of the input tools
	-- Iterate in reverse so pops do not interrupt iteration
	for i = #data, 1, -1 do
		if(not bothContain(tools, data[i].tools)) then
			table.remove(data, i)
		end
	end
	
	-- Sort table values by order of tool input
	if((#tools == 1)) then -- or (#data == 1)
		return data
	else
		local ret = {}
		-- This tracks the current position of where inserts should occur
		local toolCountOffset = {}
		for i = 1, #tools, 1 do
			table.insert(toolCountOffset, 0)
		end
		for i, recipe in ipairs(data) do
			local pos = 0
			for j, tool in ipairs(tools) do
				pos = pos + toolCountOffset[j]
				if(bothContain(recipe.tools, tool)) then
					table.insert(ret, pos + 1, recipe)
					toolCountOffset[j] = toolCountOffset[j] + 1
					break
				end
			end
		end
		return ret
	end
end

function p._main(args)
	local tool = args ~= nil and args[1] or mw.title.getCurrentTitle().text

	local tools = {}
	if(string.find(tool, ',') == nil) then
		table.insert(tools, trim(tool))
	else
		for _, toolV in ipairs(split(tool, ",")) do
			table.insert(tools, trim(toolV))
		end
	end
	
	data = p.loadData(tools, args.limit, args.offset)
	
	if(data == nil) then
		return 'Failed to find products using that tool - ensure it is spelled correctly. (ERR: no results from SMW)[[Category:Empty drop lists]]'
	end
	
	local ret = createHeader()
	for _, recipe in ipairs(data) do
		ret:node(buildRow(recipe, tools))
	end
	
	return tostring(ret)
end

--[[ DEBUG
= p._main({'Hammer'})
= p._main({'Hammer, Pickaxe'})
--]]

function p.main(frame)
	--mw.logObject(frame)
	local args = frame:getParent().args
	return p._main(args)
end

return p