Module:Uses facility list

From Roat Pkz
Revision as of 22:33, 30 March 2024 by Hefner (talk | contribs) (1 revision imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Documentation for this module may be created at Module:Uses facility list/doc

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, facility)
	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]]")
	elseif(not yesNo(recipe.members)) then
		ret:tag('td'):wikitext("[[File:Free-to-play icon.png|center|link=Free-to-play]]")
	end
	
	--Facility
	local facilityList = mw.html.create('ul'):addClass('products-materials')
	local facilitySortValue = nil
	local facilities = split(recipe.facilities, ",")
	for i, facil in ipairs(facilities) do
		facilityList:tag('li'):attr('data-sort-value', i == 1 and facil or ''):wikitext(facil) -- For highlighting _all_ facilities --- :addClass('production-selected')
	end
	ret:tag('td'):addClass('plainlist'):node(facilityList)
	
	-- 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('Facility'):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(facilities, limit, offset)
	local query = {
		'[[Uses facility::'.. table.concat(facilities, '||') ..']]',
		'[[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 facilities
	-- Iterate in reverse so pops do not interrupt iteration
	for i = #data, 1, -1 do
		if(not bothContain(facilities, data[i].facilities)) then
			table.remove(data, i)
		end
	end
	
	-- Sort table values by order of facility input
	if((#facilities == 1) or (#data == 1)) then
		return data
	else
		local ret = {}
		-- This tracks the current position of where inserts should occur
		local facilityCountOffset = {}
		for i = 1, #facilities, 1 do
			table.insert(facilityCountOffset, 0)
		end
		for i, recipe in ipairs(data) do
			local pos = 0
			for j, facil in ipairs(facilities) do
				pos = pos + facilityCountOffset[j]
				if(bothContain(recipe.facilities, facil)) then
					table.insert(ret, pos + 1, recipe)
					facilityCountOffset[j] = facilityCountOffset[j] + 1
					break
				end
			end
		end
		return ret
	end
end

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

	local facilities = {}
	if(string.find(facility, ',') == nil) then
		table.insert(facilities, trim(facility))
	else
		for _, facil in ipairs(split(facility, ",")) do
			table.insert(facilities, trim(facil))
		end
	end
	
	data = p.loadData(facilities, args.limit, args.offset)
	
	if(data == nil) then
		return 'Failed to find products using that facility - 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, facilities))
	end
	
	return tostring(ret)
end

--[[ DEBUG
= p._main({'Anvil'})
= p._main({'Crafting table 1, Crafting table 2'})
--]]

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

return p