steel_registry/recipe/
registry.rs1use rustc_hash::FxHashMap;
4use steel_utils::Identifier;
5
6use super::crafting::{CraftingInput, CraftingRecipe, ShapedRecipe, ShapelessRecipe};
7
8pub struct RecipeRegistry {
10 recipes_by_id: Vec<&'static CraftingRecipe>,
12 recipes_by_key: FxHashMap<Identifier, usize>,
14 shaped_recipes: Vec<&'static ShapedRecipe>,
16 shapeless_recipes: Vec<&'static ShapelessRecipe>,
18 allows_registering: bool,
20}
21
22impl Default for RecipeRegistry {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl RecipeRegistry {
29 #[must_use]
31 pub fn new() -> Self {
32 Self {
33 recipes_by_id: Vec::new(),
34 recipes_by_key: FxHashMap::default(),
35 shaped_recipes: Vec::new(),
36 shapeless_recipes: Vec::new(),
37 allows_registering: true,
38 }
39 }
40
41 pub fn register_shaped(&mut self, recipe: &'static ShapedRecipe) {
43 assert!(
44 self.allows_registering,
45 "Cannot register recipes after the registry has been frozen"
46 );
47 let id = self.recipes_by_id.len();
48 self.recipes_by_key.insert(recipe.id.clone(), id);
49 self.recipes_by_id
50 .push(Box::leak(Box::new(CraftingRecipe::Shaped(recipe))));
51 self.shaped_recipes.push(recipe);
52 }
53
54 pub fn register_shapeless(&mut self, recipe: &'static ShapelessRecipe) {
56 assert!(
57 self.allows_registering,
58 "Cannot register recipes after the registry has been frozen"
59 );
60 let id = self.recipes_by_id.len();
61 self.recipes_by_key.insert(recipe.id.clone(), id);
62 self.recipes_by_id
63 .push(Box::leak(Box::new(CraftingRecipe::Shapeless(recipe))));
64 self.shapeless_recipes.push(recipe);
65 }
66
67 #[must_use]
70 pub fn find_crafting_recipe(&self, input: &CraftingInput) -> Option<CraftingRecipe> {
71 for recipe in &self.shaped_recipes {
73 if recipe.matches(input) {
74 return Some(CraftingRecipe::Shaped(recipe));
75 }
76 }
77
78 for recipe in &self.shapeless_recipes {
80 if recipe.matches(input) {
81 return Some(CraftingRecipe::Shapeless(recipe));
82 }
83 }
84
85 None
86 }
87
88 #[must_use]
91 pub fn find_crafting_recipe_2x2(&self, input: &CraftingInput) -> Option<CraftingRecipe> {
92 for recipe in &self.shaped_recipes {
94 if recipe.fits_in_2x2() && recipe.matches(input) {
95 return Some(CraftingRecipe::Shaped(recipe));
96 }
97 }
98
99 for recipe in &self.shapeless_recipes {
101 if recipe.fits_in_2x2() && recipe.matches(input) {
102 return Some(CraftingRecipe::Shapeless(recipe));
103 }
104 }
105
106 None
107 }
108
109 #[must_use]
111 pub fn get_shaped(&self, id: &Identifier) -> Option<&'static ShapedRecipe> {
112 self.shaped_recipes.iter().find(|r| &r.id == id).copied()
113 }
114
115 #[must_use]
117 pub fn get_shapeless(&self, id: &Identifier) -> Option<&'static ShapelessRecipe> {
118 self.shapeless_recipes.iter().find(|r| &r.id == id).copied()
119 }
120
121 #[must_use]
123 pub fn shaped_count(&self) -> usize {
124 self.shaped_recipes.len()
125 }
126
127 #[must_use]
129 pub fn shapeless_count(&self) -> usize {
130 self.shapeless_recipes.len()
131 }
132
133 pub fn iter_shaped(&self) -> impl Iterator<Item = &'static ShapedRecipe> + '_ {
135 self.shaped_recipes.iter().copied()
136 }
137
138 pub fn iter_shapeless(&self) -> impl Iterator<Item = &'static ShapelessRecipe> + '_ {
140 self.shapeless_recipes.iter().copied()
141 }
142}
143
144impl crate::RegistryExt for RecipeRegistry {
145 type Entry = CraftingRecipe;
146
147 fn freeze(&mut self) {
148 self.allows_registering = false;
149 }
150
151 fn by_id(&self, id: usize) -> Option<&'static CraftingRecipe> {
152 self.recipes_by_id.get(id).copied()
153 }
154
155 fn by_key(&self, key: &Identifier) -> Option<&'static CraftingRecipe> {
156 self.recipes_by_key
157 .get(key)
158 .and_then(|&id| self.recipes_by_id.get(id).copied())
159 }
160
161 fn id_from_key(&self, key: &Identifier) -> Option<usize> {
162 self.recipes_by_key.get(key).copied()
163 }
164
165 fn len(&self) -> usize {
166 self.recipes_by_id.len()
167 }
168
169 fn is_empty(&self) -> bool {
170 self.recipes_by_id.is_empty()
171 }
172}
173
174impl crate::RegistryEntry for CraftingRecipe {
175 fn key(&self) -> &Identifier {
176 self.id()
177 }
178
179 fn try_id(&self) -> Option<usize> {
180 use crate::RegistryExt;
181 crate::REGISTRY.recipes.id_from_key(self.id())
182 }
183}