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