Satisfactory Wiki
S'inscrire
Advertisement

La documentation pour ce module peut être créée à Module:RenderCraftingRecipesTable/doc

local checkType = require('libraryUtil').checkType;

local cargo = mw.ext.cargo
local moduleTooltips = require('Module:ItemTooltip')
local tableTools = require('Module:TableUtil');
local argsUtil = require('Module:ArgsUtil');

local p = {}
local buildings = {}
local experimental_style = 'position:absolute; font-variant:all-small-caps; font-size:small; font-weight:bold; left:0; top:0; transform: rotate(-45deg) translate(-15px, -5px); background:red; color:white; padding: 1px 20px;'
local unreleased_style = 'position: absolute; font-variant: all-small-caps; font-size: smaller; left: 0; width: 100%; font-weight: bold; background: repeating-linear-gradient( -45deg, yellow, yellow 8px, black 8px, black 16px); color: black; text-shadow: -1px -1px 0 #fff, -1px 1px 0 #fff, 1px -1px 0 #fff, 1px 1px 0 #fff; padding-bottom: 2px;'

local function loadBuildingsPower()
	local tables = 'buildings'
	local fields = 'name, powerUsage'
	local args = { where = 'powerUsage > 0' }
	local result = cargo.query(tables, fields, args)

	buildings = {}
	for _, building in pairs(result) do
		if building.name ~= nil then
			buildings[building.name] = tonumber(building.powerUsage)
		end
	end
end

local function round(num, numDecimalPlaces)
	local mult = 10^(numDecimalPlaces or 0)
	return math.floor(num * mult + 0.5) / mult
end

