Module:Infobox Recipe

Revision as of 19:04, 18 December 2024 by Alsang (talk | contribs) (added a tooltip to the album xp display that explains it is a one-time reward)
Module documentation
This documentation is transcluded from Module:Infobox Recipe/doc. [edit] [history] [purge]
Module:Infobox Recipe's function _main is invoked by Template:Infobox Recipe.
Module:Infobox Recipe loads data from Module:Experience/data.

This module invoked using the command

{{#invoke:Infobox Recipe|_main}}

It provides this functionality to Template:Infobox Recipe


require('strict')
require('Module:Mw.html extension')
local currency = require('Module:Currency')
local hc = require('Module:Param Parse').has_content
local yn = require('Module:Yesno')
local search = require('Module:RecipeTreeSearch')
local editButton = require('Module:Edit button')
local album_xp_data = mw.loadData('Module:Experience/data').album
local lang = mw.language.getContentLanguage()
local tooltip = require('Module:Tooltip')

local function formatNum(n)
	if n == nil then
		return ''
	end
	return lang:formatNum(n)
end

local function currency_cell(amount)
	return currency._cell(amount, { html = 'yes' })
end

local p = {}

function p._main(frame)
	local args = frame:getParent().args

	--If set to true, will check if any of the items passed as a rawmaterialparam are actually intermediate materials. Will then change the display order of the infobox.
	local showFullRecipe = yn(args.showFull or 'no', false)
	
	--If set to true, will check hide the album xp section as the item does not give album xp.
	local hideAlbum = yn(args.hideAlbum or 'no', false)
	
	--Checks if the recipe has been flagged as passive, defaults to no.
	local passive = yn(args.passive or 'no', false)
	
	--Get each of the rawmatX parameters from params and store their values in a new table
	local argsMaterials = p._extractRawMaterials(args)

	--empty tables to hold materials
	local rawMaterials = {}
	local intermediateMaterials = {}

	--counter for total price of raw materials
	local rawMaterialCost = 0

	--get the value for the output product
	local output1Value = 0
	if args.output1 then
		output1Value = mw.smw.ask('[[:+]][[' .. args.output1 .. ']]|?Value|limit=1') or 0
	end

	--Calculate total value of output material(s)
	local output1TotalValue = 0
	if type(output1Value) == "table" then
		if output1Value[1]['Value'] ~= nil then
			output1TotalValue = output1Value[1]['Value'] * (tonumber(args.output1qty) or 1)
		else
			output1TotalValue = 0
		end
	end
	
	-- if the recipe is part of a quest
	args.quest = args.quest or false

	--Set SMW properties early, so latter parts can potentially use it.
	local smw_properties = {
		['Uses item'] = {},
		-- these are for when a recipe is called by a subsequent recipe that uses showFull
		['Uses facility'] = args.facility,
		-- these are for generating tables of profession xp rates and profits
		['Activity XP'] = args.exp,
		['Activity album XP'] = album_xp_data[tonumber(args.level)],
		['Activity KP'] = args.kp,
		['Activity duration'] = args.duration,
		['Activity level'] = args.level and tonumber(args.level),
		
		['Recipe JSON'] = mw.text.jsonEncode({
			xp = args.exp and tonumber(args.exp),
			kp = args.kp and tonumber(args.kp),
			duration = args.duration and tonumber(args.duration),
			materials = argsMaterials,
			profession = args.profession,
			passive = passive,
			level = args.level and tonumber(args.level),
			-- Make sure to update this when multiple outputs are supported
			output = {
				{ name = args.output1, quantity = tonumber(args.output1qty) or 1 },
			}
		}),
		-- Make sure to update this when multiple outputs are supported to be a list of all outputs
		['Recipe output'] = { args.output1 }
	}
	for _, material in ipairs(argsMaterials) do
		table.insert(smw_properties['Uses item'], material.name)
	end

	mw.smw.set(smw_properties)


	--Check if any of the raw mats provided are intermediate products, if they are, return their own raw materials
	if showFullRecipe then
		
		-- Use the RecipeTreeSearch module to get all the rawMaterials needed to make output1
		local searchResult = search.treeSearch(argsMaterials)
		rawMaterials = searchResult.materials
		intermediateMaterials = p._reverseTable(searchResult.intermediateMaterials)

		-- display XP and duration totals for multi-step process
		args.displayXP = tonumber(args.exp) and searchResult.xp and searchResult.xp + tonumber(args.exp)
		args.displayDuration = tonumber(args.duration) and searchResult.duration and searchResult.duration + tonumber(args.duration)
		
	else
		rawMaterials = argsMaterials
	end
	
	--Simple query to get the shop buy price for the provided material, if no buy price is available returns 0
	local function getBuyPrice(material)
		local queryResult = mw.smw.ask('[[:+]][[Sold item::' .. material['name'] .. ']]|?Shop buy price|sort=Shop buy price|order=asc|limit=1') or 0
		if type(queryResult) == "table" and queryResult[1]['Shop buy price'] then
			return tonumber(queryResult[1]['Shop buy price']) or 0
		end
		return 0
	end



	--If one of the raw materials provided as a param has been identified as an intermediate material then get the facility it is created at to display in the intermediate Ingredient row
	local function getFacility(material)
		local result = mw.smw.ask('[[:+]][[' .. material .. ']]|?Uses facility|limit=1') or 'unknown'
		local pageName = ''
		if result and result[1] and result[1]["Uses facility"] then
			local usesFac = result[1]["Uses facility"]
			pageName = usesFac:gsub("%[%[", ""):gsub("%]%]", ""):gsub("|.*", ""):gsub("^:", "")
		end
		return pageName
	end
	
	local album_xp = album_xp_data[tonumber(args.level)]
	if (album_xp) then
		album_xp = formatNum(album_xp)..' xp'
	end

	--Creates a row suitable for the raw materials section of the infobox.
	local function createRawMaterialRow(item)
		local materialBuyPrice = getBuyPrice(item) * (item.quantity or 1)
		rawMaterialCost = rawMaterialCost + materialBuyPrice
		return mw.html.create('tr')
			:tag('td')
				:css{ ['border-right'] = 'none' }
				:wikitext('[[File:' .. item.name .. '.png|30px|link=' .. item.name .. ']]')
			:done()
			:tag('td')
				:css{ ['border-left'] = 'none' }
				:wikitext('[[' .. item.name .. ']]')
			:done()
			:tag('td')
				:css{ ['text-align'] = 'right' }
				:wikitext(item.quantity or 1)
			:done()
			:node(currency_cell(materialBuyPrice))
		:done()
	end



	--Creates a row suitable for the intermediate materials section of the infobox.
	local function createIntermediateMaterialRow(item)
		local facility = getFacility(item.name)
		return mw.html.create('tr')
			:tag('td')
				:css{ ['border-right'] = 'none' }
				:wikitext('[[File:' .. item.name .. '.png|30px|link=' .. item.name .. ']]')
			:done()
			:tag('td')
				:css{ ['border-left'] = 'none' }
				:wikitext('[[' .. item.name .. ']]')
			:done()
			:tag('td')
				:css{ ['text-align'] = 'right' }
				:wikitext(item.quantity or 1)
			:done()
			:tag('td')
				:attr{ colspan = '10' }
				:css{ ['text-align'] = 'center' }
				:wikitext('[[File:' .. facility .. '.png|30px|link=' .. facility .. ']] [[' .. facility .. ']]')
			:done()
		:done()
	end



-- Recipe Table Head 
	local out = mw.html.create('table')
		:addClass('wikitable')
		:tag('tr')
			:tag('th')
				:attr{ colspan = '13' }
				:wikitext('Requirements')
			:done()
		:done()
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Facility')
			:done()
			:tag('td')
				:attr{ colspan = '11' }
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.facility) and ('[[File:%s.png|link=%s|30px]] [[%s]]'):format(args.facility, args.facility, args.facility) or editButton("'''?''' (edit)").. '[[Category:Needs facility recipe]]')

			:done()
		:done()
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:IF(passive)
					:wikitext('[[File:Passive small icon.png|20x20px|link=Passive Activity]] [[Passive Activity|Passive]] [[Experience]] ')
					:node(tooltip._span{ 'passive' })
					:node(tooltip._div{ name = 'passive', content = 'Passive activities give reduced experience when a players level is high enough to perform a new passive activity for that profession. The number shown here is the full experience.' })
				:ELSE()
					:wikitext('[[Experience]]')
				:END()
			:done()
			:tag('td')
				:attr{ colspan = '11' }
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.exp) and (formatNum(tonumber(args.exp))) or editButton("'''?''' (edit)").. '[[Category:Needs experience recipe]]')
				:IF(showFullRecipe)
					:wikitext(' (')
					:wikitext(hc(args.displayXP) and (formatNum(tonumber(args.displayXP))) or ("'''?'''"))
					:wikitext(' total)')
				:END()
			:done()
		:done()
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Duration')
			:done()
			:tag('td')
				:attr{ colspan = '11' }
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.duration) and ('%s seconds'):format(args.duration) or editButton("'''?''' (edit)").. '[[Category:Needs duration recipe]]')
				:IF(showFullRecipe)
					:wikitext(' (')
					:wikitext(hc(args.displayDuration) and (args.displayDuration) or ("'''?'''"))
					:wikitext(' total)')
				:END()
			:done()
		:done()
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Profession')
			:done()
			:tag('th')
				:wikitext('Level')
			:done()
			:tag('th')
				:attr{ colspan = '5' }
				:wikitext('[[Knowledge|KP]]')
			:done()
			:tag('th')
				:attr{ colspan = '5' }
				:wikitext('[[Album|Album XP]] ')
				:node(tooltip._span{ 'album' })
				:node(tooltip._div{ name = 'album', content = 'Album XP is a bonus awarded once for the first time the activity is performed.' })
			:done()
		:done()
		:tag('tr')
			:tag('td')
				:attr{ colspan = '2' }
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.profession) and ('[[' .. args.profession .. ']]') or editButton("'''?''' (edit)").. '[[Category:Needs profession recipe]]')
			:done()
			:tag('td')
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.level) and (args.level) or editButton("'''?''' (edit)").. '[[Category:Needs level recipe]]')
			:done()
			:tag('td')
				:attr{ colspan = '5' }
				:css{ ['text-align'] = 'center' }
				:wikitext(hc(args.kp) and ('%s%%'):format(args.kp) or editButton("'''?''' (edit)").. '[[Category:Needs knowledge recipe]]')
			:done()
			:tag('td')
				:attr{ colspan = '5' }
				:css{ ['text-align'] = 'center' }
				:IF(hideAlbum)
					:wikitext('N/A')
				:ELSE()
					:wikitext(album_xp or 'Unknown')
				:END()
			:done()

		:done()

