Module:Recipe: Difference between revisions

From Subnautica Wiki
(creating two new functions to facilitate two new templates to show power output for the Bioreactor and Nuclear Reactor)
(Adding basic function descriptions)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
--[[
This module standardizes the display of crafting recipes
and makes it easy to display a specified item's recipe
or all recipes that use a specified ingredient.
Thanks to User:BryghtShadow for helping out with the loops!
--]]

-- <nowiki>
-- <nowiki>
local p = {}
local p = {}
Line 14: Line 7:
local widthBig = 75
local widthBig = 75


-- verify which game is being used to make errors more helpful
function p.getGame(data)
if data == dataSN then
return "Subnautica"
elseif data == dataBZ then
return "Below Zero"
end
return ""
end

-- verify if item exists and return its ID
function p.getItemID(id, data)
function p.getItemID(id, data)
-- removing disambiguations from pagenames
-- removing disambiguations from pagenames
Line 34: Line 38:
end
end


-- generate an icon, using widths, heights, and offsets based on global variables to line everything up smoothly
function p.getIcon(item, quantity, data, mid)
function p.getIcon(item, quantity, data, mid)
if not data[item] then error("Could not generate icon for item '" .. item .. "'") end
if not data[item] then error("Could not generate icon for " .. p.getGame(data) .. " item '" .. item .. "'") end
local iconOut = ""
local iconOut = ""
Line 50: Line 55:
local iconBG
local iconBG
if not image then
if not image then
image = name .. " Icon.png"
if data[item].icon then
if data[item].icon then
local iconBGSize = data[item].icon.size or "1x1"
local iconBGSize = data[item].icon.size or "1x1"
local iconBGType = data[item].icon.bg or "default"
local iconBGType = data[item].icon.bg or "default"
image = data[item].icon.file or name .. " Icon.png"
iconBG = iconBGSize .. " " .. iconBGType
iconBG = iconBGSize .. " " .. iconBGType
if iconBGSize == "1x2" then
if iconBGSize == "1x2" then
Line 65: Line 70:
else
else
iconBG = "1x1 default"
iconBG = "1x1 default"
image = name .. " Icon.png"
end
end
end
end
Line 96: Line 102:
end
end


