Module:Sandbox/User:Towelcat/Recipe

From Old School RuneScape Wiki
Jump to: navigation, search
Module documentation
This documentation is transcluded from Template:Module sandbox/doc. [edit] [purge]

This module is a sandbox for Towelcat. It can be used to test changes to existing modules, prototype new modules, or just experimenting with lua features.

Invocations of this sandbox should be kept in userspace; if the module is intended for use in other namespaces, it should be moved out of the sandbox into a normal module and template.

This default documentation can be overridden by creating the /doc subpage of this module, as normal.

--<nowiki>

local p = {}

-- convert some used globals to locals to improve performance
local math = math
local string = string
local table = table
local mw = mw
    local expr = mw.ext.ParserFunctions.expr

local clean_image = require('Module:Clean image').clean
local coins = require('Module:Coins')._amount
local yesno = require('Module:Yesno')
local params = require('Module:Paramtest')
local commas = require('Module:Addcommas')
local geprice = require('Module:Exchange')._price
local skillpic = require('Module:SCP')._main
local editbutton = require('Module:Edit button')
local onmain = require('Module:Mainonly').on_main

-- Tools that need special handling
local toolsList = {
	['Axe'] = '[[File:Bronze axe.png|link=Axe]]',
}

local facilitiesIcons = {
    ['Anvil'] = '[[File:Anvil icon.png|link=Anvil]]',
    ['Apothecary'] = '[[File:Apothecary icon.png|link=Apothecary]]',
    ['Banner easel'] = '[[File:Banner easel icon.png|link=Banner easel]]',
    ['Barbarian anvil'] = '[[File:Anvil icon.png|link=Anvil]]',
    ['Blast furnace'] = '[[File:Furnace icon.png|link=Blast furnace]]',
    ['Brewery'] = '[[File:Brewery icon.png|link=Brewery]]',
    ['Clay oven'] = '[[File:Cooking range icon.png|link=Clay oven]]',
    ['Cooking range'] = '[[File:Cooking range icon.png|link=Cooking range]]',
    ['Crafting table 1'] = '[[File:Crafting table 1 icon.png|link=Crafting table 1]]',
    ['Crafting table 2'] = '[[File:Crafting table 2 icon.png|link=Crafting table 2]]',
    ['Crafting table 3'] = '[[File:Crafting table 3 icon.png|link=Crafting table 3]]',
    ['Crafting table 4'] = '[[File:Crafting table 4 icon.png|link=Crafting table 4]]',
    ['Dairy churn'] = '[[File:Dairy churn icon.png|link=Dairy churn]]',
    ['Demon lectern'] = '[[File:Demon lectern icon.png|link=Demon lectern]]',
    ['Eagle lectern'] = '[[File:Eagle lectern icon.png|link=Eagle lectern]]',
    ['Fancy Clothes Store'] = '[[File:Clothes shop icon.png|link=Fancy Clothes Store]]',
    ['Farming patch'] = '[[File:Farming patch icon.png|link=Farming/Patch_locations]]',
    ['Furnace'] = '[[File:Furnace icon.png|link=Furnace]]',
    ['Loom'] = '[[File:Loom icon.png|link=Loom]]',
    ['Mahogany demon lectern'] = '[[File:Mahogany demon lectern icon.png|link=Mahogany demon lectern]]',
    ['Mahogany eagle lectern'] = '[[File:Mahogany eagle lectern icon.png|link=Mahogany eagle lectern]]',
    ['Metal Press'] = '[[File:Furnace icon.png|link=Metal Press]]',
    ['Oak lectern'] = '[[File:Oak lectern icon.png|link=Oak lectern]]',
    ['Pluming stand'] = '[[File:Pluming stand icon.png|link=Pluming stand]]',
    ['Pottery wheel'] = '[[File:Pottery wheel icon.png|link=Pottery wheel]]',
    ['Sawmill'] = '[[File:Sawmill icon.png|link=Sawmill]]',
    ['Sandpit'] = '[[File:Sandpit icon.png|link=Sandpit]]',
    ['Shield easel'] = '[[File:Shield easel icon.png|link=Shield easel]]',
    ['Singing bowl'] = '[[File:Singing bowl icon.png|link=Singing bowl]]',
    ['Spinning wheel'] = '[[File:Spinning wheel icon.png|link=Spinning wheel]]',
    ['Tannery'] = '[[File:Tannery icon.png|link=Tannery]]',
    ['Taxidermist'] = '[[File:Taxidermist icon.png|link=Taxidermist]]',
    ['Teak demon lectern'] = '[[File:Teak demon lectern icon.png|link=Teak demon lectern]]',
    ['Teak eagle lectern'] = '[[File:Teak eagle lectern icon.png|link=Teak eagle lectern]]',
    ['Thakkrad Sigmundson'] = '[[File:Tannery icon.png|link=Thakkrad Sigmundson]]',
    ['Water'] = '[[File:Water source icon.png|link=Water]]',
    ['Whetstone'] = '[[File:Whetstone icon.png|link=Whetstone]]',
    ['Windmill'] = '[[File:Windmill icon.png|link=Windmill]]',
    ['Woodcutting stump'] = '[[File:Woodcutting stump icon.png|link=Woodcutting stump]]',
}

