diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java index bf0eb4539..ad1f08ac9 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java @@ -14,18 +14,17 @@ import dan200.computercraft.shared.turtle.upgrades.TurtleInventoryCrafting; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; import javax.annotation.Nonnull; -import java.util.ArrayList; +import java.util.List; public class TurtleCraftCommand implements ITurtleCommand { - private final int m_limit; + private final int limit; public TurtleCraftCommand( int limit ) { - m_limit = limit; + this.limit = limit; } @Nonnull @@ -34,32 +33,20 @@ public class TurtleCraftCommand implements ITurtleCommand { // Craft the item TurtleInventoryCrafting crafting = new TurtleInventoryCrafting( turtle ); - ArrayList results = crafting.doCrafting( turtle.getWorld(), m_limit ); - if( results != null ) + List results = crafting.doCrafting( turtle.getWorld(), limit ); + if( results == null ) return TurtleCommandResult.failure( "No matching recipes" ); + + // Store or drop any remainders + for( ItemStack stack : results ) { - // Store the results - for( ItemStack stack : results ) + ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); + if( !remainder.isEmpty() ) { - ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); - if( !remainder.isEmpty() ) - { - // Drop the remainder - BlockPos position = turtle.getPosition(); - WorldUtil.dropItemStack( remainder, turtle.getWorld(), position, turtle.getDirection() ); - } + WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), turtle.getDirection() ); } - - if( !results.isEmpty() ) - { - // Animate - turtle.playAnimation( TurtleAnimation.Wait ); - } - - // Succeed - return TurtleCommandResult.success(); } - // Fail - return TurtleCommandResult.failure( "No matching recipes" ); + if( !results.isEmpty() ) turtle.playAnimation( TurtleAnimation.Wait ); + return TurtleCommandResult.success(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java index 37136e170..40fc88fb2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleInventoryCrafting.java @@ -13,14 +13,20 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; import net.minecraft.util.NonNullList; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.world.World; import net.minecraft.world.WorldServer; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.fml.common.FMLCommonHandler; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; public class TurtleInventoryCrafting extends InventoryCrafting { @@ -28,16 +34,19 @@ public class TurtleInventoryCrafting extends InventoryCrafting private int m_xStart; private int m_yStart; + @SuppressWarnings( "ConstantConditions" ) public TurtleInventoryCrafting( ITurtleAccess turtle ) { + // Passing null in here is evil, but we don't have a container present. We override most methods in order to + // avoid throwing any NPEs. super( null, 0, 0 ); m_turtle = turtle; m_xStart = 0; m_yStart = 0; } - @Nonnull - private ItemStack tryCrafting( int xStart, int yStart ) + @Nullable + private IRecipe tryCrafting( int xStart, int yStart ) { m_xStart = xStart; m_yStart = yStart; @@ -52,118 +61,88 @@ public class TurtleInventoryCrafting extends InventoryCrafting { if( !m_turtle.getInventory().getStackInSlot( x + y * TileTurtle.INVENTORY_WIDTH ).isEmpty() ) { - return ItemStack.EMPTY; + return null; } } } } // Check the actual crafting - return CraftingManager.findMatchingResult( this, m_turtle.getWorld() ); + return CraftingManager.findMatchingRecipe( this, m_turtle.getWorld() ); } - public ArrayList doCrafting( World world, int maxCount ) + @Nullable + public List doCrafting( World world, int maxCount ) { - if( world.isRemote || !(world instanceof WorldServer) ) - { - return null; - } + if( world.isRemote || !(world instanceof WorldServer) ) return null; // Find out what we can craft - ItemStack result = tryCrafting( 0, 0 ); - if( result.isEmpty() ) - { - result = tryCrafting( 0, 1 ); - } - if( result.isEmpty() ) - { - result = tryCrafting( 1, 0 ); - } - if( result.isEmpty() ) - { - result = tryCrafting( 1, 1 ); - } + IRecipe recipe = tryCrafting( 0, 0 ); + if( recipe == null ) recipe = tryCrafting( 0, 1 ); + if( recipe == null ) recipe = tryCrafting( 1, 0 ); + if( recipe == null ) recipe = tryCrafting( 1, 1 ); + if( recipe == null ) return null; - // Craft it - if( !result.isEmpty() ) + // Special case: craft(0) just returns an empty list if crafting was possible + if( maxCount == 0 ) return Collections.emptyList(); + + TurtlePlayer player = TurtlePlayer.get( m_turtle ); + + ArrayList results = new ArrayList<>(); + for( int i = 0; i < maxCount && recipe.matches( this, world ); i++ ) { - // Special case: craft(0) just returns an empty list if crafting was possible - ArrayList results = new ArrayList<>(); - if( maxCount == 0 ) - { - return results; - } - - // Find out how many we can craft - int numToCraft = 1; - int size = getSizeInventory(); - if( maxCount > 1 ) - { - int minStackSize = 0; - for( int n = 0; n < size; n++ ) - { - ItemStack stack = getStackInSlot( n ); - if( !stack.isEmpty() && (minStackSize == 0 || minStackSize > stack.getCount()) ) - { - minStackSize = stack.getCount(); - } - } - - if( minStackSize > 1 ) - { - numToCraft = Math.min( minStackSize, result.getMaxStackSize() / result.getCount() ); - numToCraft = Math.min( numToCraft, maxCount ); - result.setCount( result.getCount() * numToCraft ); - } - } - - // Do post-pickup stuff - TurtlePlayer turtlePlayer = TurtlePlayer.get( m_turtle ); - result.onCrafting( world, turtlePlayer, numToCraft ); + ItemStack result = recipe.getCraftingResult( this ); + if( result.isEmpty() ) break; results.add( result ); - // Consume resources from the inventory - NonNullList remainingItems = CraftingManager.getRemainingItems( this, world ); - for( int n = 0; n < size; n++ ) - { - ItemStack stack = getStackInSlot( n ); - if( !stack.isEmpty() ) - { - decrStackSize( n, numToCraft ); + result.onCrafting( world, player, result.getCount() ); + FMLCommonHandler.instance().firePlayerCraftingEvent( player, result, this ); - ItemStack replacement = remainingItems.get( n ); - if( !replacement.isEmpty() ) - { - if( !(replacement.isItemStackDamageable() && replacement.getItemDamage() >= replacement.getMaxDamage()) ) - { - replacement.setCount( Math.min( numToCraft, replacement.getMaxStackSize() ) ); - if( getStackInSlot( n ).isEmpty() ) - { - setInventorySlotContents( n, replacement ); - } - else - { - results.add( replacement ); - } - } - } + ForgeHooks.setCraftingPlayer( player ); + NonNullList remainders = recipe.getRemainingItems( this ); + ForgeHooks.setCraftingPlayer( null ); + + for( int slot = 0; slot < remainders.size(); slot++ ) + { + ItemStack existing = getStackInSlot( slot ); + ItemStack remainder = remainders.get( slot ); + + if( !existing.isEmpty() ) + { + decrStackSize( slot, 1 ); + existing = getStackInSlot( slot ); + } + + if( remainder.isEmpty() ) continue; + + // Either update the current stack or add it to the remainder list (to be inserted into the inventory + // afterwards). + if( existing.isEmpty() ) + { + setInventorySlotContents( slot, remainder ); + } + else if( ItemStack.areItemsEqual( existing, remainder ) && ItemStack.areItemStackTagsEqual( existing, remainder ) ) + { + remainder.grow( existing.getCount() ); + setInventorySlotContents( slot, remainder ); + } + else + { + results.add( remainder ); } } - return results; } - return null; + return results; } @Nonnull @Override public ItemStack getStackInRowAndColumn( int x, int y ) { - if( x >= 0 && x < getWidth() && y >= 0 && y < getHeight() ) - { - return getStackInSlot( x + y * getWidth() ); - } - return ItemStack.EMPTY; + return x >= 0 && x < getWidth() && y >= 0 && y < getHeight() + ? getStackInSlot( x + y * getWidth() ) + : ItemStack.EMPTY; } @Override @@ -182,12 +161,9 @@ public class TurtleInventoryCrafting extends InventoryCrafting { int x = m_xStart + index % getWidth(); int y = m_yStart + index / getHeight(); - if( x >= 0 && x < TileTurtle.INVENTORY_WIDTH && - y >= 0 && y < TileTurtle.INVENTORY_HEIGHT ) - { - return x + y * TileTurtle.INVENTORY_WIDTH; - } - return -1; + return x >= 0 && x < TileTurtle.INVENTORY_WIDTH && y >= 0 && y < TileTurtle.INVENTORY_HEIGHT + ? x + y * TileTurtle.INVENTORY_WIDTH + : -1; } // IInventory implementation