From 218a93ef9f4f9e71fbfefd5c237f67e1243328c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sun, 17 Jul 2016 14:08:23 +0200
Subject: [PATCH] Workbench progress

+ More recipe abstraction
+ Bugfixes
+ Red cross when there are not enough ingredients
---
 graphics/alloy-furnace-gui-1.xcf              | Bin 40582 -> 40083 bytes
 graphics/gui-common.xcf                       | Bin 4420 -> 4875 bytes
 .../api/recipes/I_CraftingRecipeWrapper.java  |   5 +-
 .../api/recipes/I_RecipeRequirements.java     |  28 +++++
 src/java/mmm/recipes/ROreRecipeHelper.java    |  65 ++++++++++
 src/java/mmm/recipes/RRequirements.java       | 111 ++++++++++++++++++
 .../mmm/recipes/RShapedOreRecipeWrapper.java  |  16 +--
 .../mmm/recipes/RShapedRecipeWrapper.java     |  46 ++++++--
 .../recipes/RShapelessOreRecipeWrapper.java   |  73 ++----------
 .../mmm/recipes/RShapelessRecipeWrapper.java  |  65 +++-------
 src/java/mmm/tech/base/workbench/TBWBGui.java |  16 +++
 src/java/mmm/utils/gui/UGui.java              |  34 +++++-
 .../assets/mmm/textures/gui/gui-common.png    | Bin 1523 -> 1224 bytes
 13 files changed, 325 insertions(+), 134 deletions(-)
 create mode 100644 src/java/mmm/core/api/recipes/I_RecipeRequirements.java
 create mode 100644 src/java/mmm/recipes/ROreRecipeHelper.java
 create mode 100644 src/java/mmm/recipes/RRequirements.java