local function generateRecipeTableRow(recipe, useItemTooltips)
	local html = ''

	-- fix some parameters
	recipe.alternateRecipe = (tonumber(recipe.alternateRecipe) or 0) > 0
	recipe.inCraftBench = (tonumber(recipe.inCraftBench) or 0) > 0
	recipe.inWorkShop = (tonumber(recipe.inWorkShop) or 0) > 0
	recipe.craftingTime = tonumber(recipe.craftingTime) or 0
	recipe.avgPower = tonumber(recipe.avgPower)
	if recipe.craftedIn == nil or #tostring(recipe.craftedIn) <= 1 then -- <=1 instead of ==0 just in case of \n or single space characters; normally craftedIn is a long word
		if recipe.inWorkShop then
			recipe.craftedIn = "Atelier d'équipement"
		elseif recipe.inCraftBench then
			recipe.craftedIn = 'Établi'
		else
			recipe.craftedIn = 'Outil de construction'
		end
	end

	-- split ingredients and products from recipe into separate arrays
	local ingredients = {}
	for i=1, 9, 1 do
		local item = recipe["ingredient" .. i]
		if item ~= nil and #item > 0 then
			ingredients[i] = { name = item, amount = tonumber(recipe["quantity" .. i]) or 1 }
		end
	end

	local products = {}
	for i=1, 4, 1 do
		local item = recipe["product" .. i]
		if item ~= nil and #item > 0 then
			products[i] = { name = item, amount = tonumber(recipe["productCount" .. i]) or 1 }
		end
	end

	-- calculations for table cells
	local ingredientsCols = #ingredients <= 1 and 1 or math.max(2, math.ceil(#ingredients/2))
	local ingredientsRows = math.ceil(#ingredients/ingredientsCols)
	local ingredientsColspan = 12 / ingredientsCols -- ingredients column is 12 columns wide (with colspan) to be divisible by 1, 2, 3 or 4

	local productsCols = #products <= 1 and 1 or math.max(2, math.ceil(#products/2))
	local productsRows = math.ceil(#products/productsCols)
	local productsColspan = 2 / productsCols

	local maxRows = math.max(ingredientsRows, productsRows)
	local ingredientsRowspan = maxRows / ingredientsRows
	local productsRowspan = maxRows / productsRows

	local ingredientCells = {}
	local productCells = {}

	-- function generates HTML for ingredients/products cells and stores generated code in targetTable array
	local function generateCells(list, rowspan, colspan, targetTable, addEnergyPerItem)
		for i=1, #list, 1 do
			local td = '<td style="border:0; line-height:1; padding:0.5em" rowspan="' .. rowspan .. '" colspan="' .. colspan .. '">'
			local ipm = 60 / recipe.craftingTime * list[i].amount

			if useItemTooltips then
				--td = td .. mw.getCurrentFrame():expandTemplate{title = 'ItemTooltip', args = { list[i].name, list[i].amount }}
				local itemAmount = mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]=list[i].amount, [2]='La quantité de cette ressource par cycle de fabrication, à partir de laquelle la valeur par minute est calculée.', [3]='no underline'}}
				td = td .. moduleTooltips.getTooltip{ args = { item = list[i].name, amount = itemAmount, size = 40 } }
			else
				local itemAmount = list[i].amount .. ' × '
				td = td .. '<div style="line-height:1"><div style="font-size:medium; display:inline-table">' .. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]=itemAmount, [2]='La quantité de cette ressource par cycle de fabrication, à partir de laquelle la valeur par minute est calculée.', [3]='no underline'}} .. '[[Image:' .. list[i].name .. '.png|40px|link=' .. list[i].name .. ']]<br/>'
				td = td .. '<div style="font-size:x-small; text-align:right">' .. list[i].name .. '</div></div></div>'
			end

			if recipe.craftingTime > 0 then
				ipm = ipm .. ' / min'
				td = td .. '<div style="font-weight:bold; line-height:2">' .. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]=ipm, [2]='Quelle quantité de cette ressource doit être fournie à la machine ou retirée de celle-ci pour une efficacité maximale', [3]='no underline'}} .. '</div>'

				-- calculate energy per item
				if addEnergyPerItem then
					buildingPower = buildings[recipe.craftedIn]
					if buildingPower == nil then
						buildingPower = recipe.avgPower
					end
					if buildingPower ~= nil then
						local displayText = round(recipe.craftingTime * buildingPower / list[i].amount, 2) .. ' [[énergie|MJ]]/item'
						local tooltipText = "L'énergie consommée par un " .. recipe.craftedIn .. ' pour fabriquer 1 objet à une cadence de 100%, dans ce cycle de fabrication'
						td = td .. '<div style="font-size:x-small; line-height:1; border:solid 1px cornflowerblue; border-radius:0.5em; background-color:rgba(240,248,255,0.3); padding:0.08em 0.4em; display:inline">' .. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]=displayText, [2]=tooltipText, [3]='no underline'}} .. '</div>'
					end
				end
			else
				-- if no craftingTime and no ipm to display then add some empty block for proper spacing
				td = td .. '<div style="font-weight:bold; line-height:2">&nbsp;</div>'
			end
			td = td .. '</td>'
			targetTable[i] = td
		end
		-- odd amount of ingredients/products leaves one empty cell in the column, which should be filled by a blank cell
		-- also generate one empty cell when there's no ingredients/products at all
		if #list == 0 or #list > 1 and #list % 2 == 1 then
			targetTable[#targetTable+1] = '<td style="border:0" rowspan="' .. rowspan .. '" colspan="' .. colspan .. '">&nbsp;</td>'
		end
	end

	-- generate all ingredients and products cells
	generateCells(ingredients, ingredientsRowspan, ingredientsColspan, ingredientCells)
	generateCells(products, productsRowspan, productsColspan, productCells, true) -- for products add energy per item

	-- assemble the table
	for i=1, maxRows, 1 do
		-- recipe name column
		if i == 1 then
			recipeName = recipe.recipeName
			if (recipeName == nil or recipeName == '') then recipeName = recipe.product1 end
			if (recipeName == nil or recipeName == '') then recipeName = 'Not specified' end
			if recipe.alternateRecipe then
				recipeName = recipeName .. '<br/>' .. '<span style="text-transform:uppercase; font-size:x-small; background-color:cornsilk; padding:0.1em 0.4em; border-radius:0;border:solid 1px orange">[[Disque dur|Alternative]]</span>'
			end
			local stripe = ''
			if recipe.experimental == '1' then
				stripe = '<div style="' .. experimental_style .. '">'
					.. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]='Exp', [2]='Cette recette est disponible uniquement dans la version Expérimentale de Satisfactory.', [3]='no underline'}}
					.. '</div>'
			elseif recipe.unreleased == '1' then
				stripe = '<div style="' .. unreleased_style .. ' top:0;">'
					.. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]='Unreleased', [2]='Cette recette ne peut être obtenue en jeu. Elle a été soit dataminée, soit retirée du jeu.', [3]='no underline'}}
					.. '</div>'
					.. '<div style="' .. unreleased_style .. ' bottom:0;">&nbsp;</div>'
			end
			local season = recipe.season
			if (season ~= nil and season ~= '') then
				stripe = stripe .. mw.getCurrentFrame():expandTemplate{
					title = "CraftingTable/season",
					args  = { season = season },
				}
			end
			html = html
					.. '<tr class="firstRow" style="border-top: 2px solid #575757">'
					.. '<td rowspan="' .. maxRows .. '" style="line-height:1; position:relative; overflow:hidden;">'
					.. recipeName
					.. stripe
					.. '</td>'
		else
			html = html .. '<tr>'
		end

		-- ingredients column
		for j=1, ingredientsCols, 1 do
			local index = (i-1)*ingredientsCols+j
			if index <= #ingredientCells then
				html = html .. ingredientCells[index]
			end
		end

		-- building column
		if i == 1 then
			html = html .. '<td rowspan="' .. maxRows .. '"><span  style="font-size:large">[[' .. recipe.craftedIn .. ']]</span>'
			if recipe.craftingTime > 0 then
				html = html .. '<br/>' .. recipe.craftingTime .. ' sec'
			end
			if recipe.inCraftBench or recipe.inWorkShop then
				local displayText = '[[Fichier:Fabrication manuelle.png|16px|link=|class=fd_invert]] × ' .. tostring(recipe.craftingClicks)
				local tooltipText = 'Vitesse de fabrication manuelle : ' .. 240 / (tonumber(recipe.craftingClicks) or 0) * (tonumber(recipe.productCount1) or 1) .. '/min'
				html = html ..'<br/>' .. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]=displayText, [2]=tooltipText, [3]='no underline'}}
			end
			html = html .. '</td>'
		end

		-- products column
		for j=1, productsCols, 1 do
			local index = (i-1)*productsCols+j
			if index <= #productCells then
				html = html .. productCells[index]
			end
		end

		-- prerequisites column
		if i == 1 then
			html = html .. '<td rowspan="' .. maxRows .. '"><span style="text-align:left;">' ..  (recipe.researchTier or 0)  .. '</span></td>'
		end

		html = html .. '</tr>'
	end

	return html
