Module:Infobox Recipe: Difference between revisions

From Brighter Shores Wiki
Jump to navigation Jump to search
Content added Content deleted
(swap positions of XP and Album XP, replace "XP" with "Experience" where there is space, make "Experience", "KP", and "Album" be links to their own pages)
m (different variable names for Full Recipe XP and passive's per-hour recipe XP)
 
(8 intermediate revisions by 2 users not shown)
Line 8: Line 8:
local album_xp_data = mw.loadData('Module:Experience/data').album
local album_xp_data = mw.loadData('Module:Experience/data').album
local lang = mw.language.getContentLanguage()
local lang = mw.language.getContentLanguage()
local tooltip = require('Module:Tooltip')


local function formatNum(n)
local function formatNum(n)
Line 27: Line 28:
--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.
--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)
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
--Get each of the rawmatX parameters from params and store their values in a new table
local argsMaterials = p._extractRawMaterials(args)
local argsMaterials = p._extractRawMaterials(args)
Line 53: Line 60:
end
end
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.
--Set SMW properties early, so latter parts can potentially use it.
Line 67: Line 77:
['Recipe JSON'] = mw.text.jsonEncode({
['Recipe JSON'] = mw.text.jsonEncode({
facility = args.facility,
xp = args.exp and tonumber(args.exp),
xp = args.exp and tonumber(args.exp),
kp = args.kp and tonumber(args.kp),
kp = args.kp and tonumber(args.kp),
duration = args.duration,
duration = args.duration and tonumber(args.duration),
materials = argsMaterials,
materials = argsMaterials,
profession = args.profession,
profession = args.profession,
passive = passive,
level = args.level and tonumber(args.level),
level = args.level and tonumber(args.level),
-- Make sure to update this when multiple outputs are supported
-- Make sure to update this when multiple outputs are supported
Line 97: Line 109:


-- display XP and duration totals for multi-step process
-- display XP and duration totals for multi-step process
args.displayXP = tonumber(args.exp) and searchResult.xp and searchResult.xp + tonumber(args.exp)
args.fullXP = 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)
args.displayDuration = tonumber(args.duration) and searchResult.duration and searchResult.duration + tonumber(args.duration)
Line 103: Line 115:
rawMaterials = argsMaterials
rawMaterials = argsMaterials
end
end
--If it is a passive recipe, will want to display the per-hour values for XP and materials used
args.recipePerHour = args.duration and math.floor(3600/args.duration)
args.displayXP = (tonumber(args.exp) and args.recipePerHour and tonumber(args.exp) * args.recipePerHour) or ''
args.displayOutput1qty = (args.output1qty and args.recipePerHour and args.output1qty * args.recipePerHour) or ''
--Simple query to get the shop buy price for the provided material, if no buy price is available returns 0
--Simple query to get the shop buy price for the provided material, if no buy price is available returns 0
Line 135: Line 152:
local materialBuyPrice = getBuyPrice(item) * (item.quantity or 1)
local materialBuyPrice = getBuyPrice(item) * (item.quantity or 1)
rawMaterialCost = rawMaterialCost + materialBuyPrice
rawMaterialCost = rawMaterialCost + materialBuyPrice
local displayQuantity = item.quantity or 1
if passive then
displayQuantity = displayQuantity and args.recipePerHour and displayQuantity * args.recipePerHour
end
return mw.html.create('tr')
return mw.html.create('tr')
:tag('td')
:tag('td')
Line 147: Line 168:
:css{ ['text-align'] = 'right' }
:css{ ['text-align'] = 'right' }
:wikitext(item.quantity or 1)
:wikitext(item.quantity or 1)
:IF(passive)
:wikitext('<br>( ' .. displayQuantity .. ' per hour)' )
:END()
:done()
:done()
:node(currency_cell(materialBuyPrice))
:node(currency_cell(materialBuyPrice))
Line 204: Line 228:
:tag('th')
:tag('th')
:attr{ colspan = '2' }
:attr{ colspan = '2' }
:wikitext('[[Experience]]')
: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()
:done()
:tag('td')
:tag('td')
Line 212: Line 242:
:IF(showFullRecipe)
:IF(showFullRecipe)
:wikitext(' (')
:wikitext(' (')
:wikitext(hc(args.displayXP) and (formatNum(tonumber(args.displayXP))) or ("'''?'''"))
:wikitext(hc(args.fullXP) and (formatNum(tonumber(args.fullXP))) or ("'''?'''"))
:wikitext(' total)')
:wikitext(' total)')
:END()
:IF(passive)
:wikitext(' (')
:wikitext(hc(args.displayXP) and (formatNum(tonumber(args.displayXP))) or ("'''?'''"))
:wikitext(' per hour)')
:END()
:END()
:done()
:done()
Line 247: Line 282:
:tag('th')
:tag('th')
:attr{ colspan = '5' }
:attr{ colspan = '5' }
:wikitext('[[Album|Album XP]]')
: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()
:done()
:done()
Line 268: Line 305:
:attr{ colspan = '5' }
:attr{ colspan = '5' }
:css{ ['text-align'] = 'center' }
:css{ ['text-align'] = 'center' }
:IF(hideAlbum)
:wikitext(album_xp or 'Unknown')
:wikitext('N/A')
:ELSE()
:wikitext(album_xp or 'Unknown')
:END()
:done()
:done()

:done()
:done()


Line 360: Line 402:
:css{ ['text-align'] = 'right' }
:css{ ['text-align'] = 'right' }
:wikitext(args.output1qty or 1)
:wikitext(args.output1qty or 1)
:IF(passive)
:wikitext('<br>( ' .. args.displayOutput1qty .. ' per hour)' )
:END()
:done()
:done()
:node(currency_cell(output1TotalValue))
:node(currency_cell(output1TotalValue))
Line 402: Line 447:
return reversed
return reversed
end
end




return p
return p

Latest revision as of 19:28, 28 December 2024

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({
			facility = args.facility,
			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.fullXP = 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
	
	--If it is a passive recipe, will want to display the per-hour values for XP and materials used
	args.recipePerHour = args.duration and math.floor(3600/args.duration)
	args.displayXP = (tonumber(args.exp) and args.recipePerHour and tonumber(args.exp) * args.recipePerHour) or ''
	args.displayOutput1qty = (args.output1qty and args.recipePerHour and args.output1qty * args.recipePerHour) or ''
	
	--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
		local displayQuantity = item.quantity or 1
		if passive then
			displayQuantity = displayQuantity and args.recipePerHour and displayQuantity * args.recipePerHour
		end
		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)
				:IF(passive)
					:wikitext('<br>( ' .. displayQuantity .. ' per hour)' )
				:END()
			: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.fullXP) and (formatNum(tonumber(args.fullXP))) or ("'''?'''"))
					:wikitext(' total)')
				:END()
				:IF(passive)
					:wikitext(' (')
					:wikitext(hc(args.displayXP) and (formatNum(tonumber(args.displayXP))) or ("'''?'''"))
					:wikitext(' per hour)')
				: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)
					:IF(passive)
						:wikitext('<br>( ' .. args.displayOutput1qty .. ' per hour)' )
					:END()
				: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