diff --git a/graphics/alloy-furnace-gui-1.xcf b/graphics/alloy-furnace-gui-1.xcf
index 96351a860fe369b6fd4e6c7f98a9de5cf5d7376b..616d444ba77d45dfee30cac5f12c740dce04b898 100644
GIT binary patch
delta 437
zcmZqM%QSf>(}pTW(di5fEV>K~tbZ98IDauPa35n};QKmxGUIktZw3eu08${#!oa{3
zk1EELzd4_&gh}-YsxU|?%MVmBR*uaatn!SLcQIy6p3bJBxdT-iq=oGasu<fH5P$ML
zcB#!w>}#1dS3so=fHc%WTzgQ(xWGnCuHg=X;(w~eP!)VY8iaus@wB6g@l4-5pC^TB
z@+Sdt7ABz7WPg^N$%TUI_57<L3K@YE2!k~9A7@~gQw_vWLtt_O!VC-w?hFiS_ZS$=
zB!F0hfx(TJfguoRMuIsI1LcY?GBDJ2F);K2#rk$LFifdrV3>XqXjV4Ja7PB)|Nj{N
Zg9*m}*2n&AzAF>Ny7`PuGKkpK2mt9OOJV>3

delta 796
zcmY*XF>4e-7=1IlJ4x=2>?KVIDWbxWGnIC3enC1V=>wWvBTEcksz7on#3qUd1A!1o
zV{e2N#ISaPf|Y5^53opSpJUhe?aryQ@c6!Y?|X0OzFYfAzx=G<@2UAyAiNJ41vCuM
zyaZzQ?cer|`q+Jl`f0NWLO^f&obK~%4};g)jsB%N*S;!_aO87QD+{AVWvAmEdt|zu
zFTN}mbLDg9I=eF8gS#IbeUH`Mr}53_8kN=VG}j!8&cIDfvkLfvmi4*T+swAM^jwuH
z$=m@NYrU;oo6n#1`*zW!(Rs<o4$a(P)|px2%=Kb&SBk&-T>Mkcx1m!5l@<xZZ_+$Z
z5V)#Pq`Ar~W!z~L3As@MCxt>pGBhyK2GUOcbyi))QLQa?Bz<yZkmS+B<bQ8-+<!Mp
z<Yo$^G)WLaCv6P43;};ZX^5UiP=<s?SRr67PzGg$$#MdwoFXyBI>B=^7G<h-jNNtI
zc-^w{YVYRJ<LzE5JGTe%($kqU5o)QL;r};t0i10Ud%iZyZ}`p%(ER~CXc7y+Do56t
f@no5xw#yIha32_w8@>aMQ{W_L)<zb$U%dJQraP-c

diff --git a/graphics/gui-common.xcf b/graphics/gui-common.xcf
index 53d862797176c2b4d5c1814c73533f9f746687e5..26b7fb49d7740e49fac06f5e100a10cdfd79a4fe 100644
GIT binary patch
delta 475
zcmX@2)U7tbj!|Hvy)~m|Hv<E67y|=u1Oo!_0+}Gp2*g}LsVNG{Mft^(=Wv%!7GPqj
zmjjBj12GE_gMbJSgV+Kf4g@f{LK!fVfbvX{sA5c+AU;zL8v|p`FUy=gdn_548GeE2
zoIQ+t{#gQT{|93Iv1G7>P!REYkc8zPR*({A5Q`baVudJ%vA|+LS%?<A=0S{O+yi&R
z4@;06EJ0>6S=KYGWB_5N|7;9w|5+F~{@XAyu>AkW@Shn%Fad>FfKqHgQJ4~3DwzIH
zZfD$}#=-*)2~bFaFfepj6i~%j3^vCyxo}Qi$=$EU#|o8`0@5Zx3{=l2i7LjYxjByK
G84CdCnsM6z

delta 136
zcmeBHJEAnfj*)+(y)`3mI|BoECj%5r4rAV+#$*fS$pL8)W?^7p3PcrSO5Ci&;=;+o
p1Qec}!#i1v#}2AM3P_s(F;Ejv5ULnY5{SP!P;d(4=DqxW%mAF95G()y

diff --git a/src/java/mmm/core/api/recipes/I_CraftingRecipeWrapper.java b/src/java/mmm/core/api/recipes/I_CraftingRecipeWrapper.java
index 57471ee..92438e7 100644
--- a/src/java/mmm/core/api/recipes/I_CraftingRecipeWrapper.java
+++ b/src/java/mmm/core/api/recipes/I_CraftingRecipeWrapper.java
@@ -20,9 +20,6 @@ public interface I_CraftingRecipeWrapper
 	public void addInputsToDisplay( IInventory displayInventory );
 
 
-	public boolean canCraft( IInventory inventory );
-
-
-	public void craft( IInventory input , IInventory output , int quantity );
+	public I_RecipeRequirements getRequirements( );
 
 }
diff --git a/src/java/mmm/core/api/recipes/I_RecipeRequirements.java b/src/java/mmm/core/api/recipes/I_RecipeRequirements.java
new file mode 100644
index 0000000..f365b01
--- /dev/null
+++ b/src/java/mmm/core/api/recipes/I_RecipeRequirements.java
@@ -0,0 +1,28 @@
+package mmm.core.api.recipes;
+
+
+import java.util.List;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+
+
+
+public interface I_RecipeRequirements
+{
+
+	int size( );
+
+
+	int getQuantity( int pos );
+
+
+	List< ItemStack > getItemTypes( int pos );
+
+
+	boolean checkItemStack( int pos , ItemStack stack );
+
+
+	boolean checkInventory( IInventory inventory );
+
+}
\ No newline at end of file
diff --git a/src/java/mmm/recipes/ROreRecipeHelper.java b/src/java/mmm/recipes/ROreRecipeHelper.java
new file mode 100644
index 0000000..38f5e68
--- /dev/null
+++ b/src/java/mmm/recipes/ROreRecipeHelper.java
@@ -0,0 +1,65 @@
+package mmm.recipes;
+
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+
+
+class ROreRecipeHelper
+{
+
+	static RRequirements extractRequirements( final Object[] recipe )
+	{
+		final List< ItemStack > combinedInputStacks = new ArrayList<>( );
+		final IdentityHashMap< List< ItemStack > , Integer > combinedInputOreLists = new IdentityHashMap<>( );
+
+		ROreRecipeHelper.extractInputs( recipe , combinedInputStacks , combinedInputOreLists );
+
+		final RRequirements reqs = new RRequirements( combinedInputStacks.size( ) + combinedInputOreLists.size( ) );
+		int i;
+		for ( i = 0 ; i < combinedInputStacks.size( ) ; i++ ) {
+			final ItemStack stack = combinedInputStacks.get( i );
+			reqs.put( i , stack , stack.stackSize );
+		}
+		for ( final Map.Entry< List< ItemStack > , Integer > entry : combinedInputOreLists.entrySet( ) ) {
+			reqs.put( i++ , entry.getKey( ) , entry.getValue( ) );
+		}
+		return reqs;
+	}
+
+
+	private static void extractInputs( final Object[] recipe , final List< ItemStack > combinedInputStacks ,
+			final IdentityHashMap< List< ItemStack > , Integer > combinedInputOreLists )
+	{
+		OUTER: for ( final Object input : recipe ) {
+			if ( input instanceof ItemStack ) {
+				final ItemStack invStack = (ItemStack) input;
+				for ( final ItemStack ciStack : combinedInputStacks ) {
+					if ( OreDictionary.itemMatches( ciStack , invStack , true ) ) {
+						ciStack.stackSize ++;
+						continue OUTER;
+					}
+				}
+				ItemStack ciStack = invStack.copy( );
+				ciStack.stackSize = 1;
+				combinedInputStacks.add( ciStack );
+
+			} else if ( input instanceof List ) {
+				@SuppressWarnings( "unchecked" )
+				final List< ItemStack > oreList = (List< ItemStack >) input;
+				if ( combinedInputOreLists.containsKey( oreList ) ) {
+					combinedInputOreLists.put( oreList , combinedInputOreLists.get( oreList ) + 1 );
+				} else {
+					combinedInputOreLists.put( oreList , 1 );
+				}
+			}
+		}
+	}
+
+}
diff --git a/src/java/mmm/recipes/RRequirements.java b/src/java/mmm/recipes/RRequirements.java
new file mode 100644
index 0000000..ee8ee14
--- /dev/null
+++ b/src/java/mmm/recipes/RRequirements.java
@@ -0,0 +1,111 @@
+package mmm.recipes;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import mmm.core.api.recipes.I_RecipeRequirements;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+
+
+class RRequirements
+		implements I_RecipeRequirements
+{
+
+	private final List< ItemStack >[] itemTypes;
+	private final int[] quantities;
+
+
+	@SuppressWarnings( "unchecked" )
+	RRequirements( final int nInputs )
+	{
+		this.itemTypes = new List[ nInputs ];
+		this.quantities = new int[ nInputs ];
+	}
+
+
+	RRequirements( final ArrayList< ItemStack > inputs )
+	{
+		this( inputs.size( ) );
+		for ( int i = 0 ; i < this.quantities.length ; i++ ) {
+			final ItemStack input = inputs.get( i );
+			this.put( i , input , input.stackSize );
+		}
+	}
+
+
+	void put( final int pos , final ItemStack stack , final int quantity )
+	{
+		this.itemTypes[ pos ] = Arrays.asList( stack );
+		this.quantities[ pos ] = quantity;
+	}
+
+
+	void put( final int pos , final List< ItemStack > stacks , final int quantity )
+	{
+		assert stacks != null;
+		this.itemTypes[ pos ] = stacks;
+		this.quantities[ pos ] = quantity;
+	}
+
+
+	@Override
+	public int size( )
+	{
+		return this.quantities.length;
+	}
+
+
+	@Override
+	public int getQuantity( final int pos )
+	{
+		return this.quantities[ pos ];
+	}
+
+
+	@Override
+	public List< ItemStack > getItemTypes( final int pos )
+	{
+		return this.itemTypes[ pos ];
+	}
+
+
+	@Override
+	public boolean checkItemStack( final int pos , final ItemStack stack )
+	{
+		if ( stack != null ) {
+			for ( final ItemStack target : this.getItemTypes( pos ) ) {
+				if ( OreDictionary.itemMatches( target , stack , false ) ) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+
+	@Override
+	public boolean checkInventory( final IInventory inventory )
+	{
+		final int nInvItems = inventory.getSizeInventory( );
+		for ( int i = 0 ; i < this.quantities.length ; i++ ) {
+			int nLeft = this.quantities[ i ];
+
+			for ( int j = 0 ; j < nInvItems && nLeft > 0 ; j++ ) {
+				final ItemStack invStack = inventory.getStackInSlot( j );
+				if ( this.checkItemStack( i , invStack ) ) {
+					nLeft -= invStack.stackSize;
+				}
+			}
+
+			if ( nLeft > 0 ) {
+				return false;
+			}
+		}
+		return true;
+	}
+}
diff --git a/src/java/mmm/recipes/RShapedOreRecipeWrapper.java b/src/java/mmm/recipes/RShapedOreRecipeWrapper.java
index 3f6efdb..9fc2007 100644
--- a/src/java/mmm/recipes/RShapedOreRecipeWrapper.java
+++ b/src/java/mmm/recipes/RShapedOreRecipeWrapper.java
@@ -4,6 +4,7 @@ package mmm.recipes;
 import java.util.List;
 
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.core.api.recipes.I_RecipeRequirements;
 import net.minecraft.inventory.IInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
@@ -18,6 +19,7 @@ public class RShapedOreRecipeWrapper
 	private final ShapedOreRecipe recipe;
 	private final int width;
 	private final int height;
+	private final RRequirements requirements;
 
 
 	public RShapedOreRecipeWrapper( final ShapedOreRecipe recipe )
@@ -25,6 +27,7 @@ public class RShapedOreRecipeWrapper
 		this.recipe = recipe;
 		this.width = ObfuscationReflectionHelper.getPrivateValue( ShapedOreRecipe.class , this.recipe , "width" );
 		this.height = ObfuscationReflectionHelper.getPrivateValue( ShapedOreRecipe.class , this.recipe , "height" );
+		this.requirements = ROreRecipeHelper.extractRequirements( recipe.getInput( ) );
 	}
 
 
@@ -103,18 +106,9 @@ public class RShapedOreRecipeWrapper
 
 
 	@Override
-	public boolean canCraft( final IInventory inventory )
+	public I_RecipeRequirements getRequirements( )
 	{
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-
-	@Override
-	public void craft( final IInventory input , final IInventory output , final int quantity )
-	{
-		// TODO Auto-generated method stub
-
+		return this.requirements;
 	}
 
 }
diff --git a/src/java/mmm/recipes/RShapedRecipeWrapper.java b/src/java/mmm/recipes/RShapedRecipeWrapper.java
index 0852660..4b9b382 100644
--- a/src/java/mmm/recipes/RShapedRecipeWrapper.java
+++ b/src/java/mmm/recipes/RShapedRecipeWrapper.java
@@ -1,7 +1,10 @@
 package mmm.recipes;
 
 
+import java.util.ArrayList;
+
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.core.api.recipes.I_RecipeRequirements;
 import net.minecraft.inventory.IInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.crafting.ShapedRecipes;
@@ -13,11 +16,40 @@ public class RShapedRecipeWrapper
 		implements I_CraftingRecipeWrapper
 {
 	private final ShapedRecipes recipe;
+	private final RRequirements requirements;
 
 
 	public RShapedRecipeWrapper( final ShapedRecipes recipe )
 	{
 		this.recipe = recipe;
+		this.requirements = new RRequirements( extractInputs( recipe ) );
+	}
+
+
+	private static ArrayList< ItemStack > extractInputs( final ShapedRecipes recipe )
+	{
+		ArrayList< ItemStack > inputs = new ArrayList<>( );
+		for ( int i = 0 ; i < recipe.recipeWidth ; i++ ) {
+			RECIPE: for ( int j = 0 ; j < recipe.recipeHeight ; j++ ) {
+				ItemStack itemStack = recipe.recipeItems[ i + j * recipe.recipeWidth ];
+				if ( itemStack == null ) {
+					continue;
+				}
+
+				for ( int k = 0 ; k < inputs.size( ) ; k++ ) {
+					ItemStack input = inputs.get( k );
+					if ( OreDictionary.itemMatches( input , itemStack , true ) ) {
+						input.stackSize++;
+						continue RECIPE;
+					}
+				}
+
+				itemStack = itemStack.copy( );
+				itemStack.stackSize = 1;
+				inputs.add( itemStack );
+			}
+		}
+		return inputs;
 	}
 
 
@@ -72,23 +104,17 @@ public class RShapedRecipeWrapper
 		} else {
 			stack = stack.copy( );
 		}
+		stack.stackSize = 1;
 		inventory.setInventorySlotContents( index , stack );
 	}
 
 
-	@Override
-	public boolean canCraft( final IInventory inventory )
-	{
-		// TODO Auto-generated method stub
-		return false;
-	}
-
 
 	@Override
-	public void craft( final IInventory input , final IInventory output , final int quantity )
+	public I_RecipeRequirements getRequirements( )
 	{
-		// TODO Auto-generated method stub
-
+		return this.requirements;
 	}
 
+
 }
diff --git a/src/java/mmm/recipes/RShapelessOreRecipeWrapper.java b/src/java/mmm/recipes/RShapelessOreRecipeWrapper.java
index 59d0560..af69ef5 100644
--- a/src/java/mmm/recipes/RShapelessOreRecipeWrapper.java
+++ b/src/java/mmm/recipes/RShapelessOreRecipeWrapper.java
@@ -2,10 +2,10 @@ package mmm.recipes;
 
 
 import java.util.ArrayList;
-import java.util.IdentityHashMap;
 import java.util.List;
 
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.core.api.recipes.I_RecipeRequirements;
 import net.minecraft.inventory.IInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraftforge.oredict.OreDictionary;
@@ -17,36 +17,13 @@ public class RShapelessOreRecipeWrapper
 		implements I_CraftingRecipeWrapper
 {
 	private final ShapelessOreRecipe recipe;
-	private final List< ItemStack > combinedInputStacks;
-	private final IdentityHashMap< List< ItemStack > , Integer > combinedInputOreLists;
+	private final RRequirements requirements;
 
 
 	public RShapelessOreRecipeWrapper( final ShapelessOreRecipe recipe )
 	{
 		this.recipe = recipe;
-		this.combinedInputStacks = new ArrayList<>( );
-		this.combinedInputOreLists = new IdentityHashMap<>( );
-
-		OUTER: for ( final Object input : this.recipe.getInput( ) ) {
-			if ( input instanceof ItemStack ) {
-				final ItemStack invStack = (ItemStack) input;
-				for ( final ItemStack ciStack : this.combinedInputStacks ) {
-					if ( OreDictionary.itemMatches( ciStack , invStack , true ) ) {
-						ciStack.stackSize += invStack.stackSize;
-						continue OUTER;
-					}
-				}
-				this.combinedInputStacks.add( invStack.copy( ) );
-			} else if ( input instanceof List ) {
-				@SuppressWarnings( "unchecked" )
-				final List< ItemStack > oreList = (List< ItemStack >) input;
-				if ( this.combinedInputOreLists.containsKey( oreList ) ) {
-					this.combinedInputOreLists.put( oreList , this.combinedInputOreLists.get( oreList ) + 1 );
-				} else {
-					this.combinedInputOreLists.put( oreList , 1 );
-				}
-			}
-		}
+		this.requirements = ROreRecipeHelper.extractRequirements( recipe.getInput( ).toArray( ) );
 	}
 
 
@@ -92,47 +69,23 @@ public class RShapelessOreRecipeWrapper
 	@Override
 	public void addInputsToDisplay( final IInventory displayInventory )
 	{
-		int i = 0;
-		for ( final ItemStack stack : this.combinedInputStacks ) {
-			RShapelessOreRecipeWrapper.setSlot( displayInventory , i++ , stack , stack.stackSize );
-		}
-		for ( final List< ItemStack > oreList : this.combinedInputOreLists.keySet( ) ) {
-			if ( oreList.isEmpty( ) ) {
-				continue;
+		for ( int i = 0 ; i < this.requirements.size( ) ; i++ ) {
+			ItemStack stack = this.requirements.getItemTypes( i ).get( 0 );
+			if ( stack.getMetadata( ) == OreDictionary.WILDCARD_VALUE ) {
+				stack = new ItemStack( stack.getItem( ) , 1 , 0 );
+			} else {
+				stack = stack.copy( );
 			}
-
-			final ItemStack stack = oreList.get( 0 );
-			RShapelessOreRecipeWrapper.setSlot( displayInventory , i++ , stack ,
-					this.combinedInputOreLists.get( oreList ) );
+			stack.stackSize = this.requirements.getQuantity( i );
+			displayInventory.setInventorySlotContents( i , stack );
 		}
 	}
 
 
-	private static void setSlot( final IInventory inventory , final int index , ItemStack stack , final int size )
-	{
-		if ( stack.getMetadata( ) == OreDictionary.WILDCARD_VALUE ) {
-			stack = new ItemStack( stack.getItem( ) , 1 , 0 );
-		} else {
-			stack = stack.copy( );
-		}
-		stack.stackSize = size;
-		inventory.setInventorySlotContents( index , stack );
-	}
-
-
 	@Override
-	public boolean canCraft( final IInventory inventory )
+	public I_RecipeRequirements getRequirements( )
 	{
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-
-	@Override
-	public void craft( final IInventory input , final IInventory output , final int quantity )
-	{
-		// TODO Auto-generated method stub
-
+		return this.requirements;
 	}
 
 }
diff --git a/src/java/mmm/recipes/RShapelessRecipeWrapper.java b/src/java/mmm/recipes/RShapelessRecipeWrapper.java
index e7b5bf1..ced3170 100644
--- a/src/java/mmm/recipes/RShapelessRecipeWrapper.java
+++ b/src/java/mmm/recipes/RShapelessRecipeWrapper.java
@@ -2,9 +2,9 @@ package mmm.recipes;
 
 
 import java.util.ArrayList;
-import java.util.List;
 
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.core.api.recipes.I_RecipeRequirements;
 import net.minecraft.inventory.IInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.crafting.ShapelessRecipes;
@@ -17,23 +17,29 @@ public class RShapelessRecipeWrapper
 {
 
 	private final ShapelessRecipes recipe;
-	private final List< ItemStack > combinedInputs;
+	private final RRequirements requirements;
 
 
 	public RShapelessRecipeWrapper( final ShapelessRecipes recipe )
 	{
 		this.recipe = recipe;
+		this.requirements = new RRequirements( RShapelessRecipeWrapper.extractInputs( recipe ) );
+	}
 
-		this.combinedInputs = new ArrayList<>( );
-		OUTER: for ( final ItemStack invStack : this.recipe.recipeItems ) {
-			for ( final ItemStack ciStack : this.combinedInputs ) {
+
+	private static ArrayList< ItemStack > extractInputs( final ShapelessRecipes recipe )
+	{
+		final ArrayList< ItemStack > combinedInputs = new ArrayList<>( );
+		OUTER: for ( final ItemStack invStack : recipe.recipeItems ) {
+			for ( final ItemStack ciStack : combinedInputs ) {
 				if ( OreDictionary.itemMatches( ciStack , invStack , true ) ) {
 					ciStack.stackSize += invStack.stackSize;
 					continue OUTER;
 				}
 			}
-			this.combinedInputs.add( invStack.copy( ) );
+			combinedInputs.add( invStack.copy( ) );
 		}
+		return combinedInputs;
 	}
 
 
@@ -70,58 +76,23 @@ public class RShapelessRecipeWrapper
 	@Override
 	public void addInputsToDisplay( final IInventory displayInventory )
 	{
-		int i = 0;
-		for ( ItemStack stack : this.combinedInputs ) {
+		for ( int i = 0 ; i < this.requirements.size( ) ; i++ ) {
+			ItemStack stack = this.requirements.getItemTypes( i ).get( 0 );
 			if ( stack.getMetadata( ) == OreDictionary.WILDCARD_VALUE ) {
 				stack = new ItemStack( stack.getItem( ) , 1 , 0 );
 			} else {
 				stack = stack.copy( );
 			}
-			RShapelessRecipeWrapper.setSlot( displayInventory , i++ , stack );
+			stack.stackSize = this.requirements.getQuantity( i );
+			displayInventory.setInventorySlotContents( i , stack );
 		}
 	}
 
 
-	private static void setSlot( final IInventory inventory , final int index , ItemStack stack )
-	{
-		if ( stack.getMetadata( ) == OreDictionary.WILDCARD_VALUE ) {
-			stack = new ItemStack( stack.getItem( ) , 1 , 0 );
-		} else {
-			stack = stack.copy( );
-		}
-		inventory.setInventorySlotContents( index , stack );
-	}
-
-
 	@Override
-	public boolean canCraft( final IInventory inventory )
+	public I_RecipeRequirements getRequirements( )
 	{
-		final int nInputs = this.combinedInputs.size( );
-		final int nInvItems = inventory.getSizeInventory( );
-		for ( int i = 0 ; i < nInputs ; i++ ) {
-			final ItemStack rStack = this.combinedInputs.get( i );
-			int nLeft = rStack.stackSize;
-
-			for ( int j = 0 ; j < nInvItems && nLeft > 0 ; j++ ) {
-				final ItemStack invStack = inventory.getStackInSlot( j );
-				if ( OreDictionary.itemMatches( rStack , invStack , false ) ) {
-					nLeft -= invStack.stackSize;
-				}
-			}
-
-			if ( nLeft > 0 ) {
-				return false;
-			}
-		}
-		return true;
-	}
-
-
-	@Override
-	public void craft( final IInventory input , final IInventory output , final int quantity )
-	{
-		// TODO Auto-generated method stub
-
+		return this.requirements;
 	}
 
 }
diff --git a/src/java/mmm/tech/base/workbench/TBWBGui.java b/src/java/mmm/tech/base/workbench/TBWBGui.java
index fe8e25d..3384e32 100644
--- a/src/java/mmm/tech/base/workbench/TBWBGui.java
+++ b/src/java/mmm/tech/base/workbench/TBWBGui.java
@@ -8,14 +8,18 @@ import java.util.List;
 import mmm.Mmm;
 import mmm.core.CNetwork;
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.core.api.recipes.I_RecipeRequirements;
 import mmm.recipes.RCraftingWrappers;
 import mmm.utils.gui.A_UGContainerScreen;
 import mmm.utils.gui.UGArrowButton;
+import mmm.utils.gui.UGui;
+import mmm.utils.gui.UGui.E_Icon;
 import net.minecraft.client.gui.GuiButton;
 import net.minecraft.client.gui.GuiTextField;
 import net.minecraft.client.renderer.GlStateManager;
 import net.minecraft.client.resources.I18n;
 import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.tileentity.TileEntity;
 import net.minecraft.util.ResourceLocation;
 import net.minecraftforge.fml.relauncher.Side;
 import net.minecraftforge.fml.relauncher.SideOnly;
@@ -87,6 +91,18 @@ public class TBWBGui
 		this.mc.getTextureManager( ).bindTexture( TBWBGui.BACKGROUND );
 		this.drawTexturedModalRect( this.guiLeft , this.guiTop , 0 , 0 , this.xSize , this.ySize );
 
+		boolean canCraft;
+		if ( this.currentRecipe == -1 ) {
+			canCraft = false;
+		} else {
+			TileEntity te = this.container.world.getTileEntity( this.container.position );
+			I_RecipeRequirements requirements = this.recipes.get( this.currentRecipe ).getRequirements( );
+			canCraft = te instanceof TBWBTileEntity && requirements.checkInventory( ( (TBWBTileEntity) te ).storage );
+		}
+		if ( !canCraft ) {
+			UGui.drawIcon( this , this.guiLeft + 147 , this.guiTop + 51 , E_Icon.RED_CROSS );
+		}
+
 		GlStateManager.disableBlend( );
 		this.tfSearch.drawTextBox( );
 	}
diff --git a/src/java/mmm/utils/gui/UGui.java b/src/java/mmm/utils/gui/UGui.java
index 374b895..173be1f 100644
--- a/src/java/mmm/utils/gui/UGui.java
+++ b/src/java/mmm/utils/gui/UGui.java
@@ -1,12 +1,35 @@
 package mmm.utils.gui;
 
 
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
 import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
 
 
 
-public enum UGui {
-	INSTANCE;
+public class UGui
+{
+	public static enum E_Icon {
+		RED_CROSS( 26 , 30 , 13 , 13 ),
+		//
+		;
+
+		public final int x;
+		public final int y;
+		public final int width;
+		public final int height;
+
+
+		private E_Icon( final int x , final int y , final int w , final int h )
+		{
+			this.x = x;
+			this.y = y;
+			this.width = w;
+			this.height = h;
+		}
+	}
 
 	public static final int TAB_TEXTURE_X = 0;
 	public static final int TAB_TEXTURE_Y = 0;
@@ -26,4 +49,11 @@ public enum UGui {
 
 	public static final ResourceLocation GUI_TEXTURE = new ResourceLocation( "mmm" , "textures/gui/gui-common.png" );
 
+
+	@SideOnly( Side.CLIENT )
+	public static void drawIcon( final Gui gui , final int x , final int y , final E_Icon icon )
+	{
+		Minecraft.getMinecraft( ).getTextureManager( ).bindTexture( UGui.GUI_TEXTURE );
+		gui.drawTexturedModalRect( x , y , icon.x , icon.y , icon.width , icon.height );
+	}
 }
diff --git a/src/resources/assets/mmm/textures/gui/gui-common.png b/src/resources/assets/mmm/textures/gui/gui-common.png
index 37da863bcd2aa82c24868f38edd4f243a8d59d24..86a5fc4b3084782994b5799f0115174b46ac6f92 100644
GIT binary patch
delta 1006
zcmV<K0}=f53&;tOBYyw}VoOIv0RI600RN!9r;`8x010qNS#tmY4#5Bb4#5Gqk!$S$
z000McNliru;0F;0E)xpl1HS+O1XW2yK~#9!?cG0W8&MDkV4cR54Q_P`7e-YeT`K=8
zjR~X+1f0T+yPUvha7d@xH&U3zql`DZlAK6*<^4Vw93@L)0)O*+vrqfv$>fj44r;RF
zzyG!01JGfy3#|rRJUl$CtZdq!^To=_>kbbOn>si+XzJ+bsHuD{0N{<9KiuBl)+TJ4
z{Oamzad~;!)b;iCvQAG=Uj)GS`}yMY*Pl&&`Sz=+`1S1L{qnlw<Ktyz5JUjP>-Z`F
z;FSRoO}nj{&3|T1Wy3}QMB_&YoSmIDm4R?_a`GYoviYMT46;6a`mww&SNyu_*GWJC
zz$*e^o6X-B0MYazzUJy5A<&)xqVYqW0^(~s0AjVjySr;9fP8HNfPn@;toA8y1)|yk
z5DlMl3TWQ~<cc2w5M=^@JqmyfftUvBb#(%$A<#7eM1N%fMA-y{Jqm!Qr>C_OKwj4s
z0CfV0y1BVA0l+>4Ks0si0d#Ew)&R)nUpE2N<`)1k-~foGzPPyP-2=#5fNcJ*O~ASb
zP@6wD0tEmJEC6D~&y_y{AVZ+N2M`TkSN?b%8-aBbFdM!$zfC|G_ymy80MxevyC#78
z96;ACK!2?Awc+!v!2CS{00RqvX!N+>kIFxf-)jdzG<vM|S@Ak+((xcrj{SLC5Mdwy
zU|<1I8$N@eF92%Q5SX;y1*ogPO+XlE08E?)1OXrbV5GMIjswGp1Hf@$7;ykN4h$m>
zfc7&0@vu+Vao}wD41vD00CfV$hk`r@00W)?vVYbc2Fm8Ij|6ue2hN7iM}pk~z<>iF
zR{na>r|&p$JrdkE0n`r#sR3XU5C$3myFLzV0)PPrKz=M(4S=rWz?)70H3S3z3_Jkx
z6yT}}pzi+n1wei7zwQC#ZGixQfdxQ)?>|By1E9SJ(Dg1r-*MpjHlR-gf`1Hx$&P<A
z(0?wE1LsqL9t48_m;!eFlYtxuh7mM>n9t{b{bV4=fnmS_5KX(SavBi!C;+zE{OtfZ
zFSY8M$!n&X7v5I@JU&-nb8m7Du;&5L)!^&?9N6Okh<|9>)8v{TMi2m9A@HdAVLSn_
zDg-n?j35Av{V?JHI5z>nI0K+-<re}lf=dAC8~cR-yz>B9ch4^bU?c(1*W6P90Pi;d
zrW-W&bi?a_J=+5S1OOla0096906+i$Z_@oc41niP2nNO$0OcdXfuY%U)AyDCX_FBJ
c$`=6AZ``zJr`2JiV*mgE07*qoM6N<$f<ke%qyPW_

delta 1318
zcmV+>1=;$@3G)k(BYy?DNkl<Zc-rmVL2ncJ83yoYJeCJrX_cs7gHJ$jZExj<;0%Nl
zLfp7id#oxXr~nCZK{-cCuIaJ85hN}v#HB?@-3tehs$FfPSQFWC54$tijzd_LfDyl+
zgh;@445Rm%_y702c9C}``S^-ce*9nh9Dpg^rZ^{)k6)ifQGZOP3vg>|>%F(Xbm>x(
z<nO!he=fg2_tQh3J$p7ua&T~vB&n(@NmAE!lH}#fmyR%;5CErA{7G`>&Yj%4b!#RB
zo;-Py=I8bM{r0x6UcH*R@3-GR&G$bHlO$!?Ns^4mhk5z0f4+P2-ERB)Xf#S)*Qu&1
zgTWx<@z~LY4}a_cT6gc>{b&#V?Afziym&F&+uLozOeT{QMUl~Hl)b&Zbi3V5CX<Xt
zqnZ0Q#h)a<{#Dg}-K*YxJzeBY7tn+NaI(&675{O@<8kiazn>&oSy{=MGiOp1MSC`%
z_UAwSt__19|69+5LZd9ptgo-9*Xy-s|FSF{S@@*^@P8>Z#rg2z!wiSR42Q!kT7oof
ze-i>t7jW?N`o1~a@9*y?Nv>VH)_$-3hb0YwX%8MeXuE<=r!&_n9L{tC&Dno?2~btl
zOuK*m`t?**mAbCmjX+>Y10YGRUcH*KEOU5xxUd6gT>Rs27T$lp(9fSge<T=yB@BS)
z&!1;=bAK~Qj%)(X-){SF|5^BV(}n)#&717(>^Q3Mfx4fw_%}8-QWQVELO8z(*en5>
z;xEh2%(7tq>_1)nHUVKtJAi(_KXc}9HUQ`M0Lrp+<jlXe+Mn43Y+C*4;;-vE<MG%=
zAS`1542Q!!e*8FldwV%NJj`G)NWb4dvIj68AAh#RzqZ=Xczl@ee;Cee0+wZ&x~|*e
zpG+q0CLpkcOMus}U$^W1Uayz8Z{Hp%{v`SCyMND|?RyJ0^~U3Iy9dy;{msjQr*(BB
z2!Q{m<>++U^?q~p-lB_u=8OKIt`~+tbLHP?G)hqvcK=~n1E3l9IY~3@bCMS?UO0mA
zC4cP!0AFbbU>q2}@;w0K!0;6ZfN@~>iuVA<<8fQ;D=RCxa^=dxap1Mpe%4m|Z5aIF
zw}WHHfy=UN$AK?gxZpYfSk?e&3briE+`fIg?F!~i&;K_g!O7xr;Qjsm6h)Es_4Opl
zU@&lGVHuYI&Dy^y{-!H9ZX9^J_~(}Z(|;EMUA}yI=61ROSi%k<$xQJ#1-xh+c)s`-
zsq6Yk@lS^Xuw(&n<Hn66f3|2G_?y4{<HX0Rsw$5jJ#r-BO9_BZr<0pEZ_cho7K{V0
zt@iWRM^E2-_OI(Yy<RUH8yhoA3M0X=gaOd+_cIs_G8hbILSTLmVEPq+rV}`B9Dled
ziqv&I(+Ri^08Z9TQvCd;9G~Qqfri83OmS{*ZY~@Lo_@)1ZMENy2G5TJ@9*!Ys;cyQ
zy(G!b&Q8j*6aesn0g!y6PX<a(ZydN;LNw#RO&|d8Ujm%kCj;&7?jGqZnsMN6x0{25
zgZ2u(MS4}${yI43WxeL*zR_sZ27f}cEC5b!(PskS>C>kl?ZNNeyO$){-roM)|3=_F
zOMs7F{0b4ij3odd000312mn9;00ICI0Du4h1OOla0096906+i$0ss&IfB*ml03ZN>
z&mI7$^@gC+KE=-fmaqfZ+S>BJ3`?rm-QE2pr{{C$&UsT%00000000000F(LyxRL}4
c7?LFa1F|7j?B0P;J^%m!07*qoM6N<$f<GLW>Hq)$