use crate::item::{self, ItemStack};
use crate::block;
#[derive(Debug, Default)]
pub struct CraftTracker {
current_recipe: Option<(usize, ItemStack)>,
}
impl CraftTracker {
pub fn update(&mut self, grid: &[ItemStack; 9]) {
self.current_recipe = None;
if grid.iter().copied().all(ItemStack::is_empty) {
return;
}
for (recipe_index, recipe) in RECIPES.iter().enumerate() {
let item = match recipe {
Recipe::Shaped(shaped) => shaped.check(grid),
Recipe::Shapeless(shapeless) => shapeless.check(grid),
};
if let Some(item) = item {
self.current_recipe = Some((recipe_index, item));
break;
}
}
}
pub fn consume(&self, grid: &mut [ItemStack; 9]) {
if self.current_recipe.is_none() {
return;
}
for stack in grid.iter_mut() {
if stack.is_empty() || stack.size == 1 {
*stack = ItemStack::EMPTY;
} else {
*stack = stack.with_size(stack.size - 1);
}
}
}
pub fn recipe(&self) -> Option<ItemStack> {
self.current_recipe.map(|(_, item)| item)
}
}
macro_rules! const_stacks {
( $( $name:ident = $value:expr; )* ) => {
$( const $name: ItemStack = ItemStack { id: $value as u16, size: 1, damage: 0 }; )*
};
}
const EMPTY: ItemStack = ItemStack::EMPTY;
const PAPER_3: ItemStack = ItemStack::new_sized(item::PAPER, 0, 3);
const FENCE_2: ItemStack = ItemStack::new_block_sized(block::FENCE, 0, 2);
const STONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 0, 3);
const SANDSTONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 1, 3);
const WOOD_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 2, 3);
const COBBLESTONE_SLAB_3: ItemStack = ItemStack::new_block_sized(block::SLAB, 3, 3);
const LADDER_2: ItemStack = ItemStack::new_block_sized(block::LADDER, 0, 2);
const TRAPDOOR_2: ItemStack = ItemStack::new_block_sized(block::TRAPDOOR, 0, 2);
const WOOD_4: ItemStack = ItemStack::new_block_sized(block::WOOD, 0, 4);
const STICK_4: ItemStack = ItemStack::new_sized(item::STICK, 0, 4);
const TORCH_4: ItemStack = ItemStack::new_block_sized(block::TORCH, 0, 4);
const CHARCOAL: ItemStack = ItemStack::new_single(item::COAL, 1);
const BOWL_4: ItemStack = ItemStack::new_sized(item::BOWL, 0, 4);
const RAIL_16: ItemStack = ItemStack::new_block_sized(block::RAIL, 0, 16);
const POWERED_RAIL_6: ItemStack = ItemStack::new_block_sized(block::POWERED_RAIL, 0, 6);
const DETECTOR_RAIL_6: ItemStack = ItemStack::new_block_sized(block::DETECTOR_RAIL, 0, 6);
const WOOD_STAIR_4: ItemStack = ItemStack::new_block_sized(block::WOOD_STAIR, 0, 4);
const COBBLESTONE_STAIR_4: ItemStack = ItemStack::new_block_sized(block::COBBLESTONE_STAIR, 0, 4);
const ARROW_4: ItemStack = ItemStack::new_sized(item::ARROW, 0, 4);
const LAPIS: ItemStack = ItemStack::new_single(item::DYE, 4);
const COOKIE_8: ItemStack = ItemStack::new_sized(item::COOKIE, 0, 8);
const COCOA: ItemStack = ItemStack::new_single(item::DYE, 3);
const YELLOW_DYE_2: ItemStack = ItemStack::new_sized(item::DYE, 11, 2);
const RED_DYE_2: ItemStack = ItemStack::new_sized(item::DYE, 1, 2);
const BONE_MEAL_2: ItemStack = ItemStack::new_sized(item::DYE, 15, 3);
const_stacks! {
SUGAR_CANES = item::SUGAR_CANES;
PAPER = item::PAPER;
BOOK = item::BOOK;
STICK = item::STICK;
DIAMOND = item::DIAMOND;
WOOD = block::WOOD;
JUKEBOX = block::JUKEBOX;
REDSTONE = item::REDSTONE;
NOTE_BLOCK = block::NOTE_BLOCK;
BOOKSHELF = block::BOOKSHELF;
SNOWBALL = item::SNOWBALL;
SNOW_BLOCK = block::SNOW_BLOCK;
CLAY = item::CLAY;
CLAY_BLOCK = block::CLAY;
BRICK = item::BRICK;
BRICK_BLOCK = block::BRICK;
GLOWSTONE_DUST = item::GLOWSTONE_DUST;
GLOWSTONE = block::GLOWSTONE;
STRING = item::STRING;
WOOL = block::WOOL;
GUNPOWDER = item::GUNPOWDER;
SAND = block::SAND;
TNT = block::TNT;
BED = item::BED;
STONE = block::STONE;
SANDSTONE = block::SANDSTONE;
COBBLE = block::COBBLESTONE;
WOOD_DOOR = item::WOOD_DOOR;
IRON_DOOR = item::IRON_DOOR;
IRON_INGOT = item::IRON_INGOT;
SIGN = item::SIGN;
SUGAR = item::SUGAR;
MILK_BUCKET = item::MILK_BUCKET;
WHEAT = item::WHEAT;
EGG = item::EGG;
CAKE = item::CAKE;
LOG = block::LOG;
COAL = item::COAL;
GOLD_INGOT = item::GOLD_INGOT;
STONE_PRESSURE_PLATE = block::STONE_PRESSURE_PLATE;
MINECART = item::MINECART;
PUMPKIN = block::PUMPKIN;
TORCH = block::TORCH;
PUMPKIN_LIT = block::PUMPKIN_LIT;
CHEST_MINECART = item::CHEST_MINECART;
FURNACE_MINECART = item::FURNACE_MINECART;
CHEST = block::CHEST;
FURNACE = block::FURNACE;
BOAT = item::BOAT;
BUCKET = item::BUCKET;
FLINT_AND_STEEL = item::FLINT_AND_STEEL;
FLINT = item::FLINT;
BREAD = item::BREAD;
FISHING_ROD = item::FISHING_ROD;
PAINTING = item::PAINTING;
APPLE = item::APPLE;
GOLD_APPLE = item::GOLD_APPLE;
LEVER = block::LEVER;
REDSTONE_TORCH = block::REDSTONE_TORCH_LIT;
REPEATER = item::REPEATER;
CLOCK = item::CLOCK;
COMPASS = item::COMPASS;
MAP = item::MAP;
BUTTON = block::BUTTON;
WOOD_PRESSURE_PLATE = block::WOOD_PRESSURE_PLATE;
DISPENSER = block::DISPENSER;
BOW = item::BOW;
FEATHER = item::FEATHER;
PISTON = block::PISTON;
STICKY_PISTON = block::STICKY_PISTON;
SLIMEBALL = item::SLIMEBALL;
SHEARS = item::SHEARS;
BOWL = item::BOWL;
MUSHROOM_STEW = item::MUSHROOM_STEW;
BROWN_MUSHROOM = block::BROWN_MUSHROOM;
RED_MUSHROOM = block::RED_MUSHROOM;
CRAFTING_TABLE = block::CRAFTING_TABLE;
LEATHER = item::LEATHER;
DANDELION = block::DANDELION;
POPPY = block::POPPY;
BONE = item::BONE;
}
macro_rules! tool {
( pickaxe $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, EMPTY, STICK, EMPTY, EMPTY, STICK, EMPTY], 3)
};
( axe $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK, $mat, STICK, EMPTY], 2)
};
( shovel $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, STICK, STICK], 1)
};
( hoe $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK, EMPTY, STICK, EMPTY], 2)
};
( sword $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, STICK], 1)
};
}
macro_rules! armor {
( helmet $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, $mat, EMPTY, $mat], 3)
};
( chestplate $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, EMPTY, $mat, $mat, $mat, $mat, $mat, $mat, $mat], 3)
};
( leggings $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, $mat, $mat, $mat, EMPTY, $mat, $mat, EMPTY, $mat], 3)
};
( boots $result:expr, $mat:expr ) => {
Recipe::new_shaped(ItemStack::new_single($result, 0), &[$mat, EMPTY, $mat, $mat, EMPTY, $mat], 3)
};
}
macro_rules! ore_block {
( construct $block:expr, $ore:expr ) => {
Recipe::new_shaped(ItemStack::new_block($block, 0), &[$ore, $ore, $ore, $ore, $ore, $ore, $ore, $ore, $ore], 3)
};
( destruct $block:expr, $ore:expr ) => {
Recipe::new_shaped($ore.with_size(9), &[ItemStack::new_block($block, 0)], 1)
};
}
macro_rules! dye_mix {
( $meta:literal * $count:literal, [ $( $pattern_meta:literal ),+ ] ) => {
Recipe::new_shapeless(ItemStack::new_sized(item::DYE, $meta, $count), &[ $( ItemStack::new_single(item::DYE, $pattern_meta) ),+ ])
};
}
const RECIPES: &'static [Recipe] = &[
Recipe::new_shaped(PAPER_3, &[SUGAR_CANES, SUGAR_CANES, SUGAR_CANES], 3),
Recipe::new_shaped(BOOK, &[PAPER, PAPER, PAPER], 1),
Recipe::new_shaped(FENCE_2, &[STICK, STICK, STICK, STICK, STICK, STICK], 3),
Recipe::new_shaped(JUKEBOX, &[WOOD, WOOD, WOOD, WOOD, DIAMOND, WOOD, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(NOTE_BLOCK, &[WOOD, WOOD, WOOD, WOOD, REDSTONE, WOOD, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(BOOKSHELF, &[WOOD, WOOD, WOOD, BOOK, BOOK, BOOK, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(SNOW_BLOCK, &[SNOWBALL, SNOWBALL, SNOWBALL, SNOWBALL], 2),
Recipe::new_shaped(CLAY_BLOCK, &[CLAY, CLAY, CLAY, CLAY], 2),
Recipe::new_shaped(BRICK_BLOCK, &[BRICK, BRICK, BRICK, BRICK], 2),
Recipe::new_shaped(GLOWSTONE, &[GLOWSTONE_DUST, GLOWSTONE_DUST, GLOWSTONE_DUST, GLOWSTONE_DUST], 2),
Recipe::new_shaped(WOOL, &[STRING, STRING, STRING, STRING], 2),
Recipe::new_shaped(TNT, &[GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER, SAND, GUNPOWDER], 3),
Recipe::new_shaped(STONE_SLAB_3, &[STONE, STONE, STONE], 3),
Recipe::new_shaped(SANDSTONE_SLAB_3, &[SANDSTONE, SANDSTONE, SANDSTONE], 3),
Recipe::new_shaped(WOOD_SLAB_3, &[WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(COBBLESTONE_SLAB_3, &[COBBLE, COBBLE, COBBLE], 3),
Recipe::new_shaped(LADDER_2, &[STICK, EMPTY, STICK, STICK, STICK, STICK, STICK, EMPTY, STICK], 3),
Recipe::new_shaped(WOOD_DOOR, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD], 2),
Recipe::new_shaped(TRAPDOOR_2, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(IRON_DOOR, &[IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT], 2),
Recipe::new_shaped(SIGN, &[WOOD, WOOD, WOOD, WOOD, WOOD, WOOD, EMPTY, STICK, EMPTY], 3),
Recipe::new_shaped(CAKE, &[MILK_BUCKET, MILK_BUCKET, MILK_BUCKET, SUGAR, EGG, SUGAR, WHEAT, WHEAT, WHEAT], 3),
Recipe::new_shaped(SUGAR, &[SUGAR_CANES], 1),
Recipe::new_shaped(WOOD_4, &[LOG], 1),
Recipe::new_shaped(STICK_4, &[WOOD, WOOD], 1),
Recipe::new_shaped(TORCH_4, &[COAL, STICK], 1),
Recipe::new_shaped(TORCH_4, &[CHARCOAL, STICK], 1),
Recipe::new_shaped(BOWL_4, &[WOOD, EMPTY, WOOD, EMPTY, WOOD, EMPTY], 3),
Recipe::new_shaped(RAIL_16, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, STICK, IRON_INGOT, IRON_INGOT, EMPTY, IRON_INGOT], 3),
Recipe::new_shaped(POWERED_RAIL_6, &[GOLD_INGOT, EMPTY, GOLD_INGOT, GOLD_INGOT, STICK, GOLD_INGOT, GOLD_INGOT, REDSTONE, GOLD_INGOT], 3),
Recipe::new_shaped(DETECTOR_RAIL_6, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, STONE_PRESSURE_PLATE, IRON_INGOT, IRON_INGOT, REDSTONE, IRON_INGOT], 3),
Recipe::new_shaped(MINECART, &[IRON_INGOT, EMPTY, IRON_INGOT, IRON_INGOT, IRON_INGOT, IRON_INGOT], 3),
Recipe::new_shaped(PUMPKIN_LIT, &[PUMPKIN, TORCH], 1),
Recipe::new_shaped(CHEST_MINECART, &[CHEST, MINECART], 1),
Recipe::new_shaped(FURNACE_MINECART, &[FURNACE, MINECART], 1),
Recipe::new_shaped(BOAT, &[WOOD, EMPTY, WOOD, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(BUCKET, &[IRON_INGOT, EMPTY, IRON_INGOT, EMPTY, IRON_INGOT, EMPTY], 3),
Recipe::new_shaped(FLINT_AND_STEEL, &[IRON_INGOT, EMPTY, EMPTY, FLINT], 2),
Recipe::new_shaped(BREAD, &[WHEAT, WHEAT, WHEAT], 3),
Recipe::new_shaped(WOOD_STAIR_4, &[WOOD, EMPTY, EMPTY, WOOD, WOOD, EMPTY, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(FISHING_ROD, &[EMPTY, EMPTY, STICK, EMPTY, STICK, STRING, STICK, EMPTY, STRING], 3),
Recipe::new_shaped(COBBLESTONE_STAIR_4, &[COBBLE, EMPTY, EMPTY, COBBLE, COBBLE, EMPTY, COBBLE, COBBLE, COBBLE], 3),
Recipe::new_shaped(PAINTING, &[STICK, STICK, STICK, STICK, WOOL, STICK, STICK, STICK, STICK], 3),
Recipe::new_shaped(GOLD_APPLE, &[GOLD_INGOT, GOLD_INGOT, GOLD_INGOT, GOLD_INGOT, APPLE, GOLD_INGOT, GOLD_INGOT, GOLD_INGOT], 3),
Recipe::new_shaped(LEVER, &[STICK, COBBLE], 1),
Recipe::new_shaped(REDSTONE_TORCH, &[REDSTONE, STICK], 1),
Recipe::new_shaped(REPEATER, &[REDSTONE_TORCH, REDSTONE, REDSTONE_TORCH, STONE, STONE, STONE], 3),
Recipe::new_shaped(CLOCK, &[EMPTY, GOLD_INGOT, EMPTY, GOLD_INGOT, REDSTONE, GOLD_INGOT, EMPTY, GOLD_INGOT, EMPTY], 3),
Recipe::new_shaped(COMPASS, &[EMPTY, IRON_INGOT, EMPTY, IRON_INGOT, REDSTONE, IRON_INGOT, EMPTY, IRON_INGOT, EMPTY], 3),
Recipe::new_shaped(MAP, &[PAPER, PAPER, PAPER, PAPER, COMPASS, PAPER, PAPER, PAPER, PAPER], 3),
Recipe::new_shaped(BUTTON, &[STONE, STONE], 1),
Recipe::new_shaped(STONE_PRESSURE_PLATE, &[STONE, STONE], 2),
Recipe::new_shaped(WOOD_PRESSURE_PLATE, &[WOOD, WOOD], 2),
Recipe::new_shaped(DISPENSER, &[COBBLE, COBBLE, COBBLE, COBBLE, BOW, COBBLE, COBBLE, REDSTONE, COBBLE], 3),
Recipe::new_shaped(PISTON, &[WOOD, WOOD, WOOD, COBBLE, IRON_INGOT, COBBLE, COBBLE, REDSTONE, COBBLE], 3),
Recipe::new_shaped(STICKY_PISTON, &[SLIMEBALL, PISTON], 1),
Recipe::new_shaped(BED, &[WOOL, WOOL, WOOL, WOOD, WOOD], 3),
Recipe::new_shaped(SHEARS, &[IRON_INGOT, EMPTY, EMPTY, IRON_INGOT], 2),
Recipe::new_shaped(BOW, &[EMPTY, STICK, STRING, STICK, EMPTY, STRING, EMPTY, STICK, STRING], 3),
Recipe::new_shaped(ARROW_4, &[FLINT, STICK, FEATHER], 1),
Recipe::new_shaped(MUSHROOM_STEW, &[RED_MUSHROOM, BROWN_MUSHROOM, BOWL], 1),
Recipe::new_shaped(MUSHROOM_STEW, &[BROWN_MUSHROOM, RED_MUSHROOM, BOWL], 1),
Recipe::new_shaped(COOKIE_8, &[WHEAT, COCOA, WHEAT], 3),
Recipe::new_shaped(CHEST, &[WOOD, WOOD, WOOD, WOOD, EMPTY, WOOD, WOOD, WOOD, WOOD], 3),
Recipe::new_shaped(FURNACE, &[COBBLE, COBBLE, COBBLE, COBBLE, EMPTY, COBBLE, COBBLE, COBBLE, COBBLE], 3),
Recipe::new_shaped(CRAFTING_TABLE, &[WOOD, WOOD, WOOD, WOOD], 2),
Recipe::new_shaped(SANDSTONE, &[SAND, SAND, SAND, SAND], 2),
tool!(pickaxe item::WOOD_PICKAXE, WOOD),
tool!(pickaxe item::STONE_PICKAXE, COBBLE),
tool!(pickaxe item::GOLD_PICKAXE, GOLD_INGOT),
tool!(pickaxe item::IRON_PICKAXE, IRON_INGOT),
tool!(pickaxe item::DIAMOND_PICKAXE, DIAMOND),
tool!(axe item::WOOD_AXE, WOOD),
tool!(axe item::STONE_AXE, COBBLE),
tool!(axe item::GOLD_AXE, GOLD_INGOT),
tool!(axe item::IRON_AXE, IRON_INGOT),
tool!(axe item::DIAMOND_AXE, DIAMOND),
tool!(shovel item::WOOD_SHOVEL, WOOD),
tool!(shovel item::STONE_SHOVEL, COBBLE),
tool!(shovel item::GOLD_SHOVEL, GOLD_INGOT),
tool!(shovel item::IRON_SHOVEL, IRON_INGOT),
tool!(shovel item::DIAMOND_SHOVEL, DIAMOND),
tool!(hoe item::WOOD_HOE, WOOD),
tool!(hoe item::STONE_HOE, COBBLE),
tool!(hoe item::GOLD_HOE, GOLD_INGOT),
tool!(hoe item::IRON_HOE, IRON_INGOT),
tool!(hoe item::DIAMOND_HOE, DIAMOND),
tool!(sword item::WOOD_SWORD, WOOD),
tool!(sword item::STONE_SWORD, COBBLE),
tool!(sword item::GOLD_SWORD, GOLD_INGOT),
tool!(sword item::IRON_SWORD, IRON_INGOT),
tool!(sword item::DIAMOND_SWORD, DIAMOND),
armor!(helmet item::LEATHER_HELMET, LEATHER),
armor!(helmet item::GOLD_HELMET, GOLD_INGOT),
armor!(helmet item::IRON_HELMET, IRON_INGOT),
armor!(helmet item::DIAMOND_HELMET, DIAMOND),
armor!(chestplate item::LEATHER_CHESTPLATE, LEATHER),
armor!(chestplate item::GOLD_CHESTPLATE, GOLD_INGOT),
armor!(chestplate item::IRON_CHESTPLATE, IRON_INGOT),
armor!(chestplate item::DIAMOND_CHESTPLATE, DIAMOND),
armor!(leggings item::LEATHER_LEGGINGS, LEATHER),
armor!(leggings item::GOLD_LEGGINGS, GOLD_INGOT),
armor!(leggings item::IRON_LEGGINGS, IRON_INGOT),
armor!(leggings item::DIAMOND_LEGGINGS, DIAMOND),
armor!(boots item::LEATHER_BOOTS, LEATHER),
armor!(boots item::GOLD_BOOTS, GOLD_INGOT),
armor!(boots item::IRON_BOOTS, IRON_INGOT),
armor!(boots item::DIAMOND_BOOTS, DIAMOND),
ore_block!(construct block::IRON_BLOCK, IRON_INGOT),
ore_block!(construct block::GOLD_BLOCK, GOLD_INGOT),
ore_block!(construct block::DIAMOND_BLOCK, DIAMOND),
ore_block!(construct block::LAPIS_BLOCK, LAPIS),
ore_block!(destruct block::IRON_BLOCK, IRON_INGOT),
ore_block!(destruct block::GOLD_BLOCK, GOLD_INGOT),
ore_block!(destruct block::DIAMOND_BLOCK, DIAMOND),
ore_block!(destruct block::LAPIS_BLOCK, LAPIS),
Recipe::new_shapeless(YELLOW_DYE_2, &[DANDELION]),
Recipe::new_shapeless(RED_DYE_2, &[POPPY]),
Recipe::new_shapeless(BONE_MEAL_2, &[BONE]),
dye_mix!(9 * 2, [1, 15]),
dye_mix!(14 * 2, [1, 11]),
dye_mix!(10 * 2, [2, 15]),
dye_mix!(8 * 2, [0, 15]),
dye_mix!(7 * 2, [8, 15]),
dye_mix!(7 * 3, [0, 15, 15]),
dye_mix!(12 * 2, [4, 15]),
dye_mix!(6 * 2, [4, 2]),
dye_mix!(5 * 2, [4, 1]),
dye_mix!(13 * 2, [5, 9]),
dye_mix!(13 * 3, [4, 1, 9]),
dye_mix!(13 * 4, [4, 1, 1, 15]),
];
enum Recipe {
Shaped(ShapedRecipe),
Shapeless(ShapelessRecipe),
}
struct ShapedRecipe {
result: ItemStack,
pattern: &'static [ItemStack],
width: u8,
}
struct ShapelessRecipe {
result: ItemStack,
pattern: &'static [ItemStack],
}
impl Recipe {
const fn new_shaped(result: ItemStack, pattern: &'static [ItemStack], width: u8) -> Self {
Self::Shaped(ShapedRecipe { result, pattern, width })
}
const fn new_shapeless(result: ItemStack, pattern: &'static [ItemStack]) -> Self {
Self::Shapeless(ShapelessRecipe { result, pattern })
}
}
impl ShapedRecipe {
fn check(&self, grid: &[ItemStack; 9]) -> Option<ItemStack> {
let recipe_width = self.width as usize;
let recipe_height = (self.pattern.len() + recipe_width - 1) / recipe_width;
if recipe_width > 3 || recipe_height > 3 {
return None;
}
for start_x in 0..=(3 - recipe_width) {
'out: for start_y in 0..=(3 - recipe_height) {
let mut normal_valid = true;
let mut flip_valid = true;
for dx in 0..3 {
for dy in 0..3 {
let stack = grid[dx + dy * 3];
if dx < start_x || dx >= start_x + recipe_width || dy < start_y || dy >= start_y + recipe_height {
if !stack.is_empty() {
continue 'out;
}
} else {
let pattern_x = dx - start_x;
let pattern_y = dy - start_y;
let flip_pattern_x = recipe_width - pattern_x - 1;
if normal_valid {
let normal_stack = self.pattern[pattern_x + pattern_y * recipe_width];
normal_valid =
(normal_stack.is_empty() && stack.is_empty()) ||
(!stack.is_empty() && (normal_stack.id, normal_stack.damage) == (stack.id, stack.damage));
}
if flip_valid {
let flip_stack = self.pattern[flip_pattern_x + pattern_y * recipe_width];
flip_valid =
(flip_stack.is_empty() && stack.is_empty()) ||
(!stack.is_empty() && (flip_stack.id, flip_stack.damage) == (stack.id, stack.damage));
}
if !normal_valid && !flip_valid {
continue 'out;
}
}
}
}
if normal_valid || flip_valid {
return Some(self.result);
}
}
}
None
}
}
impl ShapelessRecipe {
fn check(&self, inv: &[ItemStack; 9]) -> Option<ItemStack> {
if inv.len() < self.pattern.len() {
return None;
}
let mut pat_matched = 0u32;
'inv: for stack in inv.iter().copied() {
if !stack.is_empty() {
for (i, pat_stack) in self.pattern.iter().copied().enumerate() {
if pat_matched & (1 << i) == 0 {
if (pat_stack.id, pat_stack.damage) == (stack.id, stack.damage) {
pat_matched |= 1 << i;
continue 'inv;
}
}
}
return None;
}
}
if pat_matched != (1 << self.pattern.len()) - 1 {
None
} else {
Some(self.result)
}
}
}