<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.funkystation.org/index.php?action=history&amp;feed=atom&amp;title=Module%3AItem_recipe</id>
	<title>Module:Item recipe - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.funkystation.org/index.php?action=history&amp;feed=atom&amp;title=Module%3AItem_recipe"/>
	<link rel="alternate" type="text/html" href="https://wiki.funkystation.org/index.php?title=Module:Item_recipe&amp;action=history"/>
	<updated>2026-06-05T02:22:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.42.3</generator>
	<entry>
		<id>https://wiki.funkystation.org/index.php?title=Module:Item_recipe&amp;diff=275&amp;oldid=prev</id>
		<title>imported&gt;Aliser: removed custom reagents handlers in favor of default logic; fixed a bug where lookup_recipe_ids_by_product_id() wasn&#039;t always returning an array; itty-bitty refactor</title>
		<link rel="alternate" type="text/html" href="https://wiki.funkystation.org/index.php?title=Module:Item_recipe&amp;diff=275&amp;oldid=prev"/>
		<updated>2024-09-16T18:51:42Z</updated>

		<summary type="html">&lt;p&gt;removed custom reagents handlers in favor of default logic; fixed a bug where lookup_recipe_ids_by_product_id() wasn&amp;#039;t always returning an array; itty-bitty refactor&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Contains utilities for working with in-game item recipes.&lt;br /&gt;
