From f121e8bd3d89100cf10e150d4322cd586d5eeb10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Sun, 17 Jul 2016 18:28:45 +0200
Subject: [PATCH] Workbench - Actual crafting! YAY!

---
 .../api/recipes/I_RecipeRequirements.java     |   8 +
 src/java/mmm/recipes/RRequirements.java       |  53 ++++++-
 .../tech/base/workbench/TBWBContainer.java    | 138 ++++++++++++++++--
 .../tech/base/workbench/TBWBCraftingSlot.java |  87 +++++++++++
 src/java/mmm/utils/UAchievements.java         |  52 +++++++
 src/java/mmm/utils/gui/UGSlotGroups.java      |  12 ++
 6 files changed, 338 insertions(+), 12 deletions(-)
 create mode 100644 src/java/mmm/tech/base/workbench/TBWBCraftingSlot.java
 create mode 100644 src/java/mmm/utils/UAchievements.java

diff --git a/src/java/mmm/core/api/recipes/I_RecipeRequirements.java b/src/java/mmm/core/api/recipes/I_RecipeRequirements.java
index f365b01..12e578b 100644
--- a/src/java/mmm/core/api/recipes/I_RecipeRequirements.java
+++ b/src/java/mmm/core/api/recipes/I_RecipeRequirements.java
@@ -25,4 +25,12 @@ public interface I_RecipeRequirements
 
 	boolean checkInventory( IInventory inventory );
 
+
+	boolean checkInventory( IInventory inventory , int amount );
+
+
+	public int getMaxOutput( IInventory inventory );
+
+
+	void removeFromInventory( IInventory inventory, int amount );
 }
\ No newline at end of file
diff --git a/src/java/mmm/recipes/RRequirements.java b/src/java/mmm/recipes/RRequirements.java
index ee8ee14..0520f32 100644
--- a/src/java/mmm/recipes/RRequirements.java
+++ b/src/java/mmm/recipes/RRequirements.java
@@ -90,10 +90,17 @@ class RRequirements
 
 	@Override
 	public boolean checkInventory( final IInventory inventory )
+	{
+		return this.checkInventory( inventory , 1 );
+	}
+
+
+	@Override
+	public boolean checkInventory( final IInventory inventory , final int amount )
 	{
 		final int nInvItems = inventory.getSizeInventory( );
 		for ( int i = 0 ; i < this.quantities.length ; i++ ) {
-			int nLeft = this.quantities[ i ];
+			int nLeft = this.quantities[ i ] * amount;
 
 			for ( int j = 0 ; j < nInvItems && nLeft > 0 ; j++ ) {
 				final ItemStack invStack = inventory.getStackInSlot( j );
@@ -108,4 +115,48 @@ class RRequirements
 		}
 		return true;
 	}
+
+
+	@Override
+	public int getMaxOutput( final IInventory inventory )
+	{
+		final int nInvItems = inventory.getSizeInventory( );
+		int maxQuantity = Integer.MAX_VALUE;
+		for ( int i = 0 ; i < this.quantities.length && maxQuantity > 0 ; i++ ) {
+			int nFound = 0;
+
+			for ( int j = 0 ; j < nInvItems ; j++ ) {
+				final ItemStack invStack = inventory.getStackInSlot( j );
+				if ( this.checkItemStack( i , invStack ) ) {
+					nFound += invStack.stackSize;
+				}
+			}
+
+			maxQuantity = Math.min( maxQuantity , nFound / this.quantities[ i ] );
+		}
+		return maxQuantity;
+	}
+
+
+	@Override
+	public void removeFromInventory( final IInventory inventory , final int amount )
+	{
+		final int nInvItems = inventory.getSizeInventory( );
+		for ( int i = 0 ; i < this.quantities.length ; i++ ) {
+			int nLeft = this.quantities[ i ] * amount;
+			for ( int j = 0 ; j < nInvItems && nLeft > 0 ; j++ ) {
+				final ItemStack invStack = inventory.getStackInSlot( j );
+				if ( this.checkItemStack( i , invStack ) ) {
+					final int used = Math.min( nLeft , invStack.stackSize );
+					nLeft -= used;
+					if ( invStack.stackSize == used ) {
+						inventory.setInventorySlotContents( j , invStack.getItem( ).getContainerItem( invStack ) );
+					} else {
+						invStack.stackSize -= used;
+					}
+				}
+			}
+			assert nLeft == 0;
+		}
+	}
 }
diff --git a/src/java/mmm/tech/base/workbench/TBWBContainer.java b/src/java/mmm/tech/base/workbench/TBWBContainer.java
index 7887522..eb3b349 100644
--- a/src/java/mmm/tech/base/workbench/TBWBContainer.java
+++ b/src/java/mmm/tech/base/workbench/TBWBContainer.java
@@ -1,14 +1,21 @@
 package mmm.tech.base.workbench;
 
 
+import javax.annotation.Nullable;
+
 import mmm.MmmTech;
 import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.recipes.RCraftingWrappers;
 import mmm.utils.UInventoryDisplay;
 import mmm.utils.gui.UGContainer;
 import mmm.utils.gui.UGSlotDisplay;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.InventoryCraftResult;
 import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.World;
 
@@ -20,7 +27,9 @@ public class TBWBContainer
 	public final TBWBTileEntity tileEntity;
 	public final World world;
 	public final BlockPos position;
-	public final UInventoryDisplay recipe;
+	public final UInventoryDisplay recipeDisplay;
+	public final InventoryCraftResult recipeOutput;
+	private I_CraftingRecipeWrapper recipeWrapper;
 
 
 	public TBWBContainer( final InventoryPlayer playerInv , final TBWBTileEntity tileEntity )
@@ -35,12 +44,19 @@ public class TBWBContainer
 		this.addGrid( Slot::new , tileEntity.storage , 8 , 15 );
 
 		this.slotGroups.nextGroup( );
-		this.recipe = new UInventoryDisplay( "Recipe" , 10 );
-		this.addGrid( UGSlotDisplay::new , this.recipe , //
+		this.recipeDisplay = new UInventoryDisplay( "Recipe" , 10 );
+		this.addGrid( UGSlotDisplay::new , this.recipeDisplay , //
 				3 , 3 , 0 , 86 , 32 );
-		this.addSlotToContainer( new UGSlotDisplay( this.recipe , 9 , 174 , 50 ) );
+
+		this.slotGroups.nextGroup( );
+		this.recipeOutput = new InventoryCraftResult( );
+		this.addSlotToContainer( new TBWBCraftingSlot( playerInv.player , this , //
+				this.recipeOutput , 0 , 174 , 50 ) );
 
 		this.slotGroups.endGroups( );
+
+		this.recipeWrapper = RCraftingWrappers.RECIPES.get( 0 );
+		this.onRecipeChanged( );
 	}
 
 
@@ -53,20 +69,120 @@ public class TBWBContainer
 	}
 
 
-	public void setCurrentRecipe( final I_CraftingRecipeWrapper wrapper , final boolean setDefault )
+	@Nullable
+	public ItemStack transferStackInSlot( EntityPlayer playerIn , int index )
 	{
-		this.recipe.clear( );
-		if ( wrapper == null ) {
-			// XXX log if confirm is set
-			return;
+		Slot slot = (Slot) this.inventorySlots.get( index );
+		if ( slot == null || !slot.getHasStack( ) ) {
+			return null;
 		}
 
-		wrapper.addInputsToDisplay( this.recipe );
-		this.recipe.setInventorySlotContents( 9 , wrapper.getOutput( ) );
+		ItemStack slotStack = slot.getStack( );
+		ItemStack outStack = slotStack.copy( );
+
+		int group = this.slotGroups.getGroup( index );
+		if ( group == 3 ) {
+			// Craft as many as possible
+			if ( this.recipeWrapper == null ) {
+				return null;
+			}
+			IInventory storage = this.getStorage( );
+			if ( storage == null ) {
+				return null;
+			}
+
+			// Can we?
+			if ( !this.recipeWrapper.getRequirements( ).checkInventory( storage ) ) {
+				return null;
+			}
+
+			// Merge it and remove ingredients
+			if ( !this.mergeItemStack( outStack , 0 , 36 , false ) ) {
+				return null;
+			}
+			( (TBWBCraftingSlot) slot ).handleCrafting( 1 );
+
+			// Drop any items that didn't fit in the inventory
+			if ( outStack.stackSize != 0 ) {
+				playerIn.dropItem( outStack , false );
+				return null;
+			}
+
+			return slotStack;
+
+		}
+
+		if ( group == 0 ) {
+			// Player inventory to storage
+			if ( !this.mergeItemStack( slotStack , 36 , 51 , false ) ) {
+				return null;
+			}
+		} else if ( group == 1 ) {
+			// Storage to player inventory
+			if ( !this.mergeItemStack( slotStack , 0 , 36 , false ) ) {
+				return null;
+			}
+		} else {
+			return null;
+		}
+
+		if ( slotStack.stackSize == 0 ) {
+			slot.putStack( (ItemStack) null );
+		} else {
+			slot.onSlotChanged( );
+		}
+
+		if ( slotStack.stackSize == outStack.stackSize ) {
+			return null;
+		}
+		slot.onPickupFromSlot( playerIn , slotStack );
+		return outStack;
+	}
+
+
+	public boolean canMergeSlot( ItemStack stack , Slot slotIn )
+	{
+		return slotIn.inventory != this.recipeOutput && super.canMergeSlot( stack , slotIn );
+	}
+
+
+	public void setCurrentRecipe( final I_CraftingRecipeWrapper wrapper , final boolean setDefault )
+	{
+		this.recipeWrapper = wrapper;
+
+		onRecipeChanged( );
 
 		if ( setDefault ) {
 			// this.tileEntity.setDefaultRecipe( wrapper.getIdentifier( ) );
 		}
 	}
 
+
+	private void onRecipeChanged( )
+	{
+		this.recipeDisplay.clear( );
+		if ( this.recipeWrapper == null ) {
+			// XXX log if confirm is set
+			return;
+		}
+		this.recipeWrapper.addInputsToDisplay( this.recipeDisplay );
+		this.recipeOutput.setInventorySlotContents( 0 , this.recipeWrapper.getOutput( ) );
+	}
+
+
+	public IInventory getStorage( )
+	{
+		TileEntity te = this.world.getTileEntity( position );
+		if ( te instanceof TBWBTileEntity ) {
+			return ( (TBWBTileEntity) te ).storage;
+		}
+		return null;
+	}
+
+
+	public I_CraftingRecipeWrapper getCurrentRecipe( )
+	{
+		return this.recipeWrapper;
+	}
+
 }
diff --git a/src/java/mmm/tech/base/workbench/TBWBCraftingSlot.java b/src/java/mmm/tech/base/workbench/TBWBCraftingSlot.java
new file mode 100644
index 0000000..b4c0836
--- /dev/null
+++ b/src/java/mmm/tech/base/workbench/TBWBCraftingSlot.java
@@ -0,0 +1,87 @@
+package mmm.tech.base.workbench;
+
+
+import javax.annotation.Nullable;
+
+import mmm.core.api.recipes.I_CraftingRecipeWrapper;
+import mmm.utils.UAchievements;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fml.common.FMLCommonHandler;
+
+
+
+public class TBWBCraftingSlot
+		extends Slot
+{
+
+	private final EntityPlayer player;
+	private final TBWBContainer container;
+
+
+	public TBWBCraftingSlot( final EntityPlayer player , final TBWBContainer container , final IInventory inventory ,
+			final int index , final int x , final int y )
+	{
+		super( inventory , index , x , y );
+		this.player = player;
+		this.container = container;
+	}
+
+
+	@Override
+	public boolean isItemValid( @Nullable final ItemStack stack )
+	{
+		return false;
+	}
+
+
+	@Override
+	public ItemStack decrStackSize( final int amount )
+	{
+		if ( !this.getHasStack( ) ) {
+			return null;
+		}
+
+		final I_CraftingRecipeWrapper wrapper = this.container.getCurrentRecipe( );
+		if ( wrapper == null ) {
+			return null;
+		}
+
+		final ItemStack stack = this.getStack( );
+		final int qt = Math.max( amount - amount % stack.stackSize , stack.stackSize ) / stack.stackSize;
+		final IInventory storage = this.container.getStorage( );
+		if ( storage == null || !wrapper.getRequirements( ).checkInventory( storage , qt ) ) {
+			return null;
+		}
+		return this.handleCrafting( qt );
+	}
+
+
+	public ItemStack handleCrafting( final int quantity )
+	{
+		if ( !this.getHasStack( ) ) {
+			return null;
+		}
+
+		final I_CraftingRecipeWrapper wrapper = this.container.getCurrentRecipe( );
+		if ( wrapper == null ) {
+			return null;
+		}
+
+		final IInventory storage = this.container.getStorage( );
+		if ( storage == null ) {
+			return null;
+		}
+
+		final ItemStack stack = this.getStack( ).copy( );
+		stack.stackSize *= quantity;
+
+		FMLCommonHandler.instance( ).firePlayerCraftingEvent( this.player , stack , this.inventory );
+		stack.onCrafting( this.player.worldObj , this.player , quantity );
+		UAchievements.checkCraftingAchievements( this.player , stack.getItem( ) );
+		wrapper.getRequirements( ).removeFromInventory( storage , quantity );
+		return stack;
+	}
+}
diff --git a/src/java/mmm/utils/UAchievements.java b/src/java/mmm/utils/UAchievements.java
new file mode 100644
index 0000000..0d63af3
--- /dev/null
+++ b/src/java/mmm/utils/UAchievements.java
@@ -0,0 +1,52 @@
+package mmm.utils;
+
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemHoe;
+import net.minecraft.item.ItemPickaxe;
+import net.minecraft.item.ItemSword;
+import net.minecraft.stats.AchievementList;
+
+
+
+public class UAchievements
+{
+
+	public static void checkCraftingAchievements( final EntityPlayer playerIn , final Item item )
+	{
+		if ( item == Item.getItemFromBlock( Blocks.CRAFTING_TABLE ) ) {
+			playerIn.addStat( AchievementList.BUILD_WORK_BENCH );
+
+		} else if ( item instanceof ItemPickaxe ) {
+			playerIn.addStat( AchievementList.BUILD_PICKAXE );
+			if ( ( (ItemPickaxe) item ).getToolMaterial( ) != Item.ToolMaterial.WOOD ) {
+				playerIn.addStat( AchievementList.BUILD_BETTER_PICKAXE );
+			}
+
+		} else if ( item == Item.getItemFromBlock( Blocks.FURNACE ) ) {
+			playerIn.addStat( AchievementList.BUILD_FURNACE );
+
+		} else if ( item instanceof ItemHoe ) {
+			playerIn.addStat( AchievementList.BUILD_HOE );
+
+		} else if ( item == Items.BREAD ) {
+			playerIn.addStat( AchievementList.MAKE_BREAD );
+
+		} else if ( item == Items.CAKE ) {
+			playerIn.addStat( AchievementList.BAKE_CAKE );
+
+		} else if ( item instanceof ItemSword ) {
+			playerIn.addStat( AchievementList.BUILD_SWORD );
+
+		} else if ( item == Item.getItemFromBlock( Blocks.ENCHANTING_TABLE ) ) {
+			playerIn.addStat( AchievementList.ENCHANTMENTS );
+
+		} else if ( item == Item.getItemFromBlock( Blocks.BOOKSHELF ) ) {
+			playerIn.addStat( AchievementList.BOOKCASE );
+		}
+	}
+
+}
diff --git a/src/java/mmm/utils/gui/UGSlotGroups.java b/src/java/mmm/utils/gui/UGSlotGroups.java
index c5ec2aa..505c7a1 100644
--- a/src/java/mmm/utils/gui/UGSlotGroups.java
+++ b/src/java/mmm/utils/gui/UGSlotGroups.java
@@ -100,4 +100,16 @@ public class UGSlotGroups
 
 		return this;
 	}
+
+
+	public int getGroup( int slotIndex )
+	{
+		int nGroups = this.firstSlots.size( );
+		for ( int i = 1 ; i < nGroups ; i++ ) {
+			if ( slotIndex < this.firstSlots.getInt( i ) ) {
+				return i - 1;
+			}
+		}
+		return nGroups - 1;
+	}
 }