Modul:RenderCraftingRecipesTable
Zur Navigation springen
Zur Suche springen
Die Dokumentation für dieses Modul kann unter Modul:RenderCraftingRecipesTable/Doku erstellt werden
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
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 = 'Equipment Workshop'
elseif recipe.inCraftBench then
recipe.craftedIn = 'Craft Bench'
else
recipe.craftedIn = 'Build Gun'
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"> </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 .. '"> </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">[[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;"> </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 = '[[File:Manual crafting.png|16px|link=|class=fd_invert]] × ' .. (recipe.craftingClicks or '')
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 or '') .. '</span></td>'
end
html = html .. '</tr>'
end
return html
end
local fields = 'recipeName, alternateRecipe, mainRecipe, craftedIn, avgPower, 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>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 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 .. "''' 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 = 'isBuilding ASC, mainRecipe DESC, alternateRecipe ASC, product 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 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<>"Build Gun")'
elseif buildingRecipes == '1' then
buildingCondition = ' and (craftedIn="Build Gun")'
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 .. "''' 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,
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 "No recipes found";
end
end
return p