Skip to main content

steel_registry/recipe/
properties.rs

1//! Passive recipe metadata and recipe-book categories.
2
3use rustc_hash::FxHashMap;
4use steel_utils::Identifier;
5
6use super::Ingredient;
7
8/// Recipe-book grouping used by crafting recipes.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum CraftingBookCategory {
11    Building,
12    Redstone,
13    Equipment,
14    Misc,
15}
16
17/// Recipe-book grouping used by cooking recipes.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum CookingBookCategory {
20    Food,
21    Blocks,
22    Misc,
23}
24
25/// Logical recipe-book category stored by recipe data.
26///
27/// Cooking categories are resolved against the operational recipe type when the
28/// registry freezes. For example, `Cooking(Food)` becomes `furnace_food` for a
29/// smelting recipe and `smoker_food` for a smoking recipe.
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum RecipeBookCategoryKind {
32    Crafting(CraftingBookCategory),
33    Cooking(CookingBookCategory),
34    Stonecutter,
35    Smithing,
36}
37
38/// Recipe-book metadata which is not part of matching or processing.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct RecipeBookProperties {
41    pub category: RecipeBookCategoryKind,
42    pub group: Option<String>,
43    pub show_notification: bool,
44}
45
46/// Precomputed ingredient placement information.
47#[derive(Debug, Clone)]
48pub struct PlacementInfo {
49    pub ingredients: Vec<Ingredient>,
50    /// Maps recipe slots to `ingredients`; `None` represents an empty slot.
51    pub slots_to_ingredient: Vec<Option<usize>>,
52}
53
54impl PlacementInfo {
55    #[must_use]
56    pub const fn not_placeable() -> Self {
57        Self {
58            ingredients: Vec::new(),
59            slots_to_ingredient: Vec::new(),
60        }
61    }
62
63    #[must_use]
64    pub fn from_ingredient(ingredient: Ingredient) -> Self {
65        if ingredient.is_empty() {
66            return Self::not_placeable();
67        }
68        Self {
69            ingredients: vec![ingredient],
70            slots_to_ingredient: vec![Some(0)],
71        }
72    }
73
74    #[must_use]
75    pub fn from_optional_ingredients(ingredients: &[Ingredient]) -> Self {
76        let mut present = Vec::with_capacity(ingredients.len());
77        let mut slots = Vec::with_capacity(ingredients.len());
78        for ingredient in ingredients {
79            if ingredient.is_empty() {
80                slots.push(None);
81            } else {
82                slots.push(Some(present.len()));
83                present.push(ingredient.clone());
84            }
85        }
86        Self {
87            ingredients: present,
88            slots_to_ingredient: slots,
89        }
90    }
91
92    #[must_use]
93    pub fn from_ingredients(ingredients: &[Ingredient]) -> Self {
94        if ingredients.iter().any(Ingredient::is_empty) {
95            return Self::not_placeable();
96        }
97        Self {
98            ingredients: ingredients.to_vec(),
99            slots_to_ingredient: (0..ingredients.len()).map(Some).collect(),
100        }
101    }
102
103    #[must_use]
104    pub const fn is_impossible_to_place(&self) -> bool {
105        self.slots_to_ingredient.is_empty()
106    }
107}
108
109/// Optional metadata common to recipe data implementations.
110#[derive(Debug, Clone)]
111pub struct RecipeProperties {
112    pub special: bool,
113    pub placement: Option<PlacementInfo>,
114    pub recipe_book: Option<RecipeBookProperties>,
115}
116
117impl RecipeProperties {
118    #[must_use]
119    pub const fn special() -> Self {
120        Self {
121            special: true,
122            placement: None,
123            recipe_book: None,
124        }
125    }
126}
127
128/// A registered protocol recipe-book category.
129#[derive(Debug)]
130pub struct RecipeBookCategory {
131    pub key: Identifier,
132}
133
134pub type RecipeBookCategoryRef = &'static RecipeBookCategory;
135
136/// Registry for protocol-visible recipe-book categories.
137pub struct RecipeBookCategoryRegistry {
138    categories: Vec<RecipeBookCategoryRef>,
139    by_key: FxHashMap<Identifier, usize>,
140    allows_registering: bool,
141}
142
143impl RecipeBookCategoryRegistry {
144    #[must_use]
145    pub fn new() -> Self {
146        Self {
147            categories: Vec::new(),
148            by_key: FxHashMap::default(),
149            allows_registering: true,
150        }
151    }
152}
153
154crate::impl_standard_methods!(
155    RecipeBookCategoryRegistry,
156    RecipeBookCategoryRef,
157    categories,
158    by_key,
159    allows_registering,
160    "Cannot register duplicate recipe book category key: {}"
161);
162crate::impl_registry!(
163    RecipeBookCategoryRegistry,
164    RecipeBookCategory,
165    categories,
166    by_key,
167    recipe_book_categories
168);
169
170/// Vanilla protocol recipe-book categories.
171pub mod vanilla_recipe_book_categories {
172    use super::{RecipeBookCategory, RecipeBookCategoryRegistry};
173    use steel_utils::Identifier;
174
175    macro_rules! category {
176        ($name:ident, $key:literal) => {
177            pub static $name: RecipeBookCategory = RecipeBookCategory {
178                key: Identifier::vanilla_static($key),
179            };
180        };
181    }
182
183    category!(CRAFTING_BUILDING_BLOCKS, "crafting_building_blocks");
184    category!(CRAFTING_REDSTONE, "crafting_redstone");
185    category!(CRAFTING_EQUIPMENT, "crafting_equipment");
186    category!(CRAFTING_MISC, "crafting_misc");
187    category!(FURNACE_FOOD, "furnace_food");
188    category!(FURNACE_BLOCKS, "furnace_blocks");
189    category!(FURNACE_MISC, "furnace_misc");
190    category!(BLAST_FURNACE_BLOCKS, "blast_furnace_blocks");
191    category!(BLAST_FURNACE_MISC, "blast_furnace_misc");
192    category!(SMOKER_FOOD, "smoker_food");
193    category!(STONECUTTER, "stonecutter");
194    category!(SMITHING, "smithing");
195    category!(CAMPFIRE, "campfire");
196
197    pub(crate) fn register(registry: &mut RecipeBookCategoryRegistry) {
198        registry.register(&CRAFTING_BUILDING_BLOCKS);
199        registry.register(&CRAFTING_REDSTONE);
200        registry.register(&CRAFTING_EQUIPMENT);
201        registry.register(&CRAFTING_MISC);
202        registry.register(&FURNACE_FOOD);
203        registry.register(&FURNACE_BLOCKS);
204        registry.register(&FURNACE_MISC);
205        registry.register(&BLAST_FURNACE_BLOCKS);
206        registry.register(&BLAST_FURNACE_MISC);
207        registry.register(&SMOKER_FOOD);
208        registry.register(&STONECUTTER);
209        registry.register(&SMITHING);
210        registry.register(&CAMPFIRE);
211    }
212}