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}