function p.main(frame)
	local args = frame:getParent().args
	
	local function to_number_expr(param)
		if tonumber(commas._strip(param),10) then
			return tonumber(commas._strip(param),10)
		elseif pcall(function() expr(param) end) then
			return tonumber(expr(param))
		end
		return 'error_not_number'
	end
	
	local function cost_to_number(cost_v, name)
		if cost_v == nil then
			if pcall(function() geprice(name) end) then
				return geprice(name)
			else
				return 'error_GE_lookup'
			end
		elseif string.lower(cost_v) == 'no' then
			return 'N/A'
		else
			return to_number_expr(cost_v)
		end
	end

	local function mat_list()
		local ret_list = {}
		for i=1,10,1 do
			local mat = args['mat'..i]
			if mat and params.has_content(mat) then
				local name = mat
				local txt = params.default_to(args['mat'..i..'txt'], nil)
				local qty = params.default_to(args['mat'..i..'quantity'], 1)
				local img = params.default_to(args['mat'..i..'pic'], name..'.png')
				local cost_v = args['mat'..i..'cost']

				table.insert(ret_list, {
					name = name,
					txt = txt,
					cost = cost_to_number(cost_v, name),
					quantity = to_number_expr(qty),
					image = clean_image({file=img, link=mat}),
				} )
			end
		end
		return ret_list
	end
	
	local function skill_list()
		local ret_list = {}
		for i=1,10,1 do
			local skill = args['skill'..i]
			if skill and params.has_content(skill) then
				local name = skill
				local lvl = params.default_to(args['skill'..i..'lvl'],'1')
				local exp = params.default_to(args['skill'..i..'exp'],'0')
				table.insert(ret_list, {
					name = name,
					level = lvl,
					experience = exp,
				} )
			end
		end
		return ret_list
	end

	local name = args.outputname or mw.title.getCurrentTitle().text
	local txt = args.outputtxt or nil
	local image_name = args.outputpic or (name .. '.png')
	local output = {
		name = name,
		txt = txt,
		image = clean_image({file=image_name, link=name}),
		quantity = to_number_expr(args.outputquantity or 1),
		cost = cost_to_number(args.outputcost, name),
		outputnote = args.outputitemnote or nil,
		quantitynote = args.outputquantitynote or nil
	}

	local materials = mat_list()
	local skills = skill_list()

	local members = ''
	if params.has_content(args.members) then
		members = yesno(args.members, true)
		if members then
			members = 'Yes'
		else
			members = 'No'
		end
	end
	
	local nosmw
	if params.has_content(args.nosmw) then
		nosmw = true
	end

	return p._main(frame, args, args.tools, skills, members, args.notes, materials, output, args.facilities, args.ticks, args.ticksnote, nosmw)
end