-- Add raw materials
	-- Headers
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Raw Ingredient')
			:done()
			:tag('th')
				:wikitext('Quantity')
			:done()
			:tag('th')
				:attr{ colspan = '10' }
				:wikitext('Cost')
			:done()
		:done()

	-- Values
	for _, material in ipairs(rawMaterials) do
		out:node(createRawMaterialRow(material))
	end

-- Add total raw cost
	out
		:tag('tr')
			:tag('th')
				:attr{ colspan = '3' }
				:wikitext('Total Raw cost')
			:done()
			:node(currency_cell(rawMaterialCost))
		:done()

-- Add Intermediate steps if required
	-- Headers
	if next(intermediateMaterials) ~= nil then
		-- Headers
		out
			:tag('tr')
				:tag('th')
					:attr{ colspan = '2' }
					:wikitext('Intermediate Ingredient')
				:done()
				:tag('th')
					:wikitext('Quantity')
				:done()
				:tag('th')
					:attr{ colspan = '10' }
					:wikitext('Faciltity')
				:done()
			:done()

	-- Values
		for _, material in ipairs(intermediateMaterials) do
			mw.logObject(material)
			out:node(createIntermediateMaterialRow(material))
		end
	end

-- Add output data
	-- Headers
	out
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Output')
			:done()
			:tag('th')
				:wikitext('Quantity')
			:done()
			:tag('th')
				:attr{ colspan = '10' }
				:wikitext('Value')
			:done()
		:done()
		:IF(args.output1)

	-- Values
			:tag('tr')
				:tag('td')
					:css{ ['border-right'] = 'none' }
					:wikitext('[[File:' .. args.output1 .. '.png|30px|link=' .. args.output1 .. ']]' or 'Unknown')
				:done()
				:tag('td')
					:css{ ['border-left'] = 'none' }
					:wikitext('[[' .. args.output1 .. ']]' or 'Unknown')
				:done()
				:tag('td')
					:css{ ['text-align'] = 'right' }
					:wikitext(args.output1qty or 1)
				:done()
				:node(currency_cell(output1TotalValue))
			:done()
		:END()
	-- Add profit data
		:tag('tr')
			:addClass('currency')
			:tag('th')
				:attr{ colspan = '3' }
				:wikitext('Profit')
			:done()
			:node(currency_cell(output1TotalValue - rawMaterialCost))
		:done()

	return out
end



function p._extractRawMaterials(args)
	local rawMaterials = {}

	for i = 1, 9 do
		local materialKey = "rawmat" .. i
		local quantityKey = "rawmat" .. i .. "qty"

		local materialValue = args[materialKey]
		if materialValue and materialValue ~= "" then
			local quantityValue = tonumber(args[quantityKey]) or 1
			table.insert(rawMaterials, { name = materialValue, quantity = quantityValue })
		end
	end
	return rawMaterials
end

function p._reverseTable(t)
    local reversed = {}
    for i = #t, 1, -1 do
        table.insert(reversed, t[i])
    end
    return reversed
end

return p