&lt;br /&gt;
local p = {} --p stands for package&lt;br /&gt;
local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
local itemModule = require(&amp;#039;Module:Item&amp;#039;)&lt;br /&gt;
local yesNo = require(&amp;#039;Module:Yesno&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
-- A table mapping recipe IDs to recipes.&lt;br /&gt;
local recipes_by_recipe_ids = mw.loadJsonData(&amp;quot;Module:Item recipe/recipes by recipe IDs.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- A table mapping product IDs to recipe IDs.&lt;br /&gt;
-- A product ID can have either a single recipe ID mapped to it, or multiple if there&amp;#039;s are multiple recipes.&lt;br /&gt;
local recipe_ids_by_product_ids = mw.loadJsonData(&amp;quot;Module:Item recipe/recipe IDs by product IDs.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- A table mapping production methods to recipe IDs, with a intermediate mapping by availability.&lt;br /&gt;
local recipe_ids_by_method_and_availability = mw.loadJsonData(&lt;br /&gt;
    &amp;quot;Module:Item recipe/recipe IDs by method and availability.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- A table mapping material IDs (item IDs) to their display order.&lt;br /&gt;
-- Order is just a number. Materials with lesser order number will appear first.&lt;br /&gt;
local materials_order_by_material_ids = mw.loadJsonData(&amp;quot;Module:Item recipe/order of materials.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- A table remapping product IDs.&lt;br /&gt;
--&lt;br /&gt;
-- Not all recipes produce products that you might think they do -&lt;br /&gt;
-- some produce their own &amp;quot;printed&amp;quot; or &amp;quot;empty&amp;quot; or other variants.&lt;br /&gt;
--&lt;br /&gt;
-- For instance, a recipe for the small power cell produces `PowerCellSmallPrinted` item,&lt;br /&gt;
-- whereas the actual power cell item has ID `PowerCellSmall`.&lt;br /&gt;
--&lt;br /&gt;
-- So, for the module functions to be able to find the recipe for the small power cell,&lt;br /&gt;
-- we first would need to define a mapping from the recipe product `PowerCellSmallPrinted`&lt;br /&gt;
-- to the actual item `PowerCellSmall`.&lt;br /&gt;
--&lt;br /&gt;
-- After that is done, a lookup for small power cell (or its id) will return the corresponding recipe.&lt;br /&gt;
local product_overrides = mw.loadJsonData(&amp;quot;Module:Item recipe/product overrides.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local current_frame = mw:getCurrentFrame()&lt;br /&gt;
&lt;br /&gt;
local methods_items_els_cache = {}&lt;br /&gt;
&lt;br /&gt;
local was_template_styles_tag_el_added = false&lt;br /&gt;
&lt;br /&gt;
-- ====================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function numeric_table_length(t)&lt;br /&gt;
    local count = 0&lt;br /&gt;
    for _ in ipairs(t) do count = count + 1 end&lt;br /&gt;
    return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function table_length(t)&lt;br /&gt;
    local count = 0&lt;br /&gt;
    for _ in pairs(t) do count = count + 1 end&lt;br /&gt;
    return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function numeric_table_has_value(tab, val)&lt;br /&gt;
    for _, value in ipairs(tab) do&lt;br /&gt;
        if value == val then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function filter_table(tab, predicate)&lt;br /&gt;
    local tab_filtered = {}&lt;br /&gt;
    for key, value in pairs(tab) do&lt;br /&gt;
        if predicate(key, value) then&lt;br /&gt;
            tab_filtered[key] = value&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return tab_filtered&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function assert_value_not_nil(value, error_message)&lt;br /&gt;
    if value == nil then&lt;br /&gt;
        if error_message == nil then&lt;br /&gt;
            error(&amp;quot;value is nil&amp;quot;)&lt;br /&gt;
        else&lt;br /&gt;
            error(error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Given a value, checks if it&amp;#039;s &amp;quot;nil&amp;quot;.&lt;br /&gt;
-- * If it&amp;#039;s not - returns the `value`.&lt;br /&gt;
-- * IF it is - returns the `value_if_nil`.&lt;br /&gt;
local function nil_or(value, value_if_nil)&lt;br /&gt;
    if value == nil then&lt;br /&gt;
        return value_if_nil&lt;br /&gt;
    else&lt;br /&gt;
        return value&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function find_first_numeric_table_item_matching_condition(table, condition)&lt;br /&gt;
    for i, item in ipairs(table) do&lt;br /&gt;
        if condition(item, i, table) then&lt;br /&gt;
            return item&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function find_first_table_item_matching_condition(table, condition)&lt;br /&gt;
    for key, value in pairs(table) do&lt;br /&gt;
        if condition(key, value, table) then&lt;br /&gt;
            return value&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function find_first_table_item_key_matching_condition(table, condition)&lt;br /&gt;
    for key, value in pairs(table) do&lt;br /&gt;
        if condition(key, value, table) then&lt;br /&gt;
            return key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function map_numeric_table(tbl, f)&lt;br /&gt;
    local t = {}&lt;br /&gt;
    for i, v in ipairs(tbl) do&lt;br /&gt;
        t[i] = f(v, i)&lt;br /&gt;
    end&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
local function map_table(tbl, f)&lt;br /&gt;
    local t = {}&lt;br /&gt;
    for k, v in pairs(tbl) do&lt;br /&gt;
        t[k] = f(k, v)&lt;br /&gt;
    end&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function passthrough_assert_true(value, valueToReturnIfTrue, errorMessageOnFalse)&lt;br /&gt;
    if value then&lt;br /&gt;
        return valueToReturnIfTrue&lt;br /&gt;
    else&lt;br /&gt;
        error(errorMessageOnFalse)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ternary_strict(valueToCheck, valueIfTrue, valueIfFalse)&lt;br /&gt;
    if valueToCheck == true then&lt;br /&gt;
        return valueIfTrue&lt;br /&gt;
    else&lt;br /&gt;
        return valueIfFalse&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- formats seconds to a string `X min. Y sec.`.&lt;br /&gt;
--&lt;br /&gt;
-- - `X min.` part is omitted if there&amp;#039;s less than a minute.&lt;br /&gt;
-- - `Y sec.` part is omitter if there&amp;#039;s no seconds left.&lt;br /&gt;
local function format_seconds_to_short_string(input_seconds)&lt;br /&gt;
    local minutes = math.floor(input_seconds / 60)&lt;br /&gt;
    local seconds = input_seconds - minutes * 60&lt;br /&gt;
&lt;br /&gt;
    local minutes_part = ternary_strict(minutes &amp;gt; 0, minutes .. &amp;quot; min.&amp;quot;, nil)&lt;br /&gt;
    local seconds_part = ternary_strict(seconds &amp;gt; 0, seconds .. &amp;quot; sec.&amp;quot;, nil)&lt;br /&gt;
&lt;br /&gt;
    if minutes_part ~= nil and seconds_part ~= nil then&lt;br /&gt;
        return minutes_part .. &amp;quot; &amp;quot; .. seconds_part&lt;br /&gt;
    elseif seconds_part ~= nil then&lt;br /&gt;
        return seconds_part&lt;br /&gt;
    elseif minutes_part ~= nil then&lt;br /&gt;
        return minutes_part&lt;br /&gt;
    else&lt;br /&gt;
        return &amp;#039;&amp;#039;&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ====================&lt;br /&gt;
&lt;br /&gt;
-- ######################&lt;br /&gt;
-- ### METHOD LOOKUPS ###&lt;br /&gt;
-- ######################&lt;br /&gt;
&lt;br /&gt;
-- Lookups recipe IDs by production method `production_method`.&lt;br /&gt;
--&lt;br /&gt;
-- Returns recipe IDs grouped by availability.&lt;br /&gt;
--&lt;br /&gt;
-- Raises an error if no recipe IDs were found by the production method.&lt;br /&gt;
-- Set `no_error` to `true` to return `nil` instead.&lt;br /&gt;
function p.lookup_recipe_ids_and_availability_by_method(production_method, no_error)&lt;br /&gt;
    assert_value_not_nil(production_method)&lt;br /&gt;
&lt;br /&gt;
    local method_item_id = p.lookup_method_item_id(production_method)&lt;br /&gt;
&lt;br /&gt;
    return recipe_ids_by_method_and_availability[method_item_id]&lt;br /&gt;
        or passthrough_assert_true(&lt;br /&gt;
            no_error,&lt;br /&gt;
            nil,&lt;br /&gt;
            &amp;quot;recipe IDs by production method lookup failed: no recipes by production method &amp;#039;&amp;quot; ..&lt;br /&gt;
            method_item_id .. &amp;quot;&amp;#039; (input method: &amp;#039;&amp;quot; .. production_method .. &amp;quot;&amp;#039;)&amp;quot;&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Lookups production methods by recipe ID `recipe_id`.&lt;br /&gt;
--&lt;br /&gt;
-- Returns an array of tables, each containg:&lt;br /&gt;
-- - `method` - production method.&lt;br /&gt;
-- - `availability` - availability for the recipe for this production method.&lt;br /&gt;
--&lt;br /&gt;
-- **NOTE:** This is an expensive function.&lt;br /&gt;
function p.lookup_methods_with_availability_by_recipe_id(inpit_recipe_id)&lt;br /&gt;
    local result = {}&lt;br /&gt;
&lt;br /&gt;
    for method, recipe_ids_grouped in pairs(recipe_ids_by_method_and_availability) do&lt;br /&gt;
        for availability, recipe_ids in pairs(recipe_ids_grouped) do&lt;br /&gt;
            if numeric_table_has_value(recipe_ids, inpit_recipe_id) then&lt;br /&gt;
                table.insert(result, {&lt;br /&gt;
                    method = method,&lt;br /&gt;
                    availability = availability&lt;br /&gt;
                })&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Checks whether a production method `production_method` exists.&lt;br /&gt;
-- Any casing is allowed.&lt;br /&gt;
function p.method_exists(production_method)&lt;br /&gt;
    assert_value_not_nil(production_method)&lt;br /&gt;
&lt;br /&gt;
    return p.lookup_recipe_ids_and_availability_by_method(production_method, true) ~= nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Asserts that a production method `production_method` exists.&lt;br /&gt;
-- Any casing is allowed.&lt;br /&gt;
function p.assert_method_exists(production_method, custom_message)&lt;br /&gt;
    assert_value_not_nil(production_method)&lt;br /&gt;
&lt;br /&gt;
    if not p.method_exists(production_method) then&lt;br /&gt;
        if custom_message then&lt;br /&gt;
            error(&amp;quot;production method exists assertion failed for method &amp;#039;&amp;quot; ..&lt;br /&gt;
                production_method .. &amp;quot;&amp;#039;: &amp;quot; .. custom_message)&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;production method exists assertion failed for method &amp;#039;&amp;quot; ..&lt;br /&gt;
                production_method .. &amp;quot;&amp;#039;: production method is not defined&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Lookups method item ID.&lt;br /&gt;
--&lt;br /&gt;
-- Raises an error if no item ID was found.&lt;br /&gt;
-- Set `no_error` to `true` to return `nil` instead.&lt;br /&gt;
--&lt;br /&gt;
-- todo this will break in the future&lt;br /&gt;
function p.lookup_method_item_id(method, no_error)&lt;br /&gt;
    assert_value_not_nil(method)&lt;br /&gt;
&lt;br /&gt;
    return itemModule.lookup_item_id(method, true)&lt;br /&gt;
        or passthrough_assert_true(&lt;br /&gt;
            no_error,&lt;br /&gt;
            nil,&lt;br /&gt;
            &amp;quot;production method item ID lookup failed: no item ID was found for method &amp;#039;&amp;quot; ..&lt;br /&gt;
            method .. &amp;quot;&amp;#039;&amp;quot;&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ################################&lt;br /&gt;
-- ### PRODUCT OVERRIDE LOOKUPS ###&lt;br /&gt;
-- ################################&lt;br /&gt;
&lt;br /&gt;
-- Lookups an override for product ID `product_id`.&lt;br /&gt;
--&lt;br /&gt;
-- For instance, if there was an override `PowerCellSmallPrinted` that maps to to item ID `PowerCellSmall`,&lt;br /&gt;
-- this functions would return `PowerCellSmall` if `product_id` was `PowerCellSmallPrinted`.&lt;br /&gt;
--&lt;br /&gt;
-- Raises an error if no match was found. Set `no_error` to `true` to return `nil` instead.&lt;br /&gt;
function p.lookup_product_id_override(product_id, no_error)&lt;br /&gt;
    assert_value_not_nil(product_id)&lt;br /&gt;
&lt;br /&gt;
    return product_overrides[product_id]&lt;br /&gt;
        or passthrough_assert_true(&lt;br /&gt;
            no_error,&lt;br /&gt;
            nil,&lt;br /&gt;
            &amp;quot;product override lookup failed: no override was found with product ID &amp;#039;&amp;quot; ..&lt;br /&gt;
            product_id .. &amp;quot;&amp;#039;&amp;quot;&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Lookups the prodct ID that was overriden with item ID `item_id`.&lt;br /&gt;
--&lt;br /&gt;
-- For instance, if there was an override `PowerCellSmallPrinted` that maps to to item ID `PowerCellSmall`,&lt;br /&gt;
-- this functions would return `PowerCellSmallPrinted` if `item_id` was `PowerCellSmall`.&lt;br /&gt;
--&lt;br /&gt;
-- **NOTE:** This is an expensive function.&lt;br /&gt;
--&lt;br /&gt;
-- Raises an error if no match was found. Set `no_error` to `true` to return `nil` instead.&lt;br /&gt;
function p.reverse_lookup_product_id_override(item_id, no_error)&lt;br /&gt;
    assert_value_not_nil(item_id)&lt;br /&gt;
&lt;br /&gt;
    return find_first_table_item_key_matching_condition(&lt;br /&gt;
            product_overrides,&lt;br /&gt;
            function(_, value) return value == item_id end&lt;br /&gt;
        )&lt;br /&gt;
        or passthrough_assert_true(&lt;br /&gt;
            no_error,&lt;br /&gt;
            nil,&lt;br /&gt;
            &amp;quot;reverse product override lookup failed: no override was found that maps to item ID &amp;#039;&amp;quot; ..&lt;br /&gt;
            item_id .. &amp;quot;&amp;#039;&amp;quot;&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- #######################&lt;br /&gt;
-- ### PRODUCT LOOKUPS ###&lt;br /&gt;
-- #######################&lt;br /&gt;
&lt;br /&gt;
-- Checks whether a an item `product` exists.&lt;br /&gt;
-- Takes into account that `product` can have a product override.&lt;br /&gt;
function p.product_exsits(product)&lt;br /&gt;
    assert_value_not_nil(product)&lt;br /&gt;
&lt;br /&gt;
    local product_with_override = p.lookup_product_id_override(product, true)&lt;br /&gt;
        or product&lt;br /&gt;
&lt;br /&gt;
    return itemModule.item_exists(product_with_override)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Asserts that a product `product` exists.&lt;br /&gt;
-- `product` can be a product ID (including overriden ones), item ID or name.&lt;br /&gt;
function p.assert_product_exists(product, custom_message)&lt;br /&gt;
    assert_value_not_nil(product)&lt;br /&gt;
&lt;br /&gt;
    if not p.product_exsits(product) then&lt;br /&gt;
        if custom_message then&lt;br /&gt;
            error(&amp;quot;product exist assertion failed for product &amp;#039;&amp;quot; .. product .. &amp;quot;&amp;#039;: &amp;quot; .. custom_message)&lt;br /&gt;
        else&lt;br /&gt;
            error(&amp;quot;product exist assertion failed for product &amp;#039;&amp;quot; ..&lt;br /&gt;
                product ..&lt;br /&gt;
                &amp;quot;&amp;#039;: no product was found. Make sure that a recipe exists with given product or that a product override is defined in Module:Item recipe&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Lookups recipe IDs by product ID `product_id`.&lt;br /&gt;
-- Accepts overriden `product_id`s.&lt;br /&gt;
--&lt;br /&gt;
-- Returns an array of matches.&lt;br /&gt;
--&lt;br /&gt;
-- **NOTE:** This is an expensive function.&lt;br /&gt;
function p.lookup_recipe_ids_by_product_id(product_id)&lt;br /&gt;
    assert_value_not_nil(product_id)&lt;br /&gt;
&lt;br /&gt;
    local result = recipe_ids_by_product_ids[&lt;br /&gt;
    p.reverse_lookup_product_id_override(product_id, true)&lt;br /&gt;
    or product_id&lt;br /&gt;
    ]&lt;br /&gt;
&lt;br /&gt;
    -- always returns an array&lt;br /&gt;
    if type(result) == &amp;#039;string&amp;#039; then&lt;br /&gt;
        return { result }&lt;br /&gt;
    elseif result ~= nil then&lt;br /&gt;
        return result&lt;br /&gt;
    else&lt;br /&gt;
        return {}&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ######################&lt;br /&gt;
-- ### RECIPE LOOKUPS ###&lt;br /&gt;
-- ######################&lt;br /&gt;
&lt;br /&gt;
-- Lookups recipe by recipe ID `recipe_id`.&lt;br /&gt;
--&lt;br /&gt;
-- Returns `nil` if no recipe was found.&lt;br /&gt;
-- Set `no_error` to `true` to return `nil` instead.&lt;br /&gt;
function p.lookup_recipe_by_recipe_id(recipe_id, no_error)&lt;br /&gt;
    assert_value_not_nil(recipe_id)&lt;br /&gt;
&lt;br /&gt;
    return recipes_by_recipe_ids[recipe_id]&lt;br /&gt;
        or passthrough_assert_true(&lt;br /&gt;
            no_error,&lt;br /&gt;
            nil,&lt;br /&gt;
            &amp;quot;failed to lookup recipe by recipe id &amp;#039;&amp;quot; .. recipe_id .. &amp;quot;&amp;#039;: no recipe was found&amp;quot;&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Asserts that a recipe ID `recipe_id` exists.&lt;br /&gt;
-- function assert_recipe_id_exists(recipe_id)&lt;br /&gt;
--     error(&amp;quot;not impl&amp;quot;)&lt;br /&gt;
-- end&lt;br /&gt;
&lt;br /&gt;
-- Searches recipes by `query`. `query` can be an product ID (item ID - including overrden ones),&lt;br /&gt;
-- name or a recipe ID.&lt;br /&gt;
function p.search_recipes(query)&lt;br /&gt;
    assert_value_not_nil(query, &amp;quot;failed to lookup recipes by method and item ID: item ID was not provided&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- check if query is a recipe ID&lt;br /&gt;
    local recipe_by_recipe_id = recipes_by_recipe_ids[query]&lt;br /&gt;
    if recipe_by_recipe_id then&lt;br /&gt;
        -- if so - we got a direct match!&lt;br /&gt;
        return { recipe_by_recipe_id }&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- check if query is an item name/ID&lt;br /&gt;
    -- this is a last possibility.&lt;br /&gt;
    -- TODO add a custom errror here?&lt;br /&gt;
    local recipe_product_by_item_id_or_name = itemModule.lookup_item_id(query)&lt;br /&gt;
&lt;br /&gt;
    local recipe_ids_by_item_id_or_name = p.lookup_recipe_ids_by_product_id(recipe_product_by_item_id_or_name)&lt;br /&gt;
&lt;br /&gt;
    local recipes = {}&lt;br /&gt;
    if recipe_ids_by_item_id_or_name ~= nil then&lt;br /&gt;
        for _, recipe_id in ipairs(recipe_ids_by_item_id_or_name) do&lt;br /&gt;
            table.insert(recipes, p.lookup_recipe_by_recipe_id(recipe_id))&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return recipes&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Lookups recipes by production method.&lt;br /&gt;
--&lt;br /&gt;
-- Raises an error if no recipes were found by the production method.&lt;br /&gt;
function p.lookup_recipes_by_production_method(method)&lt;br /&gt;
    assert_value_not_nil(method, &amp;quot;failed to lookup recipes by production method: no method was given&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    p.assert_method_exists(method,&lt;br /&gt;
        &amp;quot;failed to lookup recipes by production method: method &amp;#039;&amp;quot; .. method .. &amp;quot;&amp;#039; doesn&amp;#039;t exist&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local recipe_ids_grouped = p.lookup_recipe_ids_and_availability_by_method(method)&lt;br /&gt;
&lt;br /&gt;
    local recipes = {}&lt;br /&gt;
    for availability, recipe_ids in pairs(recipe_ids_grouped) do&lt;br /&gt;
        for _, recipe_id in ipairs(recipe_ids) do&lt;br /&gt;
            local recipe = p.lookup_recipe_by_recipe_id(recipe_id)&lt;br /&gt;
            table.insert(recipes, recipe)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return recipes&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Filters given recipes by production method.&lt;br /&gt;
-- Any casing is allowed for production method.&lt;br /&gt;
-- function p.filter_recipes_by_production_method(recipes, production_method)&lt;br /&gt;
--     p.assert_method_exists(production_method)&lt;br /&gt;
&lt;br /&gt;
--     return filter_table(&lt;br /&gt;
--         p.lookup_recipe_ids_by_method(production_method),&lt;br /&gt;
--         function(_, recipe_ids_grouped)&lt;br /&gt;
--             return find_first_numeric_table_item_matching_condition(&lt;br /&gt;
--                 recipes,&lt;br /&gt;
--                 function(recipe) return recipe.id == recipe_ids_grouped end&lt;br /&gt;
--             ) ~= nil&lt;br /&gt;
--         end&lt;br /&gt;
--     )&lt;br /&gt;
-- end&lt;br /&gt;
&lt;br /&gt;
-- ##############################&lt;br /&gt;
-- ### MATERIAL ORDER LOOKUPS ###&lt;br /&gt;
-- ##############################&lt;br /&gt;
&lt;br /&gt;
-- Searches a material item using the material order config, returning the order number or `nil`,&lt;br /&gt;
-- if the material order config doesn&amp;#039;t have the queried item.&lt;br /&gt;
--&lt;br /&gt;
-- Takes in an item ID or name.&lt;br /&gt;
local function try_lookup_order_of_material(material)&lt;br /&gt;
    assert_value_not_nil(material, &amp;quot;failed to lookup order of material: material was not provided&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    local item_id = itemModule.lookup_item_id(material, true)&lt;br /&gt;
&lt;br /&gt;
    if item_id == nil then&lt;br /&gt;
        error(&amp;quot;failed to lookup order of material: material &amp;#039;&amp;quot; .. material .. &amp;quot;&amp;#039; does not exist&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return materials_order_by_material_ids[item_id]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- =======================&lt;br /&gt;
&lt;br /&gt;
-- Produces a &amp;quot;note&amp;quot; element used in generation of item recipes.&lt;br /&gt;
-- Takes in a CSS-compatible color and text content.&lt;br /&gt;
local function generate_note_element(color, text)&lt;br /&gt;
    local el = mw.html.create(&amp;#039;span&amp;#039;)&lt;br /&gt;
        :addClass(&amp;quot;item-recipe-note&amp;quot;)&lt;br /&gt;
        :node(text)&lt;br /&gt;
&lt;br /&gt;
    if color then&lt;br /&gt;
        el:css(&amp;#039;color&amp;#039;, color)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return el&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function generate_info_icon(frame, kind)&lt;br /&gt;
    if kind == &amp;#039;research&amp;#039; then&lt;br /&gt;
        return frame:expandTemplate {&lt;br /&gt;
            title = &amp;#039;Tooltip&amp;#039;,&lt;br /&gt;
            args = {&lt;br /&gt;
                &amp;quot;[[File:JobIconResearchDirector.png|24px|link=Research_and_Development#R&amp;amp;D_Tree]]&amp;quot;,&lt;br /&gt;
                &amp;quot;This recipe is unlocked by &amp;#039;&amp;#039;&amp;#039;research&amp;#039;&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    elseif kind == &amp;#039;emag&amp;#039; then&lt;br /&gt;
        return frame:expandTemplate {&lt;br /&gt;
            title = &amp;#039;Tooltip&amp;#039;,&lt;br /&gt;
            args = {&lt;br /&gt;
                &amp;quot;[[File:Emag.png|42px|link=Cryptographic Sequencer]]&amp;quot;,&lt;br /&gt;
                &amp;quot;This recipe is unlocked by &amp;#039;&amp;#039;&amp;#039;EMAG&amp;#039;&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    elseif kind == &amp;#039;progression-symbol&amp;#039; then&lt;br /&gt;
        return mw.html.create(&amp;quot;span&amp;quot;)&lt;br /&gt;
            :addClass(&amp;#039;info-icon-progression-symbol&amp;#039;)&lt;br /&gt;
            :node(&amp;quot;↓&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        error(&amp;quot;failed to generate an info icon: unknown kind &amp;quot; .. kind)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- ====================&lt;br /&gt;
&lt;br /&gt;
-- Generates a recipe element for a given item.&lt;br /&gt;
-- This is the main external function of this module.&lt;br /&gt;
function p.generate_item_recipe(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
&lt;br /&gt;
    -- [REQUIRED]&lt;br /&gt;
&lt;br /&gt;
    -- Item name, alias, item ID or a recipe ID. Required.&lt;br /&gt;
    local input_query = args[1]&lt;br /&gt;
    assert_value_not_nil(input_query, &amp;quot;failed to generate a recipe for query: query was not provided&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- [OPTIONAL]&lt;br /&gt;
&lt;br /&gt;
    -- Amount of item. Default is 1.&lt;br /&gt;
    -- Must be a string since Module:Item uses string amount.&lt;br /&gt;
    -- All values from templates come as strings.&lt;br /&gt;
    local input_amount = nil_or(args[2], &amp;quot;1&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- Item production method. Can be &amp;quot;nil&amp;quot;, in which case it&amp;#039;s looked up.&lt;br /&gt;
    local input_method = args[3]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    -- Whether to only generate a materials block.&lt;br /&gt;
    local input_materials_only = yesNo(args[&amp;quot;materials only&amp;quot;] or args[&amp;quot;mat only&amp;quot;] or false)&lt;br /&gt;
&lt;br /&gt;
    -- Layout of materials in materials only mode.&lt;br /&gt;
    local input_materials_only_layout = args[&amp;quot;materials only layout&amp;quot;] or args[&amp;quot;mat only layout&amp;quot;] or &amp;quot;vertical&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    -- ============&lt;br /&gt;
&lt;br /&gt;
    -- search recipes&lt;br /&gt;
&lt;br /&gt;
    local recipes = p.search_recipes(input_query)&lt;br /&gt;
    local recipes_count = numeric_table_length(recipes)&lt;br /&gt;
    if recipes_count == 0 then&lt;br /&gt;
        error(&amp;quot;failed to generate a recipe for item: no recipe was found for item &amp;#039;&amp;quot; ..&lt;br /&gt;
            input_query ..&lt;br /&gt;
            &amp;quot;&amp;#039; (input method: &amp;#039;&amp;quot; ..&lt;br /&gt;
            (input_method or &amp;quot;nil&amp;quot;) ..&lt;br /&gt;
            &amp;quot;&amp;#039;). Make sure a recipe exists for this item or define a product override for an existing recipe in Module:Item recipe&amp;quot;)&lt;br /&gt;
    elseif recipes_count &amp;gt; 1 then&lt;br /&gt;
        error(&amp;quot;failed to generate a recipe for item: found multiple recipes for item &amp;#039;&amp;quot; ..&lt;br /&gt;
            input_query ..&lt;br /&gt;
            &amp;quot;&amp;#039; (input method: &amp;#039;&amp;quot; ..&lt;br /&gt;
            (input_method or &amp;quot;nil&amp;quot;) ..&lt;br /&gt;
            &amp;quot;&amp;#039;). Rendering multiple recipes is currently unsupported&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local recipe = recipes[1]&lt;br /&gt;
&lt;br /&gt;
    -- search recipe methods&lt;br /&gt;
&lt;br /&gt;
    local recipe_methods_lookup = p.lookup_methods_with_availability_by_recipe_id(recipe.id)&lt;br /&gt;
    local recipe_methods_count = table_length(recipe_methods_lookup)&lt;br /&gt;
    if recipe_methods_count == 0 then&lt;br /&gt;
        error(&amp;quot;failed to generate a recipe for item: no methods were found for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
            recipe.id ..&lt;br /&gt;
            &amp;quot;&amp;#039; (input query: &amp;#039;&amp;quot; .. input_query .. &amp;quot;&amp;#039;; input method: &amp;#039;&amp;quot; ..&lt;br /&gt;
            (input_method or &amp;quot;nil&amp;quot;) ..&lt;br /&gt;
            &amp;quot;&amp;#039;). This shouldn&amp;#039;t usually happen because the present recipes are bound to some production methods. Probable cause: bug in the recipe generation code&amp;quot;)&lt;br /&gt;
    elseif recipe_methods_count &amp;gt; 1 and input_method == nil then&lt;br /&gt;
        local methods = map_numeric_table(&lt;br /&gt;
            recipe_methods_lookup,&lt;br /&gt;
            function(match)&lt;br /&gt;
                return match.method&lt;br /&gt;
            end&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        error(&amp;quot;failed to generate a recipe for item: found multiple production methods for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
            recipe.id ..&lt;br /&gt;
            &amp;quot;&amp;#039; (input query: &amp;#039;&amp;quot; ..&lt;br /&gt;
            input_query ..&lt;br /&gt;
            &amp;quot;&amp;#039;) and input production method was NOT specified. Rendering multiple recipes is unsupported, so please specify a production method from available methods for this recipe: &amp;#039;&amp;quot; ..&lt;br /&gt;
            table.concat(methods, &amp;quot;&amp;#039;, &amp;#039;&amp;quot;) .. &amp;quot;&amp;#039;&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local recipe_method&lt;br /&gt;
    local recipe_availability&lt;br /&gt;
    if input_method == nil then&lt;br /&gt;
        -- if no input methods is specified, use the single available recipe method&lt;br /&gt;
        recipe_method = recipe_methods_lookup[1].method&lt;br /&gt;
        recipe_availability = recipe_methods_lookup[1].availability&lt;br /&gt;
    else&lt;br /&gt;
        -- otherwise, the number of available methods can vary,&lt;br /&gt;
        -- so filter it down to a single one based on the input method.&lt;br /&gt;
&lt;br /&gt;
        local method_item_id = p.lookup_method_item_id(input_method)&lt;br /&gt;
&lt;br /&gt;
        local recipe_methods_lookup_match = find_first_numeric_table_item_matching_condition(&lt;br /&gt;
            recipe_methods_lookup,&lt;br /&gt;
            function(lookup_result)&lt;br /&gt;
                return lookup_result.method == method_item_id&lt;br /&gt;
            end&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
        if recipe_methods_lookup_match == nil then&lt;br /&gt;
            error(&amp;quot;failed to generate a recipe for item: no production methods were found for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
                recipe.id ..&lt;br /&gt;
                &amp;quot;&amp;#039; (input query: &amp;#039;&amp;quot; ..&lt;br /&gt;
                input_query ..&lt;br /&gt;
                &amp;quot;&amp;#039;) matching input method &amp;#039;&amp;quot; ..&lt;br /&gt;
                (input_method or &amp;quot;nil&amp;quot;) ..&lt;br /&gt;
                &amp;quot;&amp;#039;. Make sure a recipe exists for this item with the specified production method or define a product override for an existing recipe with the specified production method in Module:Item recipe&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        recipe_method = recipe_methods_lookup_match.method&lt;br /&gt;
        recipe_availability = recipe_methods_lookup_match.availability&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- extract products&lt;br /&gt;
&lt;br /&gt;
    -- recipe product IDs mapped to amounts&lt;br /&gt;
    local recipe_products = {}&lt;br /&gt;
    if recipe.result then&lt;br /&gt;
        recipe_products[recipe.result] = 1&lt;br /&gt;
    elseif recipe.resultReagents then&lt;br /&gt;
        for product_id, amount in pairs(recipe.resultReagents) do&lt;br /&gt;
            recipe_products[product_id] = amount&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        error(&amp;quot;failed to generate a recipe for item: no products were found for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
            recipe.id ..&lt;br /&gt;
            &amp;quot;&amp;#039; (input query: &amp;#039;&amp;quot; ..&lt;br /&gt;
            input_query ..&lt;br /&gt;
            &amp;quot;&amp;#039;). This might be due to recipe having another way to describe products&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- generate recipe element&lt;br /&gt;
&lt;br /&gt;
    local recipe_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;item-recipe&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if input_materials_only_layout == &amp;quot;vertical&amp;quot; or input_materials_only_layout == &amp;quot;ver&amp;quot; then&lt;br /&gt;
        recipe_el:addClass(&amp;quot;item-recipe-materials-layout-vertical&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
        recipe_el:addClass(&amp;quot;item-recipe-materials-layout-horizontal&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    local body_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
        :addClass(&amp;#039;item-recipe-body&amp;#039;)&lt;br /&gt;
        :css(&amp;#039;background&amp;#039;, &amp;#039;linear-gradient(135deg, var(--bg-color-light-x2), var(--bg-color-light))&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    local materials_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;item-recipe-materials&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    assert_value_not_nil(recipe.materials,&lt;br /&gt;
        &amp;quot;failed to generate a recipe for item: no &amp;#039;materials&amp;#039; are specified for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
        recipe.id .. &amp;quot;&amp;#039; (input query: &amp;#039;&amp;quot; ..&lt;br /&gt;
        input_query ..&lt;br /&gt;
        &amp;quot;&amp;#039;)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- copy list of materials, but without costs&lt;br /&gt;
    local materials = {}&lt;br /&gt;
    for material, cost in pairs(recipe.materials) do&lt;br /&gt;
        table.insert(materials, material)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- sort materials list based on material order config.&lt;br /&gt;
    -- materials not in config will come after the ordered materials.&lt;br /&gt;
    table.sort(materials, function(first, second)&lt;br /&gt;
        return (try_lookup_order_of_material(first) or 999999999999)&lt;br /&gt;
            &amp;lt; (try_lookup_order_of_material(second) or 999999999999)&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    -- generate materials elements in sorted order&lt;br /&gt;
    for _, material in ipairs(materials) do&lt;br /&gt;
        local cost = recipe.materials[material]&lt;br /&gt;
&lt;br /&gt;
        local material_item_id = itemModule.lookup_item_id(material, true)&lt;br /&gt;
        if material_item_id == nil then&lt;br /&gt;
            error(&amp;quot;failed to generate a recipe for item: material &amp;#039;&amp;quot; ..&lt;br /&gt;
                material ..&lt;br /&gt;
                &amp;quot;&amp;#039; was not found in item registry (for recipe ID &amp;#039;&amp;quot; ..&lt;br /&gt;
                recipe.id .. &amp;quot;&amp;#039;; input query: &amp;#039;&amp;quot; ..&lt;br /&gt;
                input_query ..&lt;br /&gt;
                &amp;quot;&amp;#039;). Make sure that the material is added to the item name overrides in Module:Item&amp;quot;)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        materials_el:node(&lt;br /&gt;
            itemModule.generate_item {&lt;br /&gt;
                material_item_id,&lt;br /&gt;
                cost * input_amount&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    body_el:node(materials_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if input_materials_only then&lt;br /&gt;
        recipe_el:addClass(&amp;quot;materials-only&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        recipe_el:node(body_el)&lt;br /&gt;
    else&lt;br /&gt;
        local header_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;item-recipe-header&amp;quot;)&lt;br /&gt;
            :css(&amp;#039;background&amp;#039;, &amp;#039;linear-gradient(120deg, var(--bg-color-light-x3), var(--bg-color-light-x2))&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
        recipe_el:node(header_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        local product_and_method_container_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;item-recipe-product-and-method-container&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        header_el:node(product_and_method_container_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        local products_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;item-recipe-products&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        product_and_method_container_el:node(products_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        for product_id, amount in pairs(recipe_products) do&lt;br /&gt;
            local product_el = itemModule.generate_item {&lt;br /&gt;
                    [1] = product_id,&lt;br /&gt;
                    [2] = amount * input_amount,&lt;br /&gt;
                    cap = true&lt;br /&gt;
                }&lt;br /&gt;
                :addClass(&amp;quot;item-recipe-product&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            products_el:node(product_el)&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        -- TODO: not all methods will be items, so this will eventually break.&lt;br /&gt;
        local method_el = methods_items_els_cache[recipe_method]&lt;br /&gt;
        if method_el == nil then&lt;br /&gt;
            method_el = mw.html.create(&amp;quot;span&amp;quot;)&lt;br /&gt;
                :addClass(&amp;#039;item-recipe-method&amp;#039;)&lt;br /&gt;
                :node(itemModule.generate_item { [1] = recipe_method, capitalize = true })&lt;br /&gt;
&lt;br /&gt;
            methods_items_els_cache[recipe_method] = method_el&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        product_and_method_container_el:node(method_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        local info_icons_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;item-recipe-info-icons&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        header_el:node(info_icons_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        recipe_el:node(body_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        local complete_time_el = mw.html.create(&amp;quot;span&amp;quot;)&lt;br /&gt;
            :addClass(&amp;#039;item-recipe-complete-time&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
        if recipe.completetime == 0 then&lt;br /&gt;
            complete_time_el:node(&amp;quot;Instant&amp;quot;)&lt;br /&gt;
        else&lt;br /&gt;
            complete_time_el:node(format_seconds_to_short_string(recipe.completetime * input_amount))&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        body_el:node(complete_time_el)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        local notes_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
            :addClass(&amp;quot;item-recipe-notes&amp;quot;)&lt;br /&gt;
            :css(&amp;#039;background&amp;#039;, &amp;#039;linear-gradient(40deg, var(--bg-color-light-x3), var(--bg-color-light-x2))&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
        -- if recipe is not available by default,&lt;br /&gt;
        -- generate &amp;quot;info icons&amp;quot; and notes (if needed) telling about it.&lt;br /&gt;
        if recipe_availability ~= &amp;#039;static&amp;#039; then&lt;br /&gt;
            local is_recipe_unlocked_by_research = string.find(recipe_availability, &amp;quot;dynamic&amp;quot;, 1, true) ~= nil&lt;br /&gt;
            local is_recipe_unlocked_by_emag = string.find(recipe_availability, &amp;quot;emag&amp;quot;, 1, true) ~= nil&lt;br /&gt;
            local is_recipe_unlocked_by_research_and_then_emag = is_recipe_unlocked_by_research and&lt;br /&gt;
                is_recipe_unlocked_by_emag&lt;br /&gt;
&lt;br /&gt;
            if is_recipe_unlocked_by_research_and_then_emag then&lt;br /&gt;
                recipe_el:addClass(&amp;#039;item-recipe-by-research&amp;#039;)&lt;br /&gt;
                recipe_el:addClass(&amp;#039;item-recipe-by-emag&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
                info_icons_el:node(generate_info_icon(current_frame, &amp;#039;research&amp;#039;))&lt;br /&gt;
                info_icons_el:node(generate_info_icon(current_frame, &amp;#039;progression-symbol&amp;#039;))&lt;br /&gt;
                info_icons_el:node(generate_info_icon(current_frame, &amp;#039;emag&amp;#039;))&lt;br /&gt;
&lt;br /&gt;
                notes_el:node(&lt;br /&gt;
                    generate_note_element(&lt;br /&gt;
                        nil,&lt;br /&gt;
                        current_frame:preprocess(&lt;br /&gt;
                            &amp;quot;&amp;#039;&amp;#039;&amp;#039;This recipe is unlocked by [[Cryptographic Sequencer|EMAG]] after it has been [[Research_and_Development#R&amp;amp;D_Tree|researched]]&amp;#039;&amp;#039;&amp;#039;&amp;quot;)&lt;br /&gt;
                    )&lt;br /&gt;
                )&lt;br /&gt;
            elseif is_recipe_unlocked_by_research then&lt;br /&gt;
                recipe_el:addClass(&amp;#039;item-recipe-by-research&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
                info_icons_el:node(generate_info_icon(current_frame, &amp;#039;research&amp;#039;))&lt;br /&gt;
&lt;br /&gt;
                -- if not is_recipe_unlocked_by_research_and_then_emag then&lt;br /&gt;
                -- 	notes_el:node(&lt;br /&gt;
                -- 		generate_note_element(&lt;br /&gt;
                -- 			&amp;quot;gold&amp;quot;,&lt;br /&gt;
                -- 			&amp;quot;&amp;#039;&amp;#039;&amp;#039;This recipe is unlocked by research&amp;#039;&amp;#039;&amp;#039;&amp;quot;&lt;br /&gt;
                -- 		)&lt;br /&gt;
                -- 	)&lt;br /&gt;
                -- end&lt;br /&gt;
            else&lt;br /&gt;
                recipe_el:addClass(&amp;#039;item-recipe-by-emag&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
                info_icons_el:node(generate_info_icon(current_frame, &amp;#039;emag&amp;#039;))&lt;br /&gt;
&lt;br /&gt;
                -- if not is_recipe_unlocked_by_research_and_then_emag then&lt;br /&gt;
                -- 	notes_el:node(&lt;br /&gt;
                -- 		generate_note_element(&lt;br /&gt;
                -- 			&amp;quot;var(--danger-color)&amp;quot;,&lt;br /&gt;
                -- 			current_frame:preprocess(&lt;br /&gt;
                -- 			&amp;quot;&amp;#039;&amp;#039;&amp;#039;This recipe is unlocked by [[Cryptographic Sequencer|{{item|EmagUnlimited|l=EMAG}}]]&amp;#039;&amp;#039;&amp;#039;&amp;quot;)&lt;br /&gt;
                -- 		)&lt;br /&gt;
                -- 	)&lt;br /&gt;
                -- end&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
&lt;br /&gt;
        recipe_el:node(notes_el)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    if not was_template_styles_tag_el_added then&lt;br /&gt;
        recipe_el:node(current_frame:extensionTag(&amp;quot;templatestyles&amp;quot;, &amp;quot;&amp;quot;, { src = &amp;#039;Template:Item recipe/styles.css&amp;#039; }))&lt;br /&gt;
&lt;br /&gt;
        was_template_styles_tag_el_added = true&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return recipe_el&lt;br /&gt;
        :allDone()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Generates an alphabetical list of recipes (elements) for a given production method.&lt;br /&gt;
-- Used to list all recipes for a particular method.&lt;br /&gt;
-- Recipes are sorted based on their products (the items display names).&lt;br /&gt;
function p.generate_list_of_recipes_for_method(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
&lt;br /&gt;
    local method = args[1]&lt;br /&gt;
    assert_value_not_nil(method, &amp;quot;failed to generate a list of recipes for a method: method was not provided&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- Limit on how many recipes to generate&lt;br /&gt;
    local recipes_limit = tonumber(args.limit) or 99999999&lt;br /&gt;
&lt;br /&gt;
    local recipes = p.lookup_recipes_by_production_method(method)&lt;br /&gt;
    assert_value_not_nil(recipes, &amp;quot;failed to generate a list of recipes for a method: unknown method: &amp;quot; .. method)&lt;br /&gt;
&lt;br /&gt;
    -- =======================&lt;br /&gt;
&lt;br /&gt;
    local container_el = mw.html.create(&amp;quot;div&amp;quot;)&lt;br /&gt;
        :addClass(&amp;quot;item-recipes-list&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    -- -- generate a list of products&lt;br /&gt;
    -- local products = {}&lt;br /&gt;
    -- local i = 1&lt;br /&gt;
    -- for product_item_id, _ in ipairs(recipes) do&lt;br /&gt;
    --     table.insert(products, product_item_id)&lt;br /&gt;
&lt;br /&gt;
    --     if i == recipes_limit then&lt;br /&gt;
    --         break&lt;br /&gt;
    --     end&lt;br /&gt;
&lt;br /&gt;
    --     i = i + 1&lt;br /&gt;
    -- end&lt;br /&gt;
&lt;br /&gt;
    -- -- sort the list of products alphabetically&lt;br /&gt;
    -- -- by looking up the item names and comparing them&lt;br /&gt;
    -- table.sort(products, function(first, second)&lt;br /&gt;
    --     local first_item_id = p.lookup_product_id_override(first, true)&lt;br /&gt;
    --         or first&lt;br /&gt;
    --     local second_item_id = p.lookup_product_id_override(second, true)&lt;br /&gt;
    --         or second&lt;br /&gt;
&lt;br /&gt;
    --     p.assert_product_exists(first_item_id)&lt;br /&gt;
    --     p.assert_product_exists(second_item_id)&lt;br /&gt;
&lt;br /&gt;
    --     return itemModule.lookup_item_name_by_item_id(first_item_id)&lt;br /&gt;
    --         &amp;lt; itemModule.lookup_item_name_by_item_id(second_item_id)&lt;br /&gt;
    -- end)&lt;br /&gt;
&lt;br /&gt;
    -- -- generate recipe elements&lt;br /&gt;
    -- for _, product in ipairs(products) do&lt;br /&gt;
    --     container_el:node(p.generate_item_recipe {&lt;br /&gt;
    --         [1] = p.lookup_product_id_override(product),&lt;br /&gt;
    --         [2] = 1,&lt;br /&gt;
    --         [3] = method&lt;br /&gt;
    --     })&lt;br /&gt;
    -- end&lt;br /&gt;
&lt;br /&gt;
    -- generate recipe elements&lt;br /&gt;
    for _, recipe in ipairs(recipes) do&lt;br /&gt;
        container_el:node(&lt;br /&gt;
            p.generate_item_recipe {&lt;br /&gt;
                [1] = recipe.id,&lt;br /&gt;
                [2] = 1,&lt;br /&gt;
                [3] = method&lt;br /&gt;
            }&lt;br /&gt;
        )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return container_el&lt;br /&gt;
        :allDone()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Aliser</name></author>
	</entry>
</feed>