Skip to main content

steel_core/inventory/
recipe_manager.rs

1//! Recipe matching and crafting grid management.
2//!
3//! This module provides functions to match crafting grid contents against
4//! registered recipes and update the result slot accordingly.
5
6use steel_registry::{
7    REGISTRY,
8    item_stack::ItemStack,
9    recipe::{CraftingRecipe, PositionedCraftingInput},
10};
11
12use crate::inventory::container::CraftingContainer;
13
14use super::container::Container;
15
16/// Called when a slot changes in the crafting grid.
17/// Updates the result container with the matching recipe result.
18///
19/// # Arguments
20/// * `crafting` - The crafting container to check
21/// * `result` - The result container to update
22/// * `is_2x2` - Whether this is a 2x2 crafting grid (player inventory)
23pub fn slot_changed_crafting_grid<R: Container>(
24    crafting: &CraftingContainer,
25    result: &mut R,
26    is_2x2: bool,
27) {
28    let positioned = crafting.as_positioned_input();
29
30    let recipe = if is_2x2 {
31        REGISTRY.recipes.find_crafting_recipe_2x2(&positioned.input)
32    } else {
33        REGISTRY.recipes.find_crafting_recipe(&positioned.input)
34    };
35
36    let result_stack = match recipe {
37        Some(r) => r.assemble(),
38        None => ItemStack::empty(),
39    };
40
41    result.set_item(0, result_stack);
42}
43
44/// Finds a matching recipe for the given crafting container.
45///
46/// # Arguments
47/// * `crafting` - The crafting container to check
48/// * `is_2x2` - Whether this is a 2x2 crafting grid
49///
50/// # Returns
51/// The matching recipe, or None if no recipe matches.
52#[must_use]
53pub fn find_recipe(crafting: &CraftingContainer, is_2x2: bool) -> Option<CraftingRecipe> {
54    let positioned = crafting.as_positioned_input();
55
56    if is_2x2 {
57        REGISTRY.recipes.find_crafting_recipe_2x2(&positioned.input)
58    } else {
59        REGISTRY.recipes.find_crafting_recipe(&positioned.input)
60    }
61}
62
63/// Gets the remaining items (crafting remainders) for a recipe.
64///
65/// This queries the recipe for its remaining items, which may include
66/// items like empty buckets when using milk buckets in a recipe.
67///
68/// # Arguments
69/// * `crafting` - The crafting container
70/// * `is_2x2` - Whether this is a 2x2 crafting grid
71///
72/// # Returns
73/// A vector of remaining items for each slot in the positioned input,
74/// along with the positioned input for mapping back to grid slots.
75/// Returns None if no recipe matches.
76#[must_use]
77pub fn get_remaining_items(
78    crafting: &CraftingContainer,
79    is_2x2: bool,
80) -> Option<(Vec<ItemStack>, PositionedCraftingInput)> {
81    let positioned = crafting.as_positioned_input();
82
83    let recipe = if is_2x2 {
84        REGISTRY.recipes.find_crafting_recipe_2x2(&positioned.input)
85    } else {
86        REGISTRY.recipes.find_crafting_recipe(&positioned.input)
87    }?;
88
89    // Get remainders from the recipe using the positioned (trimmed) input
90    let remainders = recipe.get_remaining_items(&positioned.input);
91
92    Some((remainders, positioned))
93}