Module:Infobox Recipe

From Brighter Shores Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Infobox Recipe/doc

local currency = require('Module:Currency')
local parse = require('Module:Param Parse')
local yn = require('Module:Yesno')

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)

	--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 = args.output1 and mw.smw.ask('[[:+]][[' .. args.output1 .. ']]|?Value|limit=1')
	local output1TotalValue = 0
	if output1Value[1]['Value'] ~= nil then
		output1TotalValue = output1Value[1]['Value'] * (tonumber(args.output1qty) or 1)
	else
		output1TotalValue = 0
	end

	--Check if any of the raw mats provided are intermediate products, if they are, return their own raw materials
	if showFullRecipe then
		local Materials = p._getTrueRawMaterials(argsMaterials)
		if next(Materials) ~= nil then
			rawMaterials = Materials.rawMaterials
			intermediateMaterials = p._reverseTable(Materials.intermediateMaterials)
		end
	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

	--Set SMW properties
	for _,material in ipairs(argsMaterials) do
			mw.smw.set({
			["Uses item"] = material.name,
			["Uses item_and_quantity"] = material.name .. ',' .. tostring(material.quantity)
		})
	end
	if args.facility then
		mw.smw.set({
			["Uses facility"] = args.facility
		})
	end

	--Creates a row suitable for the raw materials section of the infobox.
	local function createRawMaterialRow(item)
		local materialBuyPrice = getBuyPrice(item)
		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()
			:wikitext(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'] = 'left' }
				: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' }
				:wikitext(args.facility and ('[[File:%s.png|link=%s|30px]] [[%s]]'):format(args.facility, args.facility, args.facility) or 'Unknown')
			:done()
		:done()
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Profession')
			:done()
			:tag('th')
				:wikitext('Level')
			:done()
			:tag('th')
				:attr{ colspan = '10' }
				:wikitext('XP')
			:done()
		:done()
		:tag('tr')
			:tag('td')
				:attr{ colspan = '2' }
				:css{ ['text-align'] = 'center' }
				:wikitext(args.profession and '[[' .. args.profession .. ']]' or 'Unknown')
			:done()
			:tag('td')
				:css{ ['text-align'] = 'center' }
				:wikitext(args.level or 'Unknown')
			:done()
			:tag('td')
				:attr{ colspan = '10' }
				:css{ ['text-align'] = 'center' }
				:wikitext(args.exp or 'Unknown')
			: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()
			:wikitext(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
	out
		-- Headers
		:tag('tr')
			:tag('th')
				:attr{ colspan = '2' }
				:wikitext('Output')
			:done()
			:tag('th')
				:wikitext('Quantity')
			:done()
			:tag('th')
				:attr{ colspan = '10' }
				:wikitext('Value')
			:done()
		:done()

		-- Values
		:tag('tr')
			:tag('td')
				:css{ ['border-right'] = 'none' }
				:wikitext('[[File:' .. args.output1 .. '.png|30px|link=' .. args.output1 .. ']]')
			:done()
			:tag('td')
				:css{ ['border-left'] = 'none' }
				:wikitext(args.output1 and '[[' .. args.output1 .. ']]' or 'Unknown')
			:done()
			:tag('td')
				:css{ ['text-align'] = 'right' }
				:wikitext(args.output1qty or 1)
			:done()
			:wikitext(currency_cell(output1TotalValue))
		:done()

		-- Add profit data
		:tag('tr')
			:tag('th')
				:attr{ colspan = '3' }
				:wikitext('Profit')
			:done()
			:wikitext(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

--This function takes a table containing the raw materials params received from the calling template. 
--For each of the raw materials, a query will be executed to see if it has the property 'Uses item'.
--If it does, that means that the 'raw material' is actually an 'intermediate material' as it is itself created.
--The materials required to create this intermediate material will be retrieved and added to the rawMaterials table.
--This allows for a full recipe to be shown where only intermediate ingredients were provided.
function p._getTrueRawMaterials(argsMaterials)
    local rawMaterials = {}
    local intermediateMaterials = {}

    local function _processMaterial(material, quantity)
        if material then
            local result = mw.smw.ask('[[:+]][[' .. material .. ']]|?Uses item|?Uses item_and_quantity')
            if result and result[1] and result[1]["Uses item"] then
                -- The item is an intermediate material, add it to intermediateMaterials
                table.insert(intermediateMaterials, { ["name"] = material, ["quantity"] = quantity })
                
                -- Add the raw materials used by this intermediate material to rawMaterials
                local usesItems = result[1]["Uses item"]
                local quantities = result[1]["Uses item and quantity"]

                if type(usesItems) == "table" then
                    for index, usedItem in ipairs(usesItems) do
                        local pageName = usedItem:gsub("%[%[", ""):gsub("%]%]", ""):gsub("|.*", ""):gsub("^:", "")
                        local qty = 1
                        if quantities and type(quantities) == "table" then
                            local quantityString = quantities[index]
                            local _, q = quantityString:match("(.-),(%d+)")
                            qty = tonumber(q) or 1
                        end
                        _processMaterial(pageName, qty)
                    end
                else
                    local pageName = usesItems:gsub("%[%[", ""):gsub("%]%]", ""):gsub("|.*", ""):gsub("^:", "")
                    local qty = 1
                    if quantities and type(quantities) == "string" then
                        local _, q = quantities:match("(.-),(%d+)")
                        qty = tonumber(q) or 1
                    end
                    _processMaterial(pageName, qty)
                end
            else
                -- The item is a raw material, add it to rawMaterials
                table.insert(rawMaterials, { ["name"] = material, ["quantity"] = quantity })
            end
        end
    end

    -- Iterate over the initial argsMaterials
    for _, item in pairs(argsMaterials) do
        if item["name"] then
            _processMaterial(item["name"], item["quantity"])
        end
    end

    return { rawMaterials = rawMaterials, intermediateMaterials = intermediateMaterials }
end

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

return p