Satisfactory Wiki
Advertisement
Template-noinfo Documentation
This template has no documentation. If you know how to use this template, please add some.

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

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

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
    buildings[building.name] = tonumber(building.powerUsage)
  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 = 'Ausrüstungswerkstatt'
    elseif recipe.inCraftBench then
      recipe.craftedIn = 'Werkzeugbank'
    else
      recipe.craftedIn = 'Konstruktionswerkzeug'
    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]='The amount of this resource per crafting cycle, from which the per minute value is calculated', [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]='The amount of this resource per crafting cycle, from which the per minute value is calculated', [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]='How much of this resource has to be supplied to/withdrawn from the machine for maximum efficiency', [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) .. ' [[energy|MJ]]/item'
            local tooltipText = 'The energy consumed by a ' .. recipe.craftedIn .. ' to craft 1 item at 100% clock speed in this crafting cycle'
            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 == Null or recipeName == '') then recipeName = recipe.product1 end
      if (recipeName == Null 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">[[Hard Drive|Alternate]]</span>'
      end
      local stripe = ''
      if recipe.experimental == '1' then
      	stripe = '<div style="' .. experimental_style .. '">' 
      			 .. mw.getCurrentFrame():expandTemplate{title='Tooltip', args={[1]='Exp', [2]='This recipe is only available in the Experimental branch', [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]='This recipe is not available, it was either datamined or removed', [3]='no underline'}}
      			 .. '</div>'
      			 .. '<div style="' .. unreleased_style .. ' bottom:0;">&nbsp;</div>'
  	  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 = '[[File:Manual crafting.png|16px|link=|class=fd_invert]] × ' .. recipe.craftingClicks
        local tooltipText = 'Manual crafting rate: ' .. 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 .. '</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'

local function generateTable(recipes, useItemTooltips)  
  local html = '<table class="wikitable" style="text-align:center"><tr><th>Recipe</th><th colspan="12">Ingredients</th><th>Building</th><th colspan="2">Products</th><th>Prerequisites</th></tr>'
  
  for i=1, #recipes, 1 do
    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]
  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 tables = 'crafting_recipes'
  local args = { where = '(product="' .. itemName .. '" or product2="' .. itemName .. '" or product3="' .. itemName .. '" or product4="' .. itemName .. '")', 
                 orderBy = 'mainRecipe DESC, alternateRecipe ASC' }
  local recipes = cargo.query(tables, fields, args)
  
  if #recipes > 0 then
    loadBuildingsPower()
    return generateTable(recipes, useItemTooltips)
  else
    return "'''" .. itemName .. "''' can't be crafted."
  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 = "mainRecipe DESC, alternateRecipe ASC",
	});

	if #recipes > 0 then
    	loadBuildingsPower();
    	return generateTable(recipes, options.useItemTooltips or false);
	else
    	return "No recipes found";
	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 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 tables = 'crafting_recipes'
  local args = { 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, 
                 orderBy = '(craftedIn IS NOT NULL or inWorkShop or inCraftBench) DESC, product ASC, alternateRecipe ASC' }
  local recipes = cargo.query(tables, fields, args)
  
  if #recipes > 0 then
    loadBuildingsPower()
    return generateTable(recipes, useItemTooltips)
  else
    return "'''" .. itemName .. "''' isn't used to craft anything."
  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,
		}
	);
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,
		orderBy = "mainRecipe DESC, alternateRecipe ASC",
	});

	if #recipes > 0 then
		loadBuildingsPower();
		return generateTable(recipes, options.useItemTooltips or false);
	else
		return "No recipes found";
	end
end

return p
Advertisement