end

local fields = 'recipeName, alternateRecipe, mainRecipe, craftedIn, inCraftBench, inWorkShop, craftingTime, craftingClicks, researchTier, unreleased, experimental, product=product1, product2, product3, product4, productCount=productCount1, productCount2, productCount3, productCount4, ingredient1, quantity1, ingredient2, quantity2, ingredient3, quantity3, ingredient4, quantity4, ingredient5, quantity5, ingredient6, quantity6, ingredient7, quantity7, ingredient8, quantity8, ingredient9, quantity9, ingredient10, quantity10, CONCAT((CASE craftedIn WHEN "Build Gun" THEN 1 ELSE 0 END)) = isBuilding'

if(pcall(mw.ext.cargo.query, "crafting_recipes", "crafting_recipes.season", { limit=1 })) then
	-- TODO: Merge this into the `fields` declaration above once an admin
	-- recreates the [[Special:CargoTables/crafting_recipes]] cargo table:
	fields = fields .. ', season'
end

local function generateTable(recipes, useItemTooltips)
	local html = '<table class="wikitable" style="text-align:center"><tr><th>Recette</th><th colspan="12">Ingrédients</th><th>Bâtiment</th><th colspan="2">Produits</th><th>Prérequis</th></tr>'

	for i=1, #recipes, 1 do
		if recipes == nil then
			return html
		end
		html = html .. generateRecipeTableRow(recipes[i], useItemTooltips)
	end

	html = html .. '</table>'
	return html