--
-- Main
--
function p._main(frame, args, tools, skills, members, notes, materials, output, facilities, ticks, ticksnote, nosmw)
	local hasreftag = false
	local function make_row(item_data)
		local classOverride_cost, classOverride_qty
		local textAlign = 'right'
		local mat_ttl, mat_qty = 0
		local output_cost = (tonumber(item_data.quantity) or 1) * (tonumber(item_data.cost) or 0)
		if item_data.quantity == 'error_not_number' then
			mat_qty = 'Error: invalid quantity parameter'
			classOverride_qty = 'table-no'
		else
			mat_qty = commas._add(item_data.quantity)
		end
		
		if item_data.cost == 'error_not_number' then
			mat_ttl = 'Error: invalid cost parameter'
			classOverride_cost = 'table-no'
		elseif item_data.cost == 'error_GE_lookup' then
			mat_ttl = 'Error: could not find exchange price for item \'' .. item_data.name .. '\''
			classOverride_cost = 'table-no'
		elseif item_data.cost == 'N/A' then
			mat_ttl = '<small>Untradeable</small>'
			classOverride_cost = 'table-na'
			textAlign = 'center'
		else
			mat_ttl = coins(output_cost)
		end
		
		local name = item_data.txt and string.format('[[%s|%s]]', item_data.name, item_data.txt) or string.format('[[%s]]', item_data.name)
		local itemnote = item_data.outputnote and frame:extensionTag{ name = 'ref', content = item_data.outputnote, args = { group = 'r' } } or ''
		local quantitynote = item_data.quantitynote and frame:extensionTag{ name = 'ref', content = item_data.quantitynote, args = { group = 'r' } } or ''
		return mw.html.create('tr')
			:tag('td'):wikitext(item_data.image):done()
			:tag('td'):wikitext(name .. itemnote):done()
			:tag('td'):addClass(classOverride_qty):wikitext(mat_qty .. quantitynote):done()
			:tag('td'):addClass(classOverride_cost):css({ ['text-align'] = textAlign }):wikitext(mat_ttl):done(),
				output_cost,
				note ~= nil
	end
	
	local function toolImages(t)
		local images = {}
				
		if t == nil then
			return 'None'
		end
		
		local spl = mw.text.split(t, ",")
		for _, image_i in ipairs(spl) do
			image_i = mw.text.trim(image_i)
			if toolsList[image_i] then
				table.insert(images, toolsList[trimmed])
			else
				table.insert(images, string.format("[[File:%s.png|link=%s]]", image_i, image_i))
			end
		end
		return table.concat(images)
	end
	
	local function facilityLinks(f)
		local links = {}
		
		if f == nil then
			return 'None'
		end
		
		local spl = mw.text.split(f, ",")
		for _, link_i in ipairs(spl) do
			if facilitiesIcons[link_i] ~= nil then
				table.insert(links, string.format("%s [[%s]]", facilitiesIcons[link_i], link_i))
			else
				table.insert(links, string.format("[[%s]]", link_i))
			end
		end
		return table.concat(links, "<br />")
	end
	local parent = mw.html.create('div')
			:css({width = 'max-content' })

	local requirements = mw.html.create('table')
			:addClass('wikitable align-center-2 align-right-3')
			:css({ width = '100%',
				['margin'] = '0' })
	
	requirements:tag('caption'):wikitext("Requirements"):done()
	local tr = requirements:tag('tr')
		if #skills ~= 0 then
			tr:tag('th'):attr('colspan', 2):wikitext('Skill'):done()
			tr:tag('th'):wikitext('Level'):done()
			tr:tag('th'):wikitext('XP'):done()
		end	
	
	local membersTemplate = editbutton('?')
	if members == 'Yes' then
		membersTemplate = "[[File:Member icon.png|center|link=Members]]"	
	elseif members == 'No' then
		membersTemplate = "[[File:Free-to-play icon.png|center|link=Free-to-play]]"	
	end

	if #skills ~= 0 then
		for i, v in ipairs(skills) do
			requirements:tag('tr')
				:tag('td'):attr('colspan', 2):wikitext(skillpic(v.name) .. ' [[' .. v.name .. ']]'):done()
				:tag('td'):wikitext(v.level):done()
				:tag('td'):wikitext(v.experience):done()
		end
	end
	
	if notes ~= nil then
		requirements:tag('tr')
			:tag('td'):attr('colspan', 4):wikitext(notes):done()
	end
	
	local tr = requirements:tag('tr')
	tr:tag('th'):wikitext('Members'):done()
	tr:tag('td'):wikitext(membersTemplate):done()
	tr:tag('th'):attr('title', 'Ticks per action'):wikitext('[[RuneScape clock#Length of a tick|Ticks]]'):done()
	if (ticks or '') == '' then
		tr:tag('td'):wikitext('?'):done()
	elseif string.lower(ticks) == 'na' then
		local classOverride = 'table-na'
		tr:tag('td'):addClass(classOverride):css({ ['text-align'] = center }):wikitext('N/A'):done()
	elseif string.lower(ticks) == 'varies' then
		local note = ''
		if ticksnote ~= nil then
			note = frame:extensionTag{ name='ref', content = ticksnote, args = { group='r' } }
			hasreftag = true
		end
		tr:tag('td'):wikitext('Varies' .. note):done()
	else
		local secs = tonumber(ticks, 10) * 0.6
		local note = ''
		if ticksnote ~= nil then
			note = frame:extensionTag{ name='ref', content = ticksnote, args = { group='r' } }
			hasreftag = true
		end
		tr:tag('td'):attr('title', ticks .. ' ticks (' .. secs .. 's) per action'):wikitext(ticks .. ' (' .. secs .. 's) ' .. note):done()
	end
	
	if tools ~= nil or facilities ~= nil then
		local toolImgs = toolImages(tools)
		local facilityLnks = facilityLinks(facilities)
		requirements:tag('tr')
			:tag('th'):wikitext('Tools'):done()
			:tag('td'):css({ ['text-align'] = 'center' }):wikitext(toolImgs):done()
			:tag('th'):wikitext('Facilities'):done()
			:tag('td'):css({ ['text-align'] = 'center' }):wikitext(facilityLnks):done()
	end
	
	local materialsTable = mw.html.create('table')
			:addClass('wikitable align-center-1 align-right-3 align-right-4')
			:css({ width = '100%',
				['margin-top'] = '-1px' })	

	materialsTable:tag('caption'):wikitext("Materials"):done()
	materialsTable:tag('tr')
		:tag('th'):attr('colspan', 2):wikitext('Item'):done()
		:tag('th'):wikitext('Quantity'):done()
		:tag('th'):wikitext('Cost'):done()
	local total_cost = 0

	for i, v in ipairs(materials) do
		row, row_cost = make_row(v)

		if row_cost ~= 0 then
			total_cost = total_cost + (tonumber(v.quantity) or 1) * v.cost
		end

		materialsTable:node(row)
	end
	
	if #materials == 0 then
		materialsTable:tag('tr')
			:tag('td'):attr('colspan','5'):css({ ['font-style'] = 'italic', ['text-align'] = 'center' }):wikitext('Materials unlisted '..editbutton()):done()
	else
		materialsTable:tag('tr')
			:tag('th'):attr('colspan', 3):css({['text-align'] = 'right'}):wikitext('Total Cost'):done()
			:tag('td'):css({['text-align'] = 'right'}):wikitext(coins(total_cost))
	end
	
	output_row, output_cost, has_output_note = make_row(output)
	hasreftag = hasreftag or has_output_note
	materialsTable:node(output_row)
	
	if output_cost > 0 then
		local profit = output_cost - total_cost
		local note 
		if (((6000 / (tonumber(ticks) or 5)) * profit) > 5000000) then
			note = ((frame:extensionTag{
				name = 'ref',
				content = 'Due to Grand Exchange prices changing based on trade volume, this profit may not be fully accurate if the components are infrequently traded.',
				args = { group = 'r' }
			}) .. '[[Category:Recipes with questionable profit]]')
		hasreftag = true
		else
			note = ''
		end
		
		materialsTable:tag('tr')
			:tag('th'):attr('colspan', 3):css({['text-align'] = 'right'}):wikitext('Profit'):done()
			:tag('td'):css({['text-align'] = 'right'}):wikitext(coins(output_cost - total_cost) .. note)
	end
	
	parent:node(requirements)
	parent:node(materialsTable)
	
	if not nosmw then
		local jsonObject = {skills = skills, materials = {}, output = output}
		local materialNames = {}
		for _, v in ipairs(materials) do
			table.insert(jsonObject.materials, {name = v.name, quantity = v.quantity})
			table.insert(materialNames, v.name)
		end
		
		mw.smw.set({
			["Uses material"] = materialNames,
			['Production JSON'] = mw.text.jsonEncode(jsonObject)
		})
	end

	local outro = ''
	if hasreftag then
		outro = '\n' .. frame:extensionTag{ name='references', args = { group='r' } }
	end

	return tostring(parent) .. categories(args) .. outro
end

function categories(args)
	if not onmain() then
		return ''
	end
	local cats = {}
	
	if (args.ticks or '') == '' then
		table.insert(cats, '[[Category:Recipes missing ticks]]')
	end
	
	if args.tools ~= nil then
		table.insert(cats, '[[Category:Recipes that require a tool]]')
	end
	
	if args.facilities ~= nil then
		table.insert(cats, '[[Category:Recipes that use a facility]]')
	end

	return table.concat(cats,'')
end

return p