1use crate::item::{self, ItemStack};
4use crate::block;
5
6
7#[derive(Debug, Default)]
10pub struct CraftTracker {
11 current_recipe: Option<(usize, ItemStack)>,
13}
14
15impl CraftTracker {
16
17 pub fn update(&mut self, grid: &[ItemStack; 9]) {
19
20 self.current_recipe = None;
21
22 if grid.iter().copied().all(ItemStack::is_empty) {
24 return;
25 }
26
27 for (recipe_index, recipe) in RECIPES.iter().enumerate() {
28
29 let item = match recipe {
30 Recipe::Shaped(shaped) => shaped.check(grid),
31 Recipe::Shapeless(shapeless) => shapeless.check(grid),
32 };
33
34 if let Some(item) = item {
35 self.current_recipe = Some((recipe_index, item));
36 break;
37 }
38
39 }
40
41 }
42
43 pub fn consume(&self, grid: &mut [ItemStack; 9]) {
48
49 if self.current_recipe.is_none() {
50 return;
51 }
52
53 for stack in grid.iter_mut() {
56 if stack.is_empty() || stack.size == 1 {
57 *stack = ItemStack::EMPTY;
58 } else {
59 *stack = stack.with_size(stack.size - 1);
60 }
61 }
62
63 }
64
65 pub fn recipe(&self) -> Option<ItemStack> {
67 self.current_recipe.map(|(_, item)| item)
68 }
69
70}
71
72
73macro_rules! const_stacks {
76 ( $( $name:ident = $value:expr; )* ) => {
77 $( const $name: ItemStack = ItemStack { id: $value as u16, size: 1, damage: 0 }; )*
78 };
79}
80
81const EMPTY: ItemStack = ItemStack::EMPTY;
82const PAPER_3: ItemStack = ItemStack::new_sized(item::PAPER, 0, 3);
83const FENCE_2: ItemStack = ItemStack::new_block_sized(block::FENCE, 0, 2);
84const STONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 0, 3);
85const SANDSTONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 1, 3);
86const WOOD_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 2, 3);
87const COBBLESTONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 3, 3);
88const LADDER_2: ItemStack = ItemStack::new_block_sized(block::LADDER, 0, 2);
89const TRAPDOOR_2: ItemStack = ItemStack::new_block_sized(block::TRAPDOOR, 0, 2);
90const WOOD_4: ItemStack = ItemStack::new_block_sized(block::WOOD, 0, 4);
91const STICK_4: ItemStack = ItemStack::new_sized(item::STICK, 0, 4);
92const TORCH_4: ItemStack = ItemStack::new_block_sized(block::TORCH, 0, 4);
93const CHARCOAL: ItemStack = ItemStack::new_single(item::COAL, 1);
94const BOWL_4: ItemStack = ItemStack::new_sized(item::BOWL, 0, 4);
95const RAIL_16: ItemStack = ItemStack::new_block_sized(block::RAIL, 0, 16);
96const POWERED_RAIL_6: ItemStack = ItemStack::new_block_sized(block::POWERED_RAIL, 0, 6);
97const DETECTOR_RAIL_6: ItemStack = ItemStack::new_block_sized(block::DETECTOR_RAIL, 0, 6);
98const WOOD_STAIR_4: ItemStack = ItemStack::new_block_sized(block::WOOD_STAIR, 0, 4);
99const COBBLESTONE_STAIR_4: ItemStack = ItemStack::new_block_sized(block::COBBLESTONE_STAIR, 0, 4);
100const ARROW_4: ItemStack = ItemStack::new_sized(item::ARROW, 0, 4);
101const LAPIS: ItemStack = ItemStack::new_single(item::DYE, 4);
102const COOKIE_8: ItemStack = ItemStack::new_sized(item::COOKIE, 0, 8);
103const COCOA: ItemStack = ItemStack::new_single(item::DYE, 3);
104const YELLOW_DYE_2: ItemStack = ItemStack::new_sized(item::DYE, 11, 2);
105const RED_DYE_2: ItemStack = ItemStack::new_sized(item::DYE, 1, 2);
106const BONE_MEAL_2: ItemStack = ItemStack::new_sized(item::DYE, 15, 3);
107
108const_stacks! {
109 SUGAR_CANES = item::SUGAR_CANES;
110 PAPER = item::PAPER;
111 BOOK = item::BOOK;
112 STICK = item::STICK;
113 DIAMOND = item::DIAMOND;
114 WOOD = block::WOOD;
115 JUKEBOX = block::JUKEBOX;
116 REDSTONE = item::REDSTONE;
117 NOTE_BLOCK = block::NOTE_BLOCK;
118 BOOKSHELF = block::BOOKSHELF;
119 SNOWBALL = item::SNOWBALL;
120 SNOW_BLOCK = block::SNOW_BLOCK;
121 CLAY = item::CLAY;
122 CLAY_BLOCK = block::CLAY;
123 BRICK = item::BRICK;
124 BRICK_BLOCK = block::BRICK;
125 GLOWSTONE_DUST = item::GLOWSTONE_DUST;
126 GLOWSTONE = block::GLOWSTONE;
127 STRING = item::STRING;
128 WOOL = block::WOOL;
129 GUNPOWDER = item::GUNPOWDER;
130 SAND = block::SAND;
131 TNT = block::TNT;
132 BED = item::BED;
133 STONE = block::STONE;
134 SANDSTONE = block::SANDSTONE;
135 COBBLE = block::COBBLESTONE;
136 WOOD_DOOR = item::WOOD_DOOR;
137 IRON_DOOR = item::IRON_DOOR;
138 IRON_INGOT = item::IRON_INGOT;
139 SIGN = item::SIGN;
140 SUGAR = item::SUGAR;
141 MILK_BUCKET = item::MILK_BUCKET;
142 WHEAT = item::WHEAT;
143 EGG = item::EGG;
144 CAKE = item::CAKE;
145 LOG = block::LOG;
146 COAL = item::COAL;
147 GOLD_INGOT = item::GOLD_INGOT;
148 STONE_PRESSURE_PLATE = block::STONE_PRESSURE_PLATE;
149 MINECART = item::MINECART;
150 PUMPKIN = block::PUMPKIN;
151 TORCH = block::TORCH;
152 PUMPKIN_LIT = block::PUMPKIN_LIT;
153 CHEST_MINECART = item::CHEST_MINECART;
154 FURNACE_MINECART = item::FURNACE_MINECART;
155 CHEST = block::CHEST;
156 FURNACE = block::FURNACE;
157 BOAT = item::BOAT;
158 BUCKET = item::BUCKET;
159 FLINT_AND_STEEL = item::FLINT_AND_STEEL;
160 FLINT = item::FLINT;
161 BREAD = item::BREAD;
162 FISHING_ROD = item::FISHING_ROD;
163 PAINTING = item::PAINTING;
164 APPLE = item::APPLE;
165 GOLD_APPLE = item::GOLD_APPLE;
166 LEVER = block::LEVER;
167 REDSTONE_TORCH = block::REDSTONE_TORCH_LIT;
168 REPEATER = item::REPEATER;
169 CLOCK = item::CLOCK;
170 COMPASS = item::COMPASS;
171 MAP = item::MAP;
172 BUTTON = block::BUTTON;
173 WOOD_PRESSURE_PLATE = block::WOOD_PRESSURE_PLATE;
174 DISPENSER = block::DISPENSER;
175 BOW = item::BOW;
176 FEATHER = item::FEATHER;
177 PISTON = block::PISTON;
178 STICKY_PISTON = block::STICKY_PISTON;
179 SLIMEBALL = item::SLIMEBALL;
180 SHEARS = item::SHEARS;
181 BOWL = item::BOWL;
182 MUSHROOM_STEW = item::MUSHROOM_STEW;
183 BROWN_MUSHROOM = block::BROWN_MUSHROOM;
184 RED_MUSHROOM = block::RED_MUSHROOM;
185 CRAFTING_TABLE = block::CRAFTING_TABLE;
186 LEATHER = item::LEATHER;
187 DANDELION = block::DANDELION;
188 POPPY = block::POPPY;
189 BONE = item::BONE;
190}
191
192macro_rules! tool {
193 ( pickaxe $result:expr, $mat:expr ) => {
194 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, EMPTY, STICK, EMPTY, EMPTY, STICK, EMPTY], 3)
195 };
196 ( axe $result:expr, $mat:expr ) => {
197 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK, $mat, STICK, EMPTY], 2)
198 };
199 ( shovel $result:expr, $mat:expr ) => {
200 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, STICK, STICK], 1)
201 };
202 ( hoe $result:expr, $mat:expr ) => {
203 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK, EMPTY, STICK, EMPTY], 2)
204 };
205 ( sword $result:expr, $mat:expr ) => {
206 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK], 1)
207 };
208}
209
210macro_rules! armor {
211 ( helmet $result:expr, $mat:expr ) => {
212 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, $mat, EMPTY, $mat], 3)
213 };
214 ( chestplate $result:expr, $mat:expr ) => {
215 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, EMPTY, $mat, $mat, $mat, $mat, $mat, $mat, $mat], 3)
216 };
217 ( leggings $result:expr, $mat:expr ) => {
218 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, $mat, EMPTY, $mat, $mat, EMPTY, $mat], 3)
219 };
220 ( boots $result:expr, $mat:expr ) => {
221 Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, EMPTY, $mat, $mat, EMPTY, $mat], 3)
222 };
223}
224
225macro_rules! ore_block {
226 ( construct $block:expr, $ore:expr ) => {
227 Recipe::new_shaped(ItemStack::new_block($block, 0), &[$ore, $ore, $ore, $ore, $ore, $ore, $ore, $ore, $ore], 3)
228 };
229 ( destruct $block:expr, $ore:expr ) => {
230 Recipe::new_shaped($ore.with_size(9), &[ItemStack::new_block($block, 0)], 1)
231 };
232}
233
234macro_rules! dye_mix {
235 ( $meta:literal * $count:literal, [ $( $pattern_meta:literal ),+ ] ) => {
236 Recipe::new_shapeless(ItemStack::new_sized(item::DYE, $meta, $count), &[ $( ItemStack::new_single(item::DYE, $pattern_meta) ),+ ])
237 };
238}
239
240const RECIPES: &'static [Recipe] = &[
241 Recipe::new_shaped(PAPER_3, &[SUGAR_CANES, SUGAR_CANES, SUGAR_CANES], 3),
242 Recipe::new_shaped(BOOK, &[PAPER, PAPER, PAPER], 1),
243 Recipe::new_shaped(FENCE_2, &[STICK, STICK, STICK, STICK, STICK, STICK], 3),
244 Recipe::new_shaped(JUKEBOX, &[WOOD, WOOD, WOOD, WOOD, DIAMOND, WOOD, WOOD, WOOD, WOOD], 3),
245 Recipe::new_shaped(NOTE_BLOCK, &[WOOD, WOOD, WOOD, WOOD, REDSTONE, WOOD, WOOD, WOOD, WOOD], 3),
246 Recipe::new_shaped(BOOKSHELF, &[WOOD, WOOD, WOOD, BOOK, BOOK, BOOK, WOOD, WOOD, WOOD], 3),
247 Recipe::new_shaped(SNOW_BLOCK, &[SNOWBALL, SNOWBALL, SNOWBALL, SNOWBALL], 2),
248 Recipe::new_shaped(CLAY_BLOCK, &[CLAY, CLAY, CLAY, CLAY], 2),
249 Recipe::new_shaped(BRICK_BLOCK, &[BRICK, BRICK, BRICK, BRICK], 2),
250 Recipe::new_shaped(GLOWSTONE, &[GLOWSTONE_DUST, GLOWSTONE_DUST, GLOWSTONE_DUST, GLOWSTONE_DUST], 2),
251 Recipe::new_shaped(WOOL, &[STRING, STRING, STRING, STRING], 2),
252 Recipe::new_shaped(TNT, &[GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER], 3),
253 Recipe::new_shaped(STONE_SLAB_3, &[STONE, STONE, STONE], 3),
254 Recipe::new_shaped(SANDSTONE_SLAB_3, &[SANDSTONE, SANDSTONE, SANDSTONE], 3),
255 Recipe::new_shaped(WOOD_SLAB_3, &[WOOD, WOOD, WOOD], 3),
256 Recipe::new_shaped(COBBLESTONE_SLAB_3, &[COBBLE, COBBLE, COBBLE], 3),
257 Recipe::new_shaped(LADDER_2, &[STICK, EMPTY, STICK, STICK, STICK, STICK, STICK, EMPTY, STICK], 3),
258 Recipe::new_shaped(WOOD_DOOR, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD], 2),
259 Recipe::new_shaped(TRAPDOOR_2, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD], 3),
260 Recipe::new_shaped(IRON_DOOR, &[IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT], 2),
261 Recipe::new_shaped(SIGN, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD, EMPTY, STICK, EMPTY], 3),
262 Recipe::new_shaped(CAKE, &[MILK_BUCKET, MILK_BUCKET, MILK_BUCKET, SUGAR, EGG, SUGAR, WHEAT, WHEAT, WHEAT], 3),
263 Recipe::new_shaped(SUGAR, &[SUGAR_CANES], 1),
264 Recipe::new_shaped(WOOD_4, &[LOG], 1),
265 Recipe::new_shaped(STICK_4, &[WOOD, WOOD], 1),
266 Recipe::new_shaped(TORCH_4, &[COAL, STICK], 1),
267 Recipe::new_shaped(TORCH_4, &[CHARCOAL, STICK], 1),
268 Recipe::new_shaped(BOWL_4, &[WOOD, EMPTY, WOOD, EMPTY, WOOD, EMPTY], 3),
269 Recipe::new_shaped(RAIL_16, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, STICK, IRON_INGOT, IRON_INGOT, EMPTY, IRON_INGOT], 3),
270 Recipe::new_shaped(POWERED_RAIL_6, &[GOLD_INGOT, EMPTY, GOLD_INGOT, GOLD_INGOT, STICK, GOLD_INGOT, GOLD_INGOT, REDSTONE, GOLD_INGOT], 3),
271 Recipe::new_shaped(DETECTOR_RAIL_6, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, STONE_PRESSURE_PLATE, IRON_INGOT, IRON_INGOT, REDSTONE, IRON_INGOT], 3),
272 Recipe::new_shaped(MINECART, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT], 3),
273 Recipe::new_shaped(PUMPKIN_LIT, &[PUMPKIN, TORCH], 1),
274 Recipe::new_shaped(CHEST_MINECART, &[CHEST, MINECART], 1),
275 Recipe::new_shaped(FURNACE_MINECART, &[FURNACE, MINECART], 1),
276 Recipe::new_shaped(BOAT, &[WOOD, EMPTY, WOOD, WOOD, WOOD, WOOD], 3),
277 Recipe::new_shaped(BUCKET, &[IRON_INGOT, EMPTY, IRON_INGOT, EMPTY, IRON_INGOT, EMPTY], 3),
278 Recipe::new_shaped(FLINT_AND_STEEL, &[IRON_INGOT, EMPTY, EMPTY, FLINT], 2),
279 Recipe::new_shaped(BREAD, &[WHEAT, WHEAT, WHEAT], 3),
280 Recipe::new_shaped(WOOD_STAIR_4, &[WOOD, EMPTY, EMPTY, WOOD, WOOD, EMPTY, WOOD, WOOD, WOOD], 3),
281 Recipe::new_shaped(FISHING_ROD, &[EMPTY, EMPTY, STICK, EMPTY, STICK, STRING, STICK, EMPTY, STRING], 3),
282 Recipe::new_shaped(COBBLESTONE_STAIR_4, &[COBBLE, EMPTY, EMPTY, COBBLE, COBBLE, EMPTY, COBBLE, COBBLE, COBBLE], 3),
283 Recipe::new_shaped(PAINTING, &[STICK, STICK, STICK, STICK, WOOL, STICK, STICK, STICK, STICK], 3),
284 Recipe::new_shaped(GOLD_APPLE, &[GOLD_INGOT, GOLD_INGOT, GOLD_INGOT, GOLD_INGOT, APPLE, GOLD_INGOT, GOLD_INGOT, GOLD_INGOT], 3),
285 Recipe::new_shaped(LEVER, &[STICK, COBBLE], 1),
286 Recipe::new_shaped(REDSTONE_TORCH, &[REDSTONE, STICK], 1),
287 Recipe::new_shaped(REPEATER, &[REDSTONE_TORCH, REDSTONE, REDSTONE_TORCH, STONE, STONE, STONE], 3),
288 Recipe::new_shaped(CLOCK, &[EMPTY, GOLD_INGOT, EMPTY, GOLD_INGOT, REDSTONE, GOLD_INGOT, EMPTY, GOLD_INGOT, EMPTY], 3),
289 Recipe::new_shaped(COMPASS, &[EMPTY, IRON_INGOT, EMPTY, IRON_INGOT, REDSTONE, IRON_INGOT, EMPTY, IRON_INGOT, EMPTY], 3),
290 Recipe::new_shaped(MAP, &[PAPER, PAPER, PAPER, PAPER, COMPASS, PAPER, PAPER, PAPER, PAPER], 3),
291 Recipe::new_shaped(BUTTON, &[STONE, STONE], 1),
292 Recipe::new_shaped(STONE_PRESSURE_PLATE, &[STONE, STONE], 2),
293 Recipe::new_shaped(WOOD_PRESSURE_PLATE, &[WOOD, WOOD], 2),
294 Recipe::new_shaped(DISPENSER, &[COBBLE, COBBLE, COBBLE, COBBLE, BOW, COBBLE, COBBLE, REDSTONE, COBBLE], 3),
295 Recipe::new_shaped(PISTON, &[WOOD, WOOD, WOOD, COBBLE, IRON_INGOT, COBBLE, COBBLE, REDSTONE, COBBLE], 3),
296 Recipe::new_shaped(STICKY_PISTON, &[SLIMEBALL, PISTON], 1),
297 Recipe::new_shaped(BED, &[WOOL, WOOL, WOOL, WOOD, WOOD], 3),
298 Recipe::new_shaped(SHEARS, &[IRON_INGOT, EMPTY, EMPTY, IRON_INGOT], 2),
299 Recipe::new_shaped(BOW, &[EMPTY, STICK, STRING, STICK, EMPTY, STRING, EMPTY, STICK, STRING], 3),
300 Recipe::new_shaped(ARROW_4, &[FLINT, STICK, FEATHER], 1),
301 Recipe::new_shaped(MUSHROOM_STEW, &[RED_MUSHROOM, BROWN_MUSHROOM, BOWL], 1),
302 Recipe::new_shaped(MUSHROOM_STEW, &[BROWN_MUSHROOM, RED_MUSHROOM, BOWL], 1),
303 Recipe::new_shaped(COOKIE_8, &[WHEAT, COCOA, WHEAT], 3),
304 Recipe::new_shaped(CHEST, &[WOOD, WOOD, WOOD, WOOD, EMPTY, WOOD, WOOD, WOOD, WOOD], 3),
305 Recipe::new_shaped(FURNACE, &[COBBLE, COBBLE, COBBLE, COBBLE, EMPTY, COBBLE, COBBLE, COBBLE, COBBLE], 3),
306 Recipe::new_shaped(CRAFTING_TABLE, &[WOOD, WOOD, WOOD, WOOD], 2),
307 Recipe::new_shaped(SANDSTONE, &[SAND, SAND, SAND, SAND], 2),
308 tool!(pickaxe item::WOOD_PICKAXE, WOOD),
310 tool!(pickaxe item::STONE_PICKAXE, COBBLE),
311 tool!(pickaxe item::GOLD_PICKAXE, GOLD_INGOT),
312 tool!(pickaxe item::IRON_PICKAXE, IRON_INGOT),
313 tool!(pickaxe item::DIAMOND_PICKAXE, DIAMOND),
314 tool!(axe item::WOOD_AXE, WOOD),
315 tool!(axe item::STONE_AXE, COBBLE),
316 tool!(axe item::GOLD_AXE, GOLD_INGOT),
317 tool!(axe item::IRON_AXE, IRON_INGOT),
318 tool!(axe item::DIAMOND_AXE, DIAMOND),
319 tool!(shovel item::WOOD_SHOVEL, WOOD),
320 tool!(shovel item::STONE_SHOVEL, COBBLE),
321 tool!(shovel item::GOLD_SHOVEL, GOLD_INGOT),
322 tool!(shovel item::IRON_SHOVEL, IRON_INGOT),
323 tool!(shovel item::DIAMOND_SHOVEL, DIAMOND),
324 tool!(hoe item::WOOD_HOE, WOOD),
325 tool!(hoe item::STONE_HOE, COBBLE),
326 tool!(hoe item::GOLD_HOE, GOLD_INGOT),
327 tool!(hoe item::IRON_HOE, IRON_INGOT),
328 tool!(hoe item::DIAMOND_HOE, DIAMOND),
329 tool!(sword item::WOOD_SWORD, WOOD),
330 tool!(sword item::STONE_SWORD, COBBLE),
331 tool!(sword item::GOLD_SWORD, GOLD_INGOT),
332 tool!(sword item::IRON_SWORD, IRON_INGOT),
333 tool!(sword item::DIAMOND_SWORD, DIAMOND),
334 armor!(helmet item::LEATHER_HELMET, LEATHER),
336 armor!(helmet item::GOLD_HELMET, GOLD_INGOT),
337 armor!(helmet item::IRON_HELMET, IRON_INGOT),
338 armor!(helmet item::DIAMOND_HELMET, DIAMOND),
339 armor!(chestplate item::LEATHER_CHESTPLATE, LEATHER),
340 armor!(chestplate item::GOLD_CHESTPLATE, GOLD_INGOT),
341 armor!(chestplate item::IRON_CHESTPLATE, IRON_INGOT),
342 armor!(chestplate item::DIAMOND_CHESTPLATE, DIAMOND),
343 armor!(leggings item::LEATHER_LEGGINGS, LEATHER),
344 armor!(leggings item::GOLD_LEGGINGS, GOLD_INGOT),
345 armor!(leggings item::IRON_LEGGINGS, IRON_INGOT),
346 armor!(leggings item::DIAMOND_LEGGINGS, DIAMOND),
347 armor!(boots item::LEATHER_BOOTS, LEATHER),
348 armor!(boots item::GOLD_BOOTS, GOLD_INGOT),
349 armor!(boots item::IRON_BOOTS, IRON_INGOT),
350 armor!(boots item::DIAMOND_BOOTS, DIAMOND),
351 ore_block!(construct block::IRON_BLOCK, IRON_INGOT),
353 ore_block!(construct block::GOLD_BLOCK, GOLD_INGOT),
354 ore_block!(construct block::DIAMOND_BLOCK, DIAMOND),
355 ore_block!(construct block::LAPIS_BLOCK, LAPIS),
356 ore_block!(destruct block::IRON_BLOCK, IRON_INGOT),
357 ore_block!(destruct block::GOLD_BLOCK, GOLD_INGOT),
358 ore_block!(destruct block::DIAMOND_BLOCK, DIAMOND),
359 ore_block!(destruct block::LAPIS_BLOCK, LAPIS),
360 Recipe::new_shapeless(YELLOW_DYE_2, &[DANDELION]),
362 Recipe::new_shapeless(RED_DYE_2, &[POPPY]),
363 Recipe::new_shapeless(BONE_MEAL_2, &[BONE]),
364 dye_mix!(9 * 2, [1, 15]),
365 dye_mix!(14 * 2, [1, 11]),
366 dye_mix!(10 * 2, [2, 15]),
367 dye_mix!(8 * 2, [0, 15]),
368 dye_mix!(7 * 2, [8, 15]),
369 dye_mix!(7 * 3, [0, 15, 15]),
370 dye_mix!(12 * 2, [4, 15]),
371 dye_mix!(6 * 2, [4, 2]),
372 dye_mix!(5 * 2, [4, 1]),
373 dye_mix!(13 * 2, [5, 9]),
374 dye_mix!(13 * 3, [4, 1, 9]),
375 dye_mix!(13 * 4, [4, 1, 1, 15]),
376];
377
378
379enum Recipe {
383 Shaped(ShapedRecipe),
387 Shapeless(ShapelessRecipe),
390}
391
392struct ShapedRecipe {
393 result: ItemStack,
394 pattern: &'static [ItemStack],
395 width: u8,
396}
397
398struct ShapelessRecipe {
399 result: ItemStack,
400 pattern: &'static [ItemStack],
401}
402
403impl Recipe {
404
405 const fn new_shaped(result: ItemStack, pattern: &'static [ItemStack], width: u8) -> Self {
406 Self::Shaped(ShapedRecipe { result, pattern, width })
407 }
408
409 const fn new_shapeless(result: ItemStack, pattern: &'static [ItemStack]) -> Self {
410 Self::Shapeless(ShapelessRecipe { result, pattern })
411 }
412
413}
414
415impl ShapedRecipe {
416
417 fn check(&self, grid: &[ItemStack; 9]) -> Option<ItemStack> {
420
421 let recipe_width = self.width as usize;
424 let recipe_height = (self.pattern.len() + recipe_width - 1) / recipe_width;
425
426 if recipe_width > 3 || recipe_height > 3 {
429 return None;
430 }
431
432 for start_x in 0..=(3 - recipe_width) {
434 'out: for start_y in 0..=(3 - recipe_height) {
435
436 let mut normal_valid = true;
437 let mut flip_valid = true;
438
439 for dx in 0..3 {
440 for dy in 0..3 {
441 let stack = grid[dx + dy * 3];
442 if dx < start_x || dx >= start_x + recipe_width || dy < start_y || dy >= start_y + recipe_height {
443 if !stack.is_empty() {
445 continue 'out;
446 }
447 } else {
448
449 let pattern_x = dx - start_x;
451 let pattern_y = dy - start_y;
452 let flip_pattern_x = recipe_width - pattern_x - 1;
453
454 if normal_valid {
455 let normal_stack = self.pattern[pattern_x + pattern_y * recipe_width];
456 normal_valid =
457 (normal_stack.is_empty() && stack.is_empty()) ||
458 (!stack.is_empty() && (normal_stack.id, normal_stack.damage) == (stack.id, stack.damage));
459 }
460
461 if flip_valid {
462 let flip_stack = self.pattern[flip_pattern_x + pattern_y * recipe_width];
463 flip_valid =
464 (flip_stack.is_empty() && stack.is_empty()) ||
465 (!stack.is_empty() && (flip_stack.id, flip_stack.damage) == (stack.id, stack.damage));
466 }
467
468 if !normal_valid && !flip_valid {
469 continue 'out;
470 }
471
472 }
473 }
474 }
475
476 if normal_valid || flip_valid {
477 return Some(self.result);
478 }
479
480 }
481 }
482
483 None
484
485 }
486
487}
488
489impl ShapelessRecipe {
490
491 fn check(&self, inv: &[ItemStack; 9]) -> Option<ItemStack> {
493
494 if inv.len() < self.pattern.len() {
496 return None;
497 }
498
499 let mut pat_matched = 0u32;
500
501 'inv: for stack in inv.iter().copied() {
502 if !stack.is_empty() {
503 for (i, pat_stack) in self.pattern.iter().copied().enumerate() {
504 if pat_matched & (1 << i) == 0 {
505 if (pat_stack.id, pat_stack.damage) == (stack.id, stack.damage) {
506 pat_matched |= 1 << i;
507 continue 'inv;
508 }
509 }
510 }
511 return None;
513 }
514 }
515
516 if pat_matched != (1 << self.pattern.len()) - 1 {
518 None
519 } else {
520 Some(self.result)
521 }
522
523 }
524
525}