end

function p.generateCraftingRecipes(frame)
	local itemName = frame.args.item or frame.args[1]
	if itemName == nil then return; end
	local useItemTooltips = frame.args.tooltips and (#frame.args.tooltips > 0) or false
	local experimental = frame.args.experimental and (#frame.args.experimental > 0) or false
	local experimentalCondition = experimental and ' and (experimental=True)' or ' and (experimental is null or experimental=False)'

	local recipes = cargo.query("crafting_recipes", fields, {
		where = '(product="' .. itemName .. '" or product2="' .. itemName .. '" or product3="' .. itemName .. '" or product4="' .. itemName .. '")',
		orderBy = 'isBuilding ASC, mainRecipe DESC, alternateRecipe ASC, product ASC'
	});

	if #recipes > 0 then
		loadBuildingsPower()
		return generateTable(recipes, useItemTooltips)
	else
		return "'''" .. itemName .. "''' ne peut être fabriqué."
	end
end

function p.generateCraftingRecipesForItems(frame)
	local itemNames = {};
	for _, itemName in tableTools.sparseIpairs(frame.args) do
		itemName = mw.text.trim(itemName);
		if (#itemName > 0) then
			itemNames[#itemNames + 1] = itemName;
		end
	end

	return p._generateCraftingRecipesForItems(
		itemNames,
		{
			useItemTooltips = frame.args.tooltips and (#frame.args.tooltips > 0) or false,
			experimental = frame.args.experimental and (#frame.args.experimental > 0) or false,
		}
	);
end

function p._generateCraftingRecipesForItems(itemNames, options)
	checkType('generateCraftingRecipesForItems', 1, itemNames, 'table');
	checkType('generateCraftingRecipesForItems', 2, options, 'table', true);
	if (#itemNames == 0) then
		return tostring(mw.html.create("strong"):addClass("error"):wikitext("No items specified"):allDone());
	end

	options = options or {};
	local experimentalCondition = options.experimental and ' and (experimental=True)' or ' and (experimental is null or experimental=False)';
	local where;
	for _, itemName in ipairs(itemNames) do
		if (where) then
			where = where .. " or ";
		end
		where = (where or "") .. '(product="' .. itemName .. '" or product2="' .. itemName .. '" or product3="' .. itemName .. '" or product4="' .. itemName .. '")';
	end
	where = "(" .. where .. ")";

	local recipes = cargo.query("crafting_recipes", fields, {
		where = where,
		orderBy = 'isBuilding ASC, mainRecipe DESC, alternateRecipe ASC, product ASC',
	});

	if #recipes > 0 then
		loadBuildingsPower();
		return generateTable(recipes, options.useItemTooltips or false);
	else
		return "Aucune recette trouvée";
	end
end

function p.generateCraftingUsage(frame)
	local itemName = frame.args.item or frame.args[1]
	local useItemTooltips = frame.args.tooltips and (#frame.args.tooltips > 0) or false
	local experimental = frame.args.experimental and (#frame.args.experimental > 0) or false
	local unreleased = frame.args.unreleased and (#frame.args.unreleased > 0) or false
    local buildingRecipes = frame.args.buildings
    
	local experimentalCondition = experimental and ' and (experimental=True)' or ' and (experimental is null or experimental=False)'
	local unreleasedCondition = unreleased and '' or ' and (unreleased is null or unreleased=False)'
	local buildingCondition = ''
	if buildingRecipes == '0' then
    	buildingCondition = ' and (craftedIn<>"Outil de construction")'
	elseif buildingRecipes == '1' then
    	buildingCondition = ' and (craftedIn="Outil de construction")'
	end
	

	local recipes = cargo.query("crafting_recipes", fields, {
		where = 'product IS NOT NULL and _pageName NOT LIKE "%/%" and (ingredient1="' .. itemName .. '" or ingredient2="' .. itemName .. '" or ingredient3="' .. itemName .. '" or ingredient4="' .. itemName .. '" or ingredient5="' .. itemName .. '" or ingredient6="' .. itemName .. '" or ingredient7="' .. itemName .. '" or ingredient8="' .. itemName .. '" or ingredient9="' .. itemName .. '" or ingredient10="' .. itemName .. '")' .. unreleasedCondition .. buildingCondition,
		limit = argsUtil.parseNumber(frame.args.limit),
		orderBy = 'isBuilding ASC, alternateRecipe ASC, product ASC'
	});

	if #recipes > 0 then
		loadBuildingsPower()
		return generateTable(recipes, useItemTooltips)
	else
		return "'''" .. itemName .. "''' n'est pas utilisé pour fabriquer quoi que ce soit."
	end
end

function p.generateCraftingUsageForItems(frame)
	local itemNames = {};
	for _, itemName in tableTools.sparseIpairs(frame.args) do
		itemName = mw.text.trim(itemName);
		if (#itemName > 0) then
			itemNames[#itemNames + 1] = itemName;
		end
	end

	return p._generateCraftingUsageForItems(
		itemNames,
		{
			useItemTooltips = frame.args.tooltips and (#frame.args.tooltips > 0) or false,
			experimental = frame.args.experimental and (#frame.args.experimental > 0) or false,
			unreleased = frame.args.unreleased and (#frame.args.unreleased > 0) or false,
			limit = argsUtil.parseNumber(frame.args.limit),
		}
	);
end

function p._generateCraftingUsageForItems(itemNames, options)
	checkType('generateCraftingUsageForItems', 1, itemNames, 'table');
	checkType('generateCraftingUsageForItems', 2, options, 'table', true);
	if (#itemNames == 0) then
		return tostring(mw.html.create("strong"):addClass("error"):wikitext("No items specified"):allDone());
	end

	options = options or {};
	local experimentalCondition = options.experimental and ' and (experimental=True)' or ' and (experimental is null or experimental=False)';
	local unreleasedCondition = options.unreleased and '' or ' and (unreleased is null or unreleased=False)'

	local where;
	for _, itemName in ipairs(itemNames) do
		if (where) then
			where = where .. " or ";
		end
		where = (where or "")
			.. '(ingredient1="' .. itemName .. '" or ingredient2="' .. itemName
			.. '" or ingredient3="' .. itemName .. '" or ingredient4="' .. itemName
			.. '" or ingredient5="' .. itemName .. '" or ingredient6="' .. itemName
			.. '" or ingredient7="' .. itemName .. '" or ingredient8="' .. itemName
			.. '" or ingredient9="' .. itemName .. '" or ingredient10="' .. itemName
			.. '")';
	end
	where = 'product IS NOT NULL and _pageName NOT LIKE "%/%" and (' .. where .. ")" .. unreleasedCondition;

	local recipes = cargo.query("crafting_recipes", fields, {
		where = where,
		limit = options.limit,
		orderBy = 'isBuilding ASC, alternateRecipe ASC, product ASC',
	});

	if #recipes > 0 then
		loadBuildingsPower();
		return generateTable(recipes, options.useItemTooltips or false);
	else
		return "Aucune recette trouvée";
	end
end

return p
Advertisement