Modul:RenderRecipesTable

Aus Satisfactory Wiki
Zur Navigation springen Zur Suche springen

Die Dokumentation für dieses Modul kann unter Modul:RenderRecipesTable/Doku erstellt werden

require("Module:DocsUtils")

local p = {}

-- hover tooltips
loc.tooltips = {
    ingredientsPerCycle = "How much of this resource is consumed every crafting cycle",
    ingredientsPerMinute = "How much of this resource has to be supplied to the machine every minute for 100% efficiency",
    productsPerCycle = "How much of this resource is produced every crafting cycle",
    productsPerMinute = "How much of this resource has to be withdrawn from the machine every minute for 100% efficiency",
    minMaxPower = "The power consumption of this machine when producing this recipe increases throughout the crafting cycle",
}

-- table column headers
loc.columns = {
    recipe = "Recipe",
    ingredients = "Ingredients",
	duration = "Duration",
    producedIn = "Produced in",
    products = "Products",
    unlockedBy = "Unlocked by"
}

-- errors shown when no recipes are found in place of the recipe table
-- make sure to respect spaces
loc.errors = {
    listAnd = "and",
    listOr = "or",
    noRecipes = "No recipes found.",
    producing = " cannot be crafted.",
    using = "No recipe uses ",
    producedIn = "No recipes can be produced in ",
}

-- page links, may be wiki links
loc.links = {
    inCraftBench = "[[Craft Bench]]",
    inWorkshop = "[[Equipment Workshop]]",
    inBuildGun = "[[Build Gun]]",
    inCustomizer = "[[Customizer]]",
    alternate = "[[Hard Drive|Alternate]]",
    asByproduct = " As byproduct",
}

-- manual crafting hammer
loc.manualCrafting = "File:Manual crafting.png"

-- TEMP - Remove recipes using Customizer materials
-- - recipes - array of recipes
local function filterCustomizerRecipes(recipes)
    local filteredRecipes = {}
    for _,recipe in pairs(recipes) do
    	-- ily2, regex
        if not (recipe.inCustomizer or string.match(recipe.className, [=[^Recipe_Wall]=]) or string.match(recipe.className, [=[^Recipe_WallSet]=]) or string.match(recipe.className, [=[^Recipe_SteelWall]=]) or string.match(recipe.className, [=[^Recipe_Foundation_[^%s_]+_]=]) or string.match(recipe.className, [=[^Recipe_Ramp]=]) or string.match(recipe.className, [=[^Recipe_InvertedRamp]=]) or string.match(recipe.className, [=[^Recipe_Roof]=]) or string.match(recipe.className, [=[^Recipe_Pillar]=]) or string.match(recipe.className, [=[QuarterPipe]=]) or string.match(recipe.className, [=[Asphalt]=])) then
            table.insert(filteredRecipes, recipe)
        end
    end
    return filteredRecipes
end

-- Sort by inBuildGun descending, alternate descending, name ascending, experimental descending
-- - a - recipe
-- - b - recipe
local function recipeComparator(a, b)
    if a.inBuildGun ~= b.inBuildGun then
        return not a.inBuildGun and b.inBuildGun
    elseif a.alternate ~= b.alternate then
        return not a.alternate and b.alternate
    elseif a.name ~= b.name then
        return a.name < b.name
    else
        return not a.experimental and b.experimental
    end
end

-- Populates a table cell with a recipe's ingredients or products.
-- - recipe - table
-- - mode - string, either "ingredients" or "products"
local function renderItemsCell(recipe, mode)
    local html = [=[<div class="recipe-items" data-items-count="]=] .. #recipe[mode] .. [=[">]=]
    local showPerMinValues = #recipe.producedIn > 0
    for _,item in pairs(recipe[mode]) do
        local itemLink, itemName  = getLinkAndName(item.item, recipe.stable, recipe.experimental)
        html = html .. [=[<div class="recipe-item"><span class="item-amount" title="]=] .. loc.tooltips[mode .. "PerCycle"] .. [=[">]=] .. formatNumber(item.amount) .. "&nbsp;&times; </span>[[" .. loc.filePage .. itemLink .. ".png|40px|link=" .. itemLink .. [=[]]<span class="item-name">]=] .. itemName .. [=[</span>]=] .. (showPerMinValues and ([=[<span class="item-minute" title="]=] .. loc.tooltips[mode .. "PerMinute"] .. [=[">]=] .. formatNumber((60.0/recipe.duration)*item.amount) ..  loc.units.pmin .. "</span>") or "") .. "</div>"
    end
    html = html .. "</div>"
    return html
end

-- Populates a table cell with a recipe's produced in buildings, plus the cycle duration or craft steps.
-- - recipe - table
-- - singleBuilding - boolean -  if the recipe is produced in a single building, do not render the building name
local function renderProducedInCell(recipe, singleBuilding)
    local html = ""
    if singleBuilding then
        html = recipe.duration .. loc.units.sec
        if recipe.minPower and recipe.maxPower then
            html = html .. [=[<br><span title="]=] .. loc.tooltips.minMaxPower .. [=[">]=] .. formatNumber(recipe.minPower) .. " - " .. formatNumber(recipe.maxPower) .. loc.units.mw .. "</span>"
        end
    else
        for _,building in pairs(recipe.producedIn) do
            local buildingLink, buildingName = getLinkAndName(building, recipe.stable, recipe.experimental)
            html = html .. [=[<div class="recipe-building">[[]=] .. buildingLink .. "|" .. buildingName .. [=[]]<br>]=] .. recipe.duration .. loc.units.sec
            if building == "Desc_HadronCollider_C" and recipe.minPower and recipe.maxPower then
                html = html .. [=[<br><span class="recipe-energy" title="]=] .. loc.tooltips.minMaxPower .. [=[">]=] .. formatNumber(recipe.minPower) .. " - " .. formatNumber(recipe.maxPower) .. loc.units.mw .. "</span>"
            end
            html = html .. "</div>"
        end
        if recipe.inCraftBench then
            html = html .. [=[<div class="recipe-building">]=] .. loc.links.inCraftBench .. [=[<br>[[]=] .. loc.manualCrafting .. [=[|16px|link=|class=invert-on-dark]]&nbsp;&times; ]=] .. formatNumber(math.ceil((recipe.manualCraftingMultiplier*recipe.duration)/2))  .. "</div>"
        end
        if recipe.inWorkshop then
            html = html .. [=[<div class="recipe-building">]=] .. loc.links.inWorkshop .. [=[<br>[[]=] .. loc.manualCrafting .. [=[|16px|link=|class=invert-on-dark]]&nbsp;&times; ]=] .. formatNumber(math.ceil((recipe.manualCraftingMultiplier*recipe.duration)/2))  .. "</div>"
        end
        if recipe.inBuildGun then
            html = html .. [=[<div class="recipe-building">]=] .. loc.links.inBuildGun .. [=[</div>]=]
        end
        if recipe.inCustomizer then
            html = html .. [=[<div class="recipe-building">]=] .. loc.links.inCustomizer .. [=[</div>]=]
        end
    end
    return html
end

-- Renders a recipe table from supplied recipes.
-- - recipes - array of recipes
-- - singleBuilding - boolean, building mode, changes the "Produced in" column
-- - targetProducts - array of product classNames, used for "recipes producing items", used for marking byproducts
-- - noRecipeMessage - string to return in place of the table if the recipes array is empty
local function renderRecipeTable(recipes, singleBuilding, targetProducts, noRecipeMessage)
    if #recipes == 0 then
        return noRecipeMessage or loc.errors.noRecipes
    end
    table.sort(recipes, recipeComparator)
    recipes = filterCustomizerRecipes(recipes)
    -- start table
    local html = [=[{| class="wikitable sortable recipetable"]=] .. "\n|-\n" .. [=[!]=] .. loc.columns.recipe .. [=[!!class="unsortable"|]=] .. loc.columns.ingredients .. [=[!!]=] .. (singleBuilding and loc.columns.duration or loc.columns.producedIn) .. [=[!!class="unsortable"|]=] .. loc.columns.products .. [=[!!class="unsortable"|]=] .. loc.columns.unlockedBy
    for _,recipe in pairs(recipes) do
        -- season check
        local season = ""
        for _,recipeSeason in ipairs(recipe.seasons) do
        	if recipeSeason == "ficsmas" then -- more seasons to be added later
        		season = season .. [=[<br><span class="recipe-badge recipe-season-]=] .. string.lower(recipeSeason) .. [=[">]=] .. string.upper(recipeSeason) .. [=[</span>]=]
        	end
    	end
        -- byproduct check
        local asByproduct = ""
        if targetProducts ~= nil then
            for i=2, #recipe.products do
                for _,targetProduct in pairs(targetProducts) do
                    if targetProduct == recipe.products[i].item then
                        asByproduct = asByproduct .. "[[" .. loc.filePage .. getLinkAndName(targetProduct, recipe.stable, recipe.experimental) .. ".png|16px|link=]]"
                    end
                end
            end
        end
        -- add table row
        html = html .. "\n|-\n|" .. recipe.name .. ((recipe.stable and not recipe.experimental) and [=[<br><span class="recipe-badge recipe-stable">]=] .. loc.branches.stable ..[=[</span>]=] or ((recipe.experimental and not recipe.stable) and [=[<br><span class="recipe-badge recipe-experimental">]=] .. loc.branches.experimental ..[=[</span>]=]) or "") .. season .. (recipe.alternate and ([=[<br><span class="recipe-badge recipe-alternate">]=] .. loc.links.alternate .. [=[</span>]=]) or "") .. ((asByproduct ~= "") and ([=[<br><span class="recipe-badge recipe-byproduct">]=] .. asByproduct .. loc.links.asByproduct .. [=[</span>]=]) or "") .. "||" .. renderItemsCell(recipe, "ingredients") .. "||" .. renderProducedInCell(recipe, singleBuilding) .. "||" .. renderItemsCell(recipe, "products") .. "||" .. recipe.unlockedBy
    end
    html = html .. "\n|-\n|}"
    return html
end

-- Render a table with recipes that yield specified products.
function p.renderRecipesProducing(frame)
    local recipes = {}
    local items = {}
    local seasonsEnabled = false
    
    for name,item in pairs(frame:getParent().args) do
    	if name == "seasons" and item ~= "" and item ~= "0" then
    		seasonsEnabled = true
		else
        	table.insert(items, item)
        end
    end
    
    -- go through all recipes, branch entries, products, check if at least one is in the items array
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
        	if seasonsEnabled or #recipe.seasons == 0 then
	            for _,product in pairs(recipe.products) do
	                for _,item in pairs(items) do
	                    if item == product.item then
	                        table.insert(recipes, recipe)
	                        break
	                    end
	                end
	            end
            end
        end
    end
    return renderRecipeTable(recipes, false, items, "'''" .. table.concat(items, "'''" .. loc.errors.listAnd .. "'''") .. "'''" .. loc.errors.producing)
end

-- Render a table with recipes that use specified ingredients.
function p.renderRecipesUsing(frame)
    local recipes = {}
    local items = {}
    local seasonsEnabled = false
    
    for name,item in pairs(frame:getParent().args) do
    	if name == "seasons" and item ~= "" and item ~= "0" then
    		seasonsEnabled = true
		else
        	table.insert(items, item)
        end
    end
    
    -- go through all recipes, branch entries, ingredients, check if at least one is in the items array
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
        	if seasonsEnabled or #recipe.seasons == 0 then
	            for _,ingredient in pairs(recipe.ingredients) do
	                for _,item in pairs(items) do
	                    if item == ingredient.item then
	                        table.insert(recipes, recipe)
	                        break
	                    end
	                end
	            end
            end
        end
    end
    return renderRecipeTable(recipes, false, nil, loc.errors.using .. "'''" .. table.concat(items, "'''" .. loc.errors.listOr .. "'''") .. "'''")
end

-- Render a table with recipes produced in specified buildings.
function p.renderRecipesProducedIn(frame)
    local recipes = {}
    local buildings = {}
    local seasonsEnabled = false
    
    for name,building in pairs(frame:getParent().args) do
    	if name == "seasons" and building ~= "" and building ~= "0" then
    		seasonsEnabled = true
		else
        	table.insert(buildings, building)
        end
    end
    
    -- go through all recipes, branch entries, producedIn buildings, check if at least one is in the buildings array
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
        	if seasonsEnabled or #recipe.seasons == 0 then
	            for _,producedInBuilding in pairs(recipe.producedIn) do
	                for _,building in pairs(buildings) do
	                    if producedInBuilding == building then
	                        table.insert(recipes, recipe)
	                        break
	                    end
	                end
	            end
            end
        end
    end
    return renderRecipeTable(recipes, #buildings == 1, nil, loc.errors.producedIn .. "'''" .. table.concat(buildings, "'''" .. loc.errors.listOr .. "'''") .. "'''")
end

-- Render alternate recipes
function p.renderAlternateRecipes(frame)
    local recipes = {}
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
            if recipe.alternate then
                table.insert(recipes, recipe)
            end
        end
    end
    table.sort(recipes, function(a, b) return a.name < b.name end)
    return renderRecipeTable(recipes, nil, nil)
end

function p.renderRecipesProducedInSpecial(frame)
    local recipes = {}
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
    		local target = frame.args[1]
            if (target == "Build Gun" and recipe.inBuildGun) or (target == "Customizer" and recipe.inCustomizer) or (target == "Craft Bench" and recipe.inCraftBench) or (target == "Equipment Workshop" and recipe.inWorkshop) then
                -- empty the building list
                recipe.producedIn = {}
                table.insert(recipes, recipe)
            end
        end
    end
    table.sort(recipes, function(a, b) return a.name < b.name end)
    return renderRecipeTable(recipes, false)
end

return p