Module:Item recipe: Difference between revisions
From Space Station 14 Wiki
(wip) |
(wip) |
||
Line 1: | Line 1: | ||
-- Contains utilities for working with in-game | -- Contains utilities for working with in-game item recipes. | ||
-- todo: material sorting. based on alphabetical sorting? maybe at .json generation step, convert materials to an array? | -- todo: material sorting. based on alphabetical sorting? maybe at .json generation step, convert materials to an array? | ||
Line 5: | Line 5: | ||
local p = {} --p stands for package | local p = {} --p stands for package | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local | local itemModule = require('Module:Item') | ||
local | -- An array of recipe groups. | ||
-- Recipes are grouped by `method`. | |||
-- Note that the order matters for perfomance - the recipe lookup happens from top to bottom. | |||
local recipe_groups = { | |||
{ method = "autolathe", recipes = mw.loadJsonData("Module:Item recipe/recipes by method/autolathe.json") }, | |||
{ method = "protolathe", recipes = mw.loadJsonData("Module:Item recipe/recipes by method/protolathe.json") }, | |||
} | } | ||
Line 20: | Line 24: | ||
function numeric_table_length(t) | local function numeric_table_length(t) | ||
local count = 0 | local count = 0 | ||
for _ in ipairs(t) do count = count + 1 end | for _ in ipairs(t) do count = count + 1 end | ||
Line 26: | Line 30: | ||
end | end | ||
function table_length(t) | local function table_length(t) | ||
local count = 0 | local count = 0 | ||
for _ in pairs(t) do count = count + 1 end | for _ in pairs(t) do count = count + 1 end | ||
Line 32: | Line 36: | ||
end | end | ||
function table_has_value(tab, val) | local function table_has_value(tab, val) | ||
for _, value in ipairs(tab) do | for _, value in ipairs(tab) do | ||
if value == val then | if value == val then | ||
Line 42: | Line 46: | ||
end | end | ||
function assert_value_not_nil(value, error_message) | local function assert_value_not_nil(value, error_message) | ||
if value == nil then | if value == nil then | ||
if error_message == nil then | if error_message == nil then | ||
Line 49: | Line 53: | ||
error(error_message) | error(error_message) | ||
end | 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 | ||
end | end | ||
Line 54: | Line 69: | ||
-- ==================== | -- ==================== | ||
-- Searches for | -- Searches a recipe for a given item, optionally with a specific production method. | ||
-- | -- @returns A table with `method` and `recipe`, or `nil` if no recipe was found. | ||
local function lookup_recipe_by_item_id(item_id, method) | |||
assert_value_not_nil(item_id, "failed to lookup recipes by method and item ID: item ID was not provided") | |||
function | |||
assert_value_not_nil( | |||
if method == | -- production methods to lookup through | ||
local methods_to_lookup = {} | |||
for _, | if method == nil then | ||
-- no method specified = look through all methods until the recipe is found | |||
for _, recipe_group in ipairs(recipe_groups) do | |||
table.insert(methods_to_lookup, recipe_group.method) | |||
end | |||
else | |||
-- method specified = only look through recipes with that production method | |||
table.insert(methods_to_lookup, method) | |||
end | |||
if | for _, method in ipairs(methods_to_lookup) do | ||
for _, recipe in ipairs(recipe_groups[method].recipes) do | |||
if recipe.result == item_id then | |||
return { | |||
method = method, | |||
recipe = recipe | |||
} | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
Line 87: | Line 102: | ||
-- Generates an HTML element, containing recipe components used to produce given item with a given method. | -- Generates an HTML element, containing recipe components used to produce given item with a given method. | ||
-- Returns a DIV containg {{item}}s. | -- Returns a DIV containg {{item}}s. | ||
function | function generate_recipe_materials(frame) | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
Line 95: | Line 110: | ||
assert_value_not_nil(product, "failed to generate a recipe skeleton: product was not provided") | assert_value_not_nil(product, "failed to generate a recipe skeleton: product was not provided") | ||
local matching_recipes = | local matching_recipes = lookup_recipes_by_method_and_item_id(method, product) | ||
local matching_recipes_len = numeric_table_length(matching_recipes) | local matching_recipes_len = numeric_table_length(matching_recipes) | ||
local | |||
end | |||
function p.generate_item_recipe(frame) | |||
local args = getArgs(frame) | |||
-- Item name, alias or ID. Required. | |||
local item = args[1] | |||
-- Amount of item. Default is 1. | |||
-- Must be a string since Module:Item uses string amount. | |||
-- All values from templates come as strings. | |||
local amount = nil_or(args[2], "1") | |||
-- Item production method. Can be "nil", in which case it's looked up. | |||
local method = nil | |||
assert_value_not_nil(item, "failed to generate a recipe for item: item was not provided") | |||
-- ============ | |||
local current_frame = mw:getCurrentFrame() | |||
local item_id = itemModule.lookup_item_id_by_name_and_amount(item, amount) | |||
local recipe_lookup_result = lookup_recipe_by_item_id(item_id, method) | |||
assert_value_not_nil(recipe_lookup_result, "failed to generate a recipe for item: no recipe found for item " .. item_id .. " (method: " .. method ..")") | |||
local recipe = recipe_lookup_result.recipe | |||
local method = recipe_lookup_result.method | |||
local recipe_el = mw.html.create("div") | |||
:addClass("item-recipe") | :addClass("item-recipe") | ||
local product_el = mw.html.create("div") | |||
:addClass("item-recipe-product") | |||
recipe_el:node(current_frame:preprocess("{{item|" .. item_id .. "|" .. amount .. "}}")) | |||
recipe_el:node(product_el) | |||
local method_el = mw.html.create("div") | |||
:addClass("item-recipe-method") | |||
-- TODO: not all methods will be items, so this will eventually break. | |||
recipe_el:node(current_frame:preprocess("{{item|" .. method .. "}}")) | |||
recipe_el:node(method_el) | |||
local materials_el = mw.html.create("div") | |||
:addClass("item-recipe-materials") | |||
assert_value_not_nil(recipe_lookup_result.materials, "failed to generate a recipe for item: no 'materials' are specified for item " .. item_id .. " recipe (method: " .. method ..")") | |||
for material, cost in pairs(recipe_lookup_result.materials) do | |||
recipe_el:node(current_frame:preprocess("{{item|" .. material .. "|" .. (cost * amount) .. "}}")) | |||
end | end | ||
materials_el:node(materials_el) | |||
return recipe_el | |||
:allDone() | |||
end | end | ||
-- -- Generates a list of items needed for a recipe, along with exact amounts. | -- -- Generates a list of items needed for a recipe, along with exact amounts. |
Revision as of 17:22, 25 August 2024
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.
-- todo: material sorting. based on alphabetical sorting? maybe at .json generation step, convert materials to an array?
local p = {} --p stands for package
local getArgs = require('Module:Arguments').getArgs
local itemModule = require('Module:Item')
-- An array of recipe groups.
-- Recipes are grouped by `method`.
-- Note that the order matters for perfomance - the recipe lookup happens from top to bottom.
local recipe_groups = {
{ method = "autolathe", recipes = mw.loadJsonData("Module:Item recipe/recipes by method/autolathe.json") },
{ method = "protolathe", recipes = mw.loadJsonData("Module:Item recipe/recipes by method/protolathe.json") },
}
-- A table containing item recipes, identified by recipe IDs.
-- local recipes_by_recipe_id =
-- A table containing item recipe categories, identified by recipe category IDs.
-- local recipy_categories_by_recipe_category_id = mw.loadJsonData("Module:Item recipe/recipy categories by recipe category id.json")
-- ====================
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 table_has_value(tab, val)
for _, value in ipairs(tab) do
if value == val then
return true
end
end
return false
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
-- ====================
-- Searches a recipe for a given item, optionally with a specific production method.
-- @returns A table with `method` and `recipe`, or `nil` if no recipe was found.
local function lookup_recipe_by_item_id(item_id, method)
assert_value_not_nil(item_id, "failed to lookup recipes by method and item ID: item ID was not provided")
-- production methods to lookup through
local methods_to_lookup = {}
if method == nil then
-- no method specified = look through all methods until the recipe is found
for _, recipe_group in ipairs(recipe_groups) do
table.insert(methods_to_lookup, recipe_group.method)
end
else
-- method specified = only look through recipes with that production method
table.insert(methods_to_lookup, method)
end
for _, method in ipairs(methods_to_lookup) do
for _, recipe in ipairs(recipe_groups[method].recipes) do
if recipe.result == item_id then
return {
method = method,
recipe = recipe
}
end
end
end
end
-- ====================
-- Generates an HTML element, containing recipe components used to produce given item with a given method.
-- Returns a DIV containg {{item}}s.
function generate_recipe_materials(frame)
local args = getArgs(frame)
local method = args[1]
assert_value_not_nil(method, "failed to generate a recipe skeleton: method was not provided")
local product = args[2]
assert_value_not_nil(product, "failed to generate a recipe skeleton: product was not provided")
local matching_recipes = lookup_recipes_by_method_and_item_id(method, product)
local matching_recipes_len = numeric_table_length(matching_recipes)
end
function p.generate_item_recipe(frame)
local args = getArgs(frame)
-- Item name, alias or ID. Required.
local item = args[1]
-- Amount of item. Default is 1.
-- Must be a string since Module:Item uses string amount.
-- All values from templates come as strings.
local amount = nil_or(args[2], "1")
-- Item production method. Can be "nil", in which case it's looked up.
local method = nil
assert_value_not_nil(item, "failed to generate a recipe for item: item was not provided")
-- ============
local current_frame = mw:getCurrentFrame()
local item_id = itemModule.lookup_item_id_by_name_and_amount(item, amount)
local recipe_lookup_result = lookup_recipe_by_item_id(item_id, method)
assert_value_not_nil(recipe_lookup_result, "failed to generate a recipe for item: no recipe found for item " .. item_id .. " (method: " .. method ..")")
local recipe = recipe_lookup_result.recipe
local method = recipe_lookup_result.method
local recipe_el = mw.html.create("div")
:addClass("item-recipe")
local product_el = mw.html.create("div")
:addClass("item-recipe-product")
recipe_el:node(current_frame:preprocess("{{item|" .. item_id .. "|" .. amount .. "}}"))
recipe_el:node(product_el)
local method_el = mw.html.create("div")
:addClass("item-recipe-method")
-- TODO: not all methods will be items, so this will eventually break.
recipe_el:node(current_frame:preprocess("{{item|" .. method .. "}}"))
recipe_el:node(method_el)
local materials_el = mw.html.create("div")
:addClass("item-recipe-materials")
assert_value_not_nil(recipe_lookup_result.materials, "failed to generate a recipe for item: no 'materials' are specified for item " .. item_id .. " recipe (method: " .. method ..")")
for material, cost in pairs(recipe_lookup_result.materials) do
recipe_el:node(current_frame:preprocess("{{item|" .. material .. "|" .. (cost * amount) .. "}}"))
end
materials_el:node(materials_el)
return recipe_el
:allDone()
end
-- -- Generates a list of items needed for a recipe, along with exact amounts.
-- -- Needs a recipe ID passed as a single frame argument.
-- -- Uses {{Item}} to produce the items. Returns a div containing them.
-- function p.generate_recipe_items(frame)
-- local recipe_id = args[1]
-- assert_value_not_nil(recipe_id, "recipe ID was not provided")
-- end
return p