-- output every recipe using an item and a machine (all machines by default)
function p.getAllRecipes(id, machine, data)
function p.getAllRecipes(id, machine, data)
-- First, get the ID of the product item provided by template; pagename used by default
-- First, get the ID of the product item provided by template; pagename used by default
local itemID = p.getItemID(id, data)
local itemID = p.getItemID(id, data)
if not itemID then error("Could not find item '" .. id .. "'") end
if not itemID then error("Could not find " .. p.getGame(data) .. " item '" .. id .. "'") end
-- Item is found, start finding all recipes using it
-- Item is found, start finding all recipes using it
local allProducts = {}
local allProducts = {}
for productID, product in pairs(data) do
for productID, product in pairs(data) do
productMachine = product.machine or ""
productMachine = product.machine or ""
if machine == "all" or machine == productMachine then
if machine == "all" or machine == productMachine then
for _, ingredient in ipairs(product.recipe or {}) do
for _, ingredient in ipairs(product.recipe or {}) do
local ingredientID = ingredient[1]
local ingredientID = ingredient[1]
local count = ingredient[2]
local count = ingredient[2]
if ingredientID == itemID then
if ingredientID == itemID then
allProducts[#allProducts+1] = productID
allProducts[#allProducts+1] = productID
end
end
end
end
end
end
end
end
if #allProducts == 0 then
if #allProducts == 0 then
if machine == "all" then
if machine == "all" then
error("No recipes use item '" .. id .. "'")
error("No " .. p.getGame(data) .. " recipes use item '" .. id .. "'")
else
else
if(data[machine]) then
if(data[machine]) then
error("No recipes use item '" .. id .."' and machine '" .. machine .. "'")
error("No " .. p.getGame(data) .. " recipes use item '" .. id .."' and machine '" .. machine .. "'")
else
else
error("Machine '" .. machine .. "' does not exist")
error(p.getGame(data) .. " machine '" .. machine .. "' does not exist")
end
end
end
end
end
end
local output = ""
local output = ""
for _, product in ipairs(allProducts) do
for _, product in ipairs(allProducts) do
output = output .. p.getRecipe(product, false, data)
output = output .. p.getRecipe(product, false, data)
end
end
return output
return output
end

-- test function to remove after testing all BZ recipes
function p.testAllRecipes()
local allProducts = {}
for productID, product in pairs(dataBZ) do
if dataBZ[productID].machine then
allProducts[#allProducts+1] = productID
end
end
local output = ""
for _, product in ipairs(allProducts) do
local ran, recipe = pcall(p.getRecipe, product, false, dataBZ)
if ran then
output = output .. recipe
else
output = output .. "<div class='recipe'>" .. recipe .. "</div>"
end
end

return output
end
end


-- output a single recipe or a single item's power output
function p.getRecipe(id, power, data)
function p.getRecipe(id, power, data)
-- First, get the ID of the product item provided by template; pagename used by default
-- First, get the ID of the product item provided by template; pagename used by default
local itemID = p.getItemID(id, data)
local itemID = p.getItemID(id, data)
if not itemID then error("Could not find recipe for item '" .. id .. "'") end
if not itemID then error("Could not find " .. p.getGame(data) .. " item '" .. id .. "'") end
-- Item is found, start processing it
-- Item is found, start processing it
Line 147: Line 177:
if data[itemID].original and not power then
if data[itemID].original and not power then
-- If the item is made as part of a different item's recipe
-- If the item is made as part of a different item's recipe
error ("No standalone recipe for item '" .. itemID .. "' (use '" .. data[itemID].original .. "' instead)")
error (p.getGame(data) " item '" .. itemID .. "' cannot be directly crafted (use '" .. data[itemID].original .. "' instead)")
elseif power then
elseif power then
-- power
-- power
Line 153: Line 183:
local machineID = ""
local machineID = ""
local outNum = 0
local outNum = 0
local usedRod = false
if data[itemID].energy then
if data[itemID].energy then
machineID = "basebioreactor"
machineID = "basebioreactor"
Line 159: Line 190:
machineID = "basenuclearreactor"
machineID = "basenuclearreactor"
outNum = data[itemID].nuclear
outNum = data[itemID].nuclear
usedRod = true
else
else
error("Item '" .. itemID .. "' does not generate power")
error(p.getGame(data) .. " item '" .. itemID .. "' does not generate power")
end
end
local iconMachine = p.getIcon(machineID, 0, data, false)
local iconMachine = p.getIcon(machineID, 0, data, false)
Line 172: Line 204:
local iconProduct = p.getIcon("energy", outNum, data, false)
local iconProduct = p.getIcon("energy", outNum, data, false)
output = output .. iconProduct
output = output .. iconProduct
if usedRod then
local iconRod = p.getIcon("depletedreactorrod", 1, data, false)
output = output .. iconRod
end
output = output .. "</div>"
output = output .. "</div>"
Line 177: Line 213:
else
else
-- standard recipe
-- standard recipe
if data[itemID].recipe and data[itemID].machine then
if data[itemID].machine then
-- Recipe is found, start writing the output
-- Item is craftable, start writing the output
output = output .. "<div class='recipe__machine'>"
output = output .. "<div class='recipe__machine'>"
local machineID = data[itemID].machine
local machineID = data[itemID].machine
if not data[machineID] then
if not data[machineID] then
error ("Invalid machine listed in recipe for item '" .. itemID .. "'")
error ("Invalid machine listed in " .. p.getGame(data) .. " recipe for item '" .. itemID .. "'")
end
end
local iconMachine = p.getIcon(machineID, 0, data, false)
local iconMachine = p.getIcon(machineID, 0, data, false)
output = output .. iconMachine .. "</div>"
output = output .. iconMachine .. "</div>"
if data[itemID].recipe then
output = output .. arrow .. "<div class='recipe__ingredients'>"
output = output .. arrow .. "<div class='recipe__ingredients'>"
for i,v in ipairs(data[itemID].recipe) do
for i,v in ipairs(data[itemID].recipe) do
local icon = p.getIcon(v[1], v[2], data, true)
local icon = p.getIcon(v[1], v[2], data, true)
output = output .. icon
output = output .. icon
end
output = output .. "</div>"
end
end
output = output .. "</div>"
output = output .. arrow .. "<div class='recipe__output'>"
output = output .. arrow .. "<div class='recipe__output'>"
Line 202: Line 240:
local addIcon
local addIcon
for i,v in ipairs(addOut) do
for i,v in ipairs(addOut) do
addIcon = p.getIcon(v[1], v[2], data, false)
addIcon = p.getIcon(v[1], v[2], data, false)
output = output .. addIcon
output = output .. addIcon
end
end
end
end
Line 211: Line 249:
else
else
if data[itemID].recipe then
if data[itemID].recipe then
error ("Machine is missing for item '" .. itemID .. "'")
error ("Machine is missing for " .. p.getGame(data) .. " item '" .. itemID .. "'")
elseif data[itemID].machine then
error ("Recipe is missing for item '" .. itemID .. "'")
else
else
error (p.getGame(data) .. " item '" .. itemID .. "' is not craftable")
-- If there is no recipe for the item
if data == dataBZ then
error ("Cannot craft item '" .. itemID .. "' (use Template:UsesBZ instead)")
else
error ("Cannot craft item '" .. itemID .. "' (use Template:UsesSN instead)")
end
end
end
end
end
Line 226: Line 257:
end
end


function p.SN(frame)
-- main function called by the template
function p.main(frame)
local args = getArgs(frame)
local args = getArgs(frame)
local id = args[1] or mw.title.getCurrentTitle().text
local id = args[1] or mw.title.getCurrentTitle().text
local source = args[2] or "SN"
return p.getRecipe(id, false, dataSN)
local func = args[3] or "recipe"
end

local dataSrc = dataSN
function p.allSN(frame)
if source == "BZ" then
local args = getArgs(frame)
dataSrc = dataBZ
local id = args[1] or mw.title.getCurrentTitle().text
end
local machine = args[2] or "all"
return p.getAllRecipes(id, machine, dataSN)
if func == "recipe" then
end
return p.getRecipe(id, false, dataSrc)

elseif func == "uses" then
function p.powerSN(frame)
local args = getArgs(frame)
local machine = args[4] or "all"
return p.getAllRecipes(id, machine, dataSrc)
local id = args[1] or mw.title.getCurrentTitle().text
elseif func == "power" then
return p.getRecipe(id, true, dataSN)
return p.getRecipe(id, true, dataSrc)
end
end

function p.BZ(frame)
local args = getArgs(frame)
local id = args[1] or mw.title.getCurrentTitle().text
return p.getRecipe(id, false, dataBZ)
end

function p.allBZ(frame)
local args = getArgs(frame)
local id = args[1] or mw.title.getCurrentTitle().text
local machine = args[2] or "all"
return p.getAllRecipes(id, machine, dataBZ)
end

function p.powerBZ(frame)
local args = getArgs(frame)
local id = args[1] or mw.title.getCurrentTitle().text
return p.getRecipe(id, true, dataBZ)
end
end



Latest revision as of 02:02, 23 July 2022

This module standardizes the display of crafting recipes and makes it easy to display a specified item's recipe or all recipes that use a specified ingredient. See Template:RecipeNew for more information. Thanks to BryghtShadow for helping out with the loops!


-- <nowiki>
local p = {}
local dataSN = mw.loadData('Module:Recipe/SN')
local dataBZ = mw.loadData('Module:Recipe/BZ')
local getArgs = require('Dev:Arguments').getArgs
local width = 50
local widthBig = 75

-- verify which game is being used to make errors more helpful
function p.getGame(data)
	if data == dataSN then
		return "Subnautica"
	elseif data == dataBZ then
		return "Below Zero"
	end
	return ""
end

-- verify if item exists and return its ID
function p.getItemID(id, data)
	-- removing disambiguations from pagenames
	id = string.gsub(id, " %(Subnautica%)", "")
	id = string.gsub(id, " %(Below Zero%)", "")
	
	-- if item ID is provided directly...
	if data[id] then
		return id
	end
	
	-- ...otherwise try to find by item name
	for k, v in pairs(data) do
		if v.name == id then -- item found
			return k
		end
	end
	
	return nil -- Could not find item
end

-- generate an icon, using widths, heights, and offsets based on global variables to line everything up smoothly
function p.getIcon(item, quantity, data, mid)
	if not data[item] then error("Could not generate icon for " .. p.getGame(data) .. " item '" .. item .. "'") end
	local iconOut = ""
	
	local iconWidth = width
	if not mid then
		iconWidth = widthBig
	end
	local iconHeight = iconWidth
	local iconOffset = 0
	
	local name = data[item].name
	
	local image = data[item].image
	local iconBG
	if not image then
		if data[item].icon then
			local iconBGSize = data[item].icon.size or "1x1"
			local iconBGType = data[item].icon.bg or "default"
			image = data[item].icon.file or name .. " Icon.png"
			iconBG = iconBGSize .. " " .. iconBGType
			if iconBGSize == "1x2" then
				iconHeight = iconWidth * 2
			elseif iconBGSize == "3x2" then
				iconHeight = (iconWidth / 3) * 2
			elseif iconBGSize == "2x3" then
				iconHeight = (iconWidth / 2) * 3
			end
			iconOffset = math.max(0, ((iconHeight - iconWidth) / 2))
		else
			iconBG = "1x1 default"
			image = name .. " Icon.png"
		end
	end
	
	iconOut = "<div class='recipe__icon' style='width:" .. iconWidth .. "px;'>"
	
	if iconBG then
		iconOut = iconOut .. "<span class='recipe__icon__bg'>[[File:" .. iconBG .. " bg.png|" .. iconWidth .. "px|link=]]</span>"
	end
	
	-- disambiguation check
	local namefix = ""
	if data == dataBZ then
		namefix = name .. " (Below Zero)"
	else
		namefix = name .. " (Subnautica)"
	end
	local exists = mw.getCurrentFrame():callParserFunction('PROTECTIONEXPIRY:edit', namefix) ~= ''
	if not exists then
		-- disambiguated poge does not exist
		namefix = name
	end
	
	iconOut = iconOut .. "<span class='recipe__icon__img' style='top:" .. iconOffset .. "px;'>[[File:" .. image .. "|" .. iconWidth .. "px|link=" .. namefix .. "|" .. namefix .. "]]</span><span class='recipe__icon__pseudo' style='width:" .. iconWidth .. "px;'><span class='recipe__icon__pseudo__img' style='height:" .. iconHeight .. "px;width:" .. iconWidth .. "px;'></span>×0</span>"
	
	if quantity > 1 then
		iconOut = iconOut .. "<span class='recipe__icon__quantity' style='top:" .. iconHeight .. "px;'>×" .. quantity .. "</span>"
	end
	
	return iconOut .. "</div>"
end

-- output every recipe using an item and a machine (all machines by default)
function p.getAllRecipes(id, machine, data)
	-- First, get the ID of the product item provided by template; pagename used by default
	local itemID = p.getItemID(id, data)
	if not itemID then error("Could not find " .. p.getGame(data) .. " item '" .. id .. "'") end
	
	-- Item is found, start finding all recipes using it
	local allProducts = {}
	for productID, product in pairs(data) do
		productMachine = product.machine or ""
		if machine == "all" or machine == productMachine then
			for _, ingredient in ipairs(product.recipe or {}) do
				local ingredientID = ingredient[1]
				local count = ingredient[2]
				if ingredientID == itemID then
					allProducts[#allProducts+1] = productID
				end
			end
		end
	end
	
	if #allProducts == 0 then
		if machine == "all" then
			error("No " .. p.getGame(data) .. " recipes use item '" .. id .. "'")
		else
			if(data[machine]) then
				error("No " .. p.getGame(data) .. " recipes use item '" .. id .."' and machine '" .. machine .. "'")
			else
				error(p.getGame(data) .. " machine '" .. machine .. "' does not exist")
			end
		end
	end
	
	local output = ""
	for _, product in ipairs(allProducts) do
		output = output .. p.getRecipe(product, false, data)
	end
	
	return output
end

-- test function to remove after testing all BZ recipes
function p.testAllRecipes()
	local allProducts = {}
	for productID, product in pairs(dataBZ) do
		if dataBZ[productID].machine then
			allProducts[#allProducts+1] = productID
		end
	end
	
	local output = ""
	for _, product in ipairs(allProducts) do
		local ran, recipe = pcall(p.getRecipe, product, false, dataBZ)
		if ran then
			output = output .. recipe
		else
			output = output .. "<div class='recipe'>" .. recipe .. "</div>"
		end
	end

	return output
end

-- output a single recipe or a single item's power output
function p.getRecipe(id, power, data)
	-- First, get the ID of the product item provided by template; pagename used by default
	local itemID = p.getItemID(id, data)
	if not itemID then error("Could not find " .. p.getGame(data) .. " item '" .. id .. "'") end
	
	-- Item is found, start processing it
	local output = "<div class='recipe'>"
	local arrow = "<div class='recipe__step'><div class='recipe__step__arrow'>[[File:Item Arrow.png|20px|link=]]</div><span class='recipe__step__pseudo'><span class='recipe__step__pseudo__img'></span>×0</span></div>"
	
	if data[itemID].original and not power then
		-- If the item is made as part of a different item's recipe
		error (p.getGame(data) " item '" .. itemID .. "' cannot be directly crafted (use '" .. data[itemID].original .. "' instead)")
	elseif power then
		-- power
		output = output .. "<div class='recipe__machine'>"
		local machineID = ""
		local outNum = 0
		local usedRod = false
		if data[itemID].energy then
			machineID = "basebioreactor"
			outNum = data[itemID].energy
		elseif data[itemID].nuclear then
			machineID = "basenuclearreactor"
			outNum = data[itemID].nuclear
			usedRod = true
		else
			error(p.getGame(data) .. " item '" .. itemID .. "' does not generate power")
		end
		local iconMachine = p.getIcon(machineID, 0, data, false)
		output = output .. iconMachine .. "</div>"
			
		output = output .. arrow .. "<div class='recipe__ingredients'>"
		output = output .. p.getIcon(itemID, 1, data, true)
		output = output .. "</div>"
		
		output = output .. arrow .. "<div class='recipe__output'>"
		local iconProduct = p.getIcon("energy", outNum, data, false)
		output = output .. iconProduct
		if usedRod then
			local iconRod = p.getIcon("depletedreactorrod", 1, data, false)
			output = output .. iconRod
		end
		output = output .. "</div>"
			
		return output .. "</div>"
	else
		-- standard recipe
		if data[itemID].machine then
			-- Item is craftable, start writing the output
			output = output .. "<div class='recipe__machine'>"
			local machineID = data[itemID].machine
			if not data[machineID] then
				error ("Invalid machine listed in " .. p.getGame(data) .. " recipe for item '" .. itemID .. "'")
			end
			local iconMachine = p.getIcon(machineID, 0, data, false)
			output = output .. iconMachine .. "</div>"
			
			if data[itemID].recipe then
				output = output .. arrow .. "<div class='recipe__ingredients'>"
				for i,v in ipairs(data[itemID].recipe) do
					local icon = p.getIcon(v[1], v[2], data, true)
					output = output .. icon
				end
				output = output .. "</div>"
			end
			
			output = output .. arrow .. "<div class='recipe__output'>"
			local outNum = data[itemID].quantity or 1
			local iconProduct = p.getIcon(itemID, outNum, data, false)
			output = output .. iconProduct
			if data[itemID].additional then
				local addOut = data[itemID].additional
				local addIcon
				for i,v in ipairs(addOut) do
					addIcon = p.getIcon(v[1], v[2], data, false)
					output = output .. addIcon
				end
			end
			output = output .. "</div>"
			
			return output .. "</div>"
		else
			if data[itemID].recipe then
				error ("Machine is missing for " .. p.getGame(data) .. " item '" .. itemID .. "'")
			else
				error (p.getGame(data) .. " item '" .. itemID .. "' is not craftable")
			end
		end
	end
end

-- main function called by the template
function p.main(frame)
	local args = getArgs(frame)
	local id = args[1] or mw.title.getCurrentTitle().text
	local source = args[2] or "SN"
	local func = args[3] or "recipe"
	
	local dataSrc = dataSN
	if source == "BZ" then
		dataSrc = dataBZ
	end
	
	if func == "recipe" then
		return p.getRecipe(id, false, dataSrc)
	elseif func == "uses" then
		local machine = args[4] or "all"
		return p.getAllRecipes(id, machine, dataSrc)
	elseif func == "power" then
		return p.getRecipe(id, true, dataSrc)
	end
end

return p
-- </nowiki>