# Common parameter types

Parameter types common to various simplecrafting_lib functions have the following names:

* `craft_type` - a string identifying a group of crafting recipes
* `item_name` - a string identifying a particular type of item, eg **"default:stone"**
* `item_list` - the product of an `InvRef:get_list(listname)` call
* `count_list` - a list of items and quantities in the form as used in this mod's recipe definitions
* `listname` - a string identifying a list within an inventory

# Recipe Registration

## `simplecrafting_lib.register(craft_type, def)`

A crafting system first needs recipes for things to craft. Simplecrafting_lib allows recipes to be added to its library directly using this method.

example parameters:

	simplecrafting_lib.register("table", {
		input = {
			["group:stone"] = 1,
			["bucket:lava_bucket"] = 1,
		},
		output = "default:obsidian 2",
	
		-- Items which the crafting recipe produces, but is not
		-- formally used to make.
		returns = {
			["bucket:bucket"] = 1,
		},
	})
	
	simplecrafting_lib.register("furnace", {
		input = {
			["farming:flour"] = 1,
			["simplecrafting_lib:heat"] = 5,
		},
		output = "farming:bread",
	})
	
	simplecrafting_lib.register("fuel", {
		-- Group names are allowed when defining fuel inputs.
		-- If there is not an item specific recipe then it will take the
		-- definition of its longest burning group
		input = {
			["group:tree"] = 1,
		},
		output = "simplecrafting_lib:heat 40",
	})


The recipe def can have any other properties the user wishes to add, these examples only show the ones that are used by the crafting methods in this mod.

### `simplecrafting_lib.register_pre_craft` and `simplecrafting_lib.register_post_craft` callbacks

There are two optional callback functions that can be added to a simplecrafting_lib recipe that are useful in certain unusual circumstances; `pre_craft` and `post_craft`.

pre_craft is a callback with the signature `function(craft_type, recipe, output_stack, source_item_list)`. It is called whenever the recipe is evaluated (for example, to populate the output list of a crafting interface). It is intended to allow the output ItemStack to be modified programmatically, for example to add ItemStack metadata to the output. It should modify the output_stack accordingly. It is provided with the source itemlist that the crafting system will be drawing inputs from, which may include items other than those that are part of the recipe's inputs. Don't modify the source item list.

post_craft is a callback with the signature `function(craft_type, recipe, output_stack, source_inv, source_listname, destination_inv, destination_listname)`

It is called after the crafting operation has been executed.
	
* `output_stack` - a copy of the ItemStack that was just crafted.
* `source_inv, source_listname` - the inventory that the source materials were taken from
* `destination_inv, destination_listname` - the inventory that the output was placed into. The actual crafted instance of `output_stack` should already be in here, though it may have been merged with another stack so the count may be different.

Here's an example of how to use these callbacks to copy the metadata of a written book crafted using an existing written book and a blank book as inputs:

	simplecrafting_lib.register_pre_craft(function(craft_type, recipe, output_stack, source_item_list)
		-- screen for the recipes we care about. Note that you can't simply compare `recipe` to the
		-- registered recipe, since pre_craft may be called on a modified copy of the registered original
		if craft_type ~= "player" or recipe.output == nil or recipe.output:get_name() ~= "default:book_written" then
			return
		end
		-- find the first written book in the source inventory
		for k, source_item in ipairs(source_item_list) do
			if source_item:get_name() == "default:book_written" then
				-- the output book will have the same metadata as the source book
				local copymeta = source_item:get_meta():to_table()
				output_stack:get_meta():from_table(copymeta)
				return
			end
		end
	end)
	
	simplecrafting_lib.register_post_craft(function(craft_type, recipe, output_stack, source_inv, source_listname, destination_inv, destination_listname)
		-- screen for the recipes we care about
		if craft_type ~= "player" or recipe.output == nil or recipe.output:get_name() ~= "default:book_written" then
			return
		end
		-- add an additional copy of the book into the destination inventory
		destination_inv:add_item(destination_listname, output_stack)
	end)

## `simplecrafting_lib.register_reversible(craft_type, forward_def)`

A convenience method that registers the provided crafting recipe and also automatically creates and registers a "reverse" craft of the same type.

This should generally only be done with craft that turns one type of item into one other type of item (for example, metal ingots <-> metal blocks), but it will still work if there are multiple inputs.

If there's more than one input type it will use "returns" to give them to the player in the reverse craft, using the input with the highest count as the primary output.

Don't use a recipe that has a **"group:"** input with this, because obviously that can't be turned into an output. The mod will assert if you try to do this.

# Legacy Recipe Import

These two methods will allow recipes to be imported into simplecrafting_lib from mods using the old built-in crafting system.

## `simplecrafting_lib.register_recipe_import_filter(filter_function)`

registers a filter function used by `import_legacy_recipes`. The mod using simplecrafting_lib will need to define this filter function. The filter function's signature must be of the form:  

		function(recipe)

* `recipe` will be in the format described above for `simplecrafting_lib.register`. This method can modify the `recipe` parameter in the course of execution and those modifications will be reflected in the final registered version.

The filter function should return two values: `craft_type` (a string) and `clear_recipe` (a bool). If `craft_type` is **nil** then the recipe won't be imported. If `clear_recipe` is **true** then the recipe will be removed from the native crafting system.

As a simple example, the following code will register a filter that imports all "normal" crafting recipes into a craft_type called "table", and removes them from the legacy crafting system in the process, but leaves "cooking" and "fuel" recipes alone:

	simplecrafting_lib.register_recipe_import_filter(function(legacy_recipe)
		if legacy_recipe.input["simplecrafting_lib:heat"] then
			return nil, false
		elseif legacy_recipe.output and legacy_recipe.output:get_name() == "simplecrafting_lib:heat" then
			return nil, false
		else
			return "table", true
		end
	end)

# Simple Methods for User Crafting

The following methods are convenience functions that can be used by a mod to quickly implement a crafting system using simplecrafting_lib. They provide wrappers and templates that bring together the more basic functions of simplecrafting_lib in predefined ways.

## `simplecrafting_lib.craft_stack(craft_type, request_stack, source_inv, source_listname, destination_inv, destination_listname, player_or_pos)`

A convenienence function that attempts to do a generic crafting operation as part of an `on_metadata_inventory_take` or `on_metadata_inventory_move` call. You don't need to use this method if you want to do something more sophisticated.

* `request_stack` is an item stack that it is assumed the player has taken from a set of possible outputs. It is assumed that the contents of `request_stack` will be added to the destination inventory as a result of an existing inventory transfer and it will be deducted from the craft result. Note that the actual output of this crafting operation could be larger than `request_stack` if a recipe that produces an even multiple of `request_stack` cannot be found (for example, `request_stack` has 3 of an item but there is only a recipe producing 4 of the item in `craft_type`, a total of 4 will be crafted.)
* `source_inv`, `source_listname` are where the raw materials will be taken from.
* `destination_inv`, `destination_listname` are where the crafting outputs will be placed. This can be the same as the source inventory.
* `player_or_pos` is either a player object or a pos table. This is used for logging purposes and as a place to put output in the event that `destination_inv` can't hold it all.

## `simplecrafting_lib.show_crafting_guide(craft_type, player)`

Displays a crafting guide to the player for the given `craft_type`

![A typical crafting guide formspec](screenshot-guide.png)  
A typical crafting guide formspec

Items with a "G" superimposed over them represent item *groups*. Simplecrafting_lib will automatically select an item belonging to a group to provide the representative image for that group, but this can be overridden by adding entries to the simplecrafting_lib.guide.groups table, like so:

    simplecrafting_lib.guide.groups["wood"] = "default:wood"

## `simplecrafting_lib.set_crafting_guide_def(craft_type, guide_def)`

Defines some parameters regarding how the formspec of the guide for a given craft_type is displayed. `guide_def` is a table with the following optional parameters:

	{
		output_width = 10,
		output_height = 6,
		recipes_per_page = 4,
		append_to_formspec = string,
		is_recipe_included = function(recipe, player_name),
	}

* `output_width` and `output_height` define the dimensions of the grid of output buttons to click on.
* `recipes_per_page` defines how many rows are dedicated to displaying recipes at the bottom.
* `append to formspec` is a string that gets appended to the guide's formspec, and can be used to set colors and background images or add other decorative elements.
* `is_recipe_included` is a function that takes a recipe and returns true to include this recipe in the guide. If it is not defined then all recipes are included
	
## `simplecrafting_lib.set_description(craft_type, description)`

A crafting type can be given a human-readable text label by adding a `description` string to the `craft_type`'s info. This label is used by the template crafting formspecs.

## `simplecrafting_lib.get_description(craft_type)`

Returns the description set by the previous function.

## `simplecrafting_lib.generate_table_functions(craft_type, table_def)`

A convenience function that generates a table of functions that can be used directly with register node to create a basic crafting table.

`table_def` is a table of various options for the node's behaviour. They include:

	{
		show_guides = true,
		alphabetize_items = false,
		description = "String",
		hopper_node_name = "mod:node",
		enable_pipeworks = true,
		protect_inventory = true,
		append_to_formspec = "string",
	}

* If `hopper_node_name` is defined and the `[hopper]` mod is installed, a set of hopper inputs and outputs will be registered for the node that's named. If `enable_pipeworks` is **true** then the crafting table will behave like a chest does with the `[pipeworks]` mod.  **Note:** if you enable pipeworks, remember to also set `groups = {tubedevice = 1, tubedevice_receiver = 1}` on your crafting table's node.
* `protect_inventory` causes the inventories to respect player protection.
* `append to formspec` is a string that gets appended to the inventory formspec, and can be used to set colors and background images or add other decorative elements.

The returned table of functions contains:

		allow_metadata_inventory_move
		allow_metadata_inventory_put
		allow_metadata_inventory_take
		can_dig
		on_construct
		on_metadata_inventory_move
		on_metadata_inventory_put
		on_metadata_inventory_take
		on_receive_fields

And, if `enable_pipeworks` is **true**, also the function `tube`.

Add these functions to a node definition and it will produce a node that allows crafting of the specified type to be done via a standardized formspec.

![An example of the crafting interface that a table template can produce.](screenshot.png)

An example of the crafting interface that a table template can produce.

## `simplecrafting_lib.generate_autocraft_functions(craft_type, autocraft_def)`

A convenience function that generates a table of functions that can be used directly with register node to create an auto-crafting node. Place raw materials in the leftmost inventory grid, select an output from the array of buttons, and then the auto-crafting system will start producing output into the upper inventory grid. If the recipe has a `cooktime` parameter this will determine the number of seconds each crafting event will take, otherwise the crafting time equals the number of input items the recipe requires.

`autocraft_def` is a table of various options for the node's behaviour. They include:

	{
		show_guides = true,
		alphabetize_items = true,
		description = "String",
		hopper_node_name = "mod:node",
		enable_pipeworks = true,
		protect_inventory = true,
		crafting_time_multiplier = function(pos, recipe),
		active_node = "mod:node",
		lock_in_mode = "count" | "endless",
		append_to_formspec = "string",
	}

* If `hopper_node_name` is defined and the `[hopper]` mod is installed, a set of hopper inputs and outputs will be registered for the node that's named.
* If `enable_pipeworks` is **true** then the autocrafter will interact with the `[pipeworks]` mod like a chest (input items go to the input inventory). **Note:** if you enable pipeworks, remember to also set `groups = {tubedevice = 1, tubedevice_receiver = 1}` on your autocrafter's node.
* `protect_inventory` causes the inventories to respect player protection.
* `active_node` is the name of a node that the autocrafter will swap into its position when it's actively crafting. When it stops crafting it'll swap back to the node that it was initially placed as. **Note:** it is important to add the same autocraft functions to both the "inactive" and "active" nodes.
* `lock_in_mode` can be used to permanently lock the autocrafter in either **"count"** mode or **"endless"** mode. If this is not specified the user will have a button that allows them to change between the two nodes.
* `append to formspec` is a string that gets appended to the inventory formspec, and can be used to set colors and background images or add other decorative elements.

The returned table of functions contains:

		allow_metadata_inventory_move
		allow_metadata_inventory_put
		allow_metadata_inventory_take
		can_dig
		on_construct
		on_metadata_inventory_move
		on_metadata_inventory_put
		on_metadata_inventory_take
		on_receive_fields
		on_timer

And, if `enable_pipeworks` is **true**, also the function `tube`.
		
Add these functions to a node definition and it will produce a node that allows crafting of the specified type to be done via a standardized formspec.

![An example of the crafting interface that an autocrafting template can produce.](screenshot-autocraft.png)

An example of the crafting interface that an autocrafting template can produce.

## `simplecrafting_lib.generate_multifurnace_functions(craft_type, fuel_type, multifurnace_def)`

A convenience function that generates a table of functions that can be used directly with register node to create a furnace-like node. Unlike a default furnace a multifurnace is able to support recipes that take multiple inputs and produce multiple outputs. It also has a fuel inventory that can hold multiple different fuel items.

`multifurnace_def` is a table of various options for the node's behaviour. They include:

	{
		show_guides = true,
		alphabetize_items = true,
		description = "String",
		hopper_node_name = "mod:node",
		enable_pipeworks = true,
		protect_inventory = true,
		crafting_time_multiplier = function(pos, recipe),
		active_node = "mod:node",
		lock_in_mode = "count" | "endless",
		append_to_formspec = "string",
	}

* If `hopper_node_name` is defined and the `[hopper]` mod is installed, a furnace-like set of hopper inputs and outputs will be registered for the node that's named.
* If `enable_pipeworks` is **true** then the multifurnace will behave like a furnace does with the `[pipeworks]` mod. **Note:** if you enable pipeworks, remember to also set `groups = {tubedevice = 1, tubedevice_receiver = 1}` on your multifurnace's node.
* `protect_inventory` causes the inventories to respect player protection.
* `active_node` is the name of a node that the autocrafter will swap into its position when it's actively crafting. When it stops crafting it'll swap back to the node that it was initially placed as. **Note:** it is important to add the same autocraft functions to both the "inactive" and "active" nodes.
* `lock_in_mode` can be used to permanently lock the autocrafter in either **"count"** mode or **"endless"** mode. If this is not specified the user will have a button that allows them to change between the two nodes.
* `append to formspec` is a string that gets appended to the inventory formspec, and can be used to set colors and background images or add other decorative elements.

The returned table of functions contains:

		allow_metadata_inventory_move
		allow_metadata_inventory_put
		allow_metadata_inventory_take
		can_dig
		on_construct
		on_metadata_inventory_move
		on_metadata_inventory_put
		on_metadata_inventory_take
		on_receive_fields
		on_timer

And, if `enable_pipeworks` is **true**, also the function `tube`.
		
Add these functions to a node definition and it will produce a node that allows crafting of the specified type to be done via a standardized formspec.

![An example of the crafting interface that a multifurnace template can produce.](screenshot-multifurnace.png)

An example of the crafting interface that a multifurnace template can produce.

## `simplecrafting_lib.register_crafting_guide_item(item_name, craft_type, guide_item_def)`

Registers a basic crafting guide item. `guide_item_def` has many options.

	{
		description = "string",
		inventory_image = "image.png",
		guide_color = "#000000",
		wield_image = "image.png",
		groups = {book = 1},
		stack_max = 1,
		wield_scale = nil,
		copy_item_to_book = "mod:item",
	}

* `description` is a string description the item will get. Defaults to "<description of craft type> Guide" (method of setting this description is given under the show_crafting_guide documentation above, if not set will simply use the craft_type string directly)
* `inventory_image` is the inventory image to be used with this item. Defaults to the book texture included with simplecrafting_lib
* `guide_color` is a ColorString. If defined, the inventory image will be tinted with this color.
* `wield_image` is the image to be used when wielding this item. Defaults to inventory image.
* `wield_scale` is the scale of wield_image, defaults to **nil** (same as standard craftitem def)
* `groups` is a table of the groups this item will belong to. Defaults to **{book = 1}**
* `stack_max` is the maximum stack size. Defaults to 1.
* `copy_item_to_book` is an item name string (eg, **"workshops:smelter"**). If the `[default]` mod is installed a recipe will be generated that combines a **"default:book"** with `copy_item_to_book` and returns this guide and `copy_item_to_book`. In this manner the player can only get a handy portable reference guide if they are already in possession of the thing that the guide is used with. If `copy_item_to_book` is not defined then no crafting recipe is generated for this guide.
	
# Lower-level functions

These functions can be used for more sophisticated and special-purpose crafting mechanics.

## `simplecrafting_lib.get_crafting_info(craft_type)`

Returns a table with the members "recipe", "recipe_by_out" and "recipe_by_in" that contains all registered recipes for the craft type.
Using this method will ensure that the craft_type's recipe tables will exist even if nothing's been registered for them yet.

## `simplecrafting_lib.is_fuel(craft_type, item_name)`

Returns the fuel definition for the item if it is fuel, **nil** otherwise.

Group names are allowed in fuel definitions. If there is not an item specific recipe for the ``item_name`` provided here then it will take the definition of the longest burning group it belongs to.

## `simplecrafting_lib.get_fuels(craft_type, item_list, grade_definition)`

Returns a list of all fuel recipes whose ingredients can be satisfied by the item_list

Optional grade definition parameter of the form {min = minimum grade, max = maximum grade} to filter by grade

## `simplecrafting_lib.get_craftable_recipes(craft_type, item_list)`

Returns a list of all recipes whose ingredients can be satisfied by the item_list (as returned by InvRef:get_list(list_name))

## `simplecrafting_lib.get_craftable_items(craft_type, item_list, max_craftable, alphabetize)`

Returns a list of all the possible item stacks that could be crafted from the provided item list

if `max_craftable` is **true** the returned stacks will have as many items in them as possible to craft, otherwise the returned stacks will have only the minimum output

if `alphabetize` is **true** then the items will be sorted alphabetically by description, otherwise the items will be left in default order

## `simplecrafting_lib.is_possible_input(craft_type, item_name)`

Returns **true** if the item name is an input for at least one recipe belonging to the given craft type

## `simplecrafting_lib.is_possible_output(craft_type, item_name)`

Returns **true** if the item is a possible output for at least one recipe belonging to the given craft type

## `simplecrafting_lib.count_list_add(count_list1, count_list2)`

Adds two count lists together, returns a new count list with the sum of the parameters' contents

Can be used for example to combine a recipe's "output" and "returns" to get a complete list of the recipe's products

## `simplecrafting_lib.add_items(inv, listname, count_list)`

Add the items in `count_list` to the inventory. Returns a count list containing the items that couldn't be added.

## `simplecrafting_lib.add_items_if_room(inv, listname, count_list)`

Attempts to add the items in count_list to the inventory. If it succeeds, returns **true**.

If it fails, the inventory is not modified and returns **false** (this method is an atomic operation).

## `simplecrafting_lib.room_for_items(inv, listname, count_list)`

Returns **true** if there's room in the inventory to add all of the items in `count_list`, **false** otherwise.

## `simplecrafting_lib.remove_items(inv, listname, count_list)`

Removes the items in the count_list (formatted as per recipe standards) from the inventory. Returns true on success, false on failure.

Does not affect the inventory on failure (this method is an atomic operation)

## `simplecrafting_lib.drop_items(pos, count_list)`

Drops the contents of a `count_list` at the given location in the world

## `simplecrafting_lib.get_crafting_result(crafting_type, item_list, request_stack)`

Returns a recipe with the inputs and outputs multiplied to match the requested quantity of ouput items in the crafted stack.

Note that the output could actually be larger than `request_stack` if an exactly matching recipe can't be found.

returns **nil** if crafting is impossible with the given source inventory

# Automatic Disintermediation

## `simplecrafting_lib.set_disintermediation_cycles(craft_type, cycle_count)`

This is a somewhat more advanced function. Setting a crafting type's disintermediation cycle count greater than zero will cause the recipes for that type to be searched for recipes that produce outputs that are inputs for other recipes within that type. New recipes will then be generated to allow the player to skip those intermediate steps. For example, if there was a recipe A + B => C and a recipe C + D => E within a crafting type, disintermediation would automatically generate the recipe A + B + D => E for the player. The original recipes are left intact, this only adds new recipes.

The following illustrates a concrete example from the `[mesecons]` modpack:

![Example of a disintermediation recipe](screenshot-disintermediation.png)

The first two recipes are registered explicitly by `[mesecons]`. Silicon is produced by combining sand and steel, and solar panels are produced from silicon. If these two recipes are registered under the same `craft_type`, then disintermediation will automatically add the bottom recipe to the `craft_type` as well. This allows a solar panel to be produced directly from sand and steel. The "shortcut" still requires the same amount of raw materials, it just saves a pointless extra step.

### Potential problems arising from `cycle_count` > 1

The disintermediation process can be repeated up to `cycle_count` times, since each run can provide additional "shortcuts" based on the recipes that were added in the previous run. Normally the recipe set should converge on an optimal state where further disintermediation cycles won't add any new recipes.

Note however that it is possible to have a set of recipes where repeatedly running disintermediation will result in an endless exponential growth of new recipes. This usually happens when there's some recipe that "recycles" an output back into its original inputs. This is why it's a good idea to keep the cycle_count low, possibly no more than 1 or 2. If runaway recipe growth like this occurs then simplecrafting_lib will log an error indicating what craft_type got into that state. You'll have to examine the resulting craft_type's guide and figure out where the source of the problem is based on which outputs are building up an unreasonable number of recipes.

There are two ways that one can "fine-tune" the disintermediation process to prevent runaways like this. Recipes can have the flags `do_not_disintermediate` or `do_not_use_for_disintermediation` added to them to break a loop like this. The two flags can be used to attack the problem from two different sides; `do_not_disintermediate` prevents disintermediation from trying to find replacement recipes for that recipe's inputs, and `do_not_use_for_disintermediation` prevents disintermediation from using that recipe *as* a replacement for some other recipe's inputs.

As a concrete example the `[digtron]` mod uses a "digtron_core" craft item as an intermediate in a lot of its recipes, and allows nodes crafted from them to be broken back down again to recover digtron cores. When disintermediation is run on a craft_type containing digtron's recipes this causes a very rapid exponential growth in possible recipes. However, adding the following to the legacy recipe importer:

	simplecrafting_lib.register_recipe_import_filter(function(legacy_method, recipe)
	...
		if recipe.output["digtron:digtron_core"] and not recipe.input["default:mese_crystal_fragment"] then
			recipe.do_not_use_for_disintermediation = true
		end
	...
	end

results in only the original make-a-digtron-core-from-scratch being left as an option for disintermediation.