mc173/
craft.rs

1//! Item crafting management.
2
3use crate::item::{self, ItemStack};
4use crate::block;
5
6
7/// This structure keeps track of the current crafting recipe selected and allows lazy
8/// update of the crafting recipe. A crafting recipe is based on a 3x3 item grid.
9#[derive(Debug, Default)]
10pub struct CraftTracker {
11    /// The index and result item of the current selected recipe
12    current_recipe: Option<(usize, ItemStack)>,
13}
14
15impl CraftTracker {
16
17    /// Update this tracker to track a new 3x3 grid of items.
18    pub fn update(&mut self, grid: &[ItemStack; 9]) {
19
20        self.current_recipe = None;
21
22        // Do not search if all slots are empty.
23        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    /// If there is a selected recipe, consume the recipe items from the given inventory,
44    /// this inventory should be coherent with the one that selected this recipe through
45    /// the `update` method. You need to call the `update` method again in order to update
46    /// the tracker for the new inventory.
47    pub fn consume(&self, grid: &mut [ItemStack; 9]) {
48
49        if self.current_recipe.is_none() {
50            return;
51        }
52
53        // We just decrement all stack's size in the grid, because stack size is ignored
54        // in current patterns.
55        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    /// If a crafting recipe is currently selected, return the result item.
66    pub fn recipe(&self) -> Option<ItemStack> {
67        self.current_recipe.map(|(_, item)| item)
68    }
69
70}
71
72
73// Here are the stack definitions of crafting recipes.
74
75macro_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    // Tools...
309    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    // Armors...
335    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 blocks...
352    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    // Dyes...
361    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
379/// The recipe enumeration stores different types of recipes.
380/// 
381/// **Note that crafting recipes currently ignore the stack size in of patterns.**
382enum Recipe {
383    /// A shaped crafting recipe requires the items to be in a specific pattern, the 
384    /// pattern has a size and if smaller than 3x3 it can be moved everywhere in the 
385    /// table.
386    Shaped(ShapedRecipe),
387    /// A shapeless crafting just define a list of items that must be present in the
388    /// crafting grid, each stack must be present once.
389    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    /// Check if this shaped recipe can be crafted with the given inventory of the given 
418    /// size and items.
419    fn check(&self, grid: &[ItemStack; 9]) -> Option<ItemStack> {
420
421        // Compute recipe size based on pattern length and width.
422        // NOTE: We compute the height in which the pattern fit.
423        let recipe_width = self.width as usize;
424        let recipe_height = (self.pattern.len() + recipe_width - 1) / recipe_width;
425
426        // Recipe size cannot fit in the given inventory shape: discard immediately.
427        // NOTE: This also avoids arithmetics underflow just below.
428        if recipe_width > 3 || recipe_height > 3 {
429            return None;
430        }
431
432        // For each possible starting point in the inventory, check.
433        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                            // We are outside the checked region, slot should be empty.
444                            if !stack.is_empty() {
445                                continue 'out;
446                            }
447                        } else {
448
449                            // We are in the checked region.
450                            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    /// Check if this shapeless recipe can be crafted with the given inventory items.
492    fn check(&self, inv: &[ItemStack; 9]) -> Option<ItemStack> {
493        
494        // Too few stacks for the current pattern: discard immediately.
495        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                // If we land here, we did not found the required item in the pattern.
512                return None;
513            }
514        }
515
516        // Not all the pattern has been matched
517        if pat_matched != (1 << self.pattern.len()) - 1 {
518            None
519        } else {
520            Some(self.result)
521        }
522
523    }
524
525}