/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.container.implementations; import java.util.ArrayList; import java.util.List; import java.util.Optional; import appeng.container.slot.*; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.IContainerListener; import net.minecraft.inventory.InventoryCraftResult; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.inventory.Slot; import net.minecraft.inventory.SlotCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.wrapper.PlayerInvWrapper; import appeng.api.AEApi; import appeng.api.config.Actionable; import appeng.api.definitions.IDefinitions; import appeng.api.storage.IMEMonitor; import appeng.api.storage.ITerminalHost; import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.container.ContainerNull; import appeng.container.guisync.GuiSync; import appeng.core.sync.packets.PacketPatternSlot; import appeng.helpers.IContainerCraftingPacket; import appeng.items.storage.ItemViewCell; import appeng.me.helpers.MachineSource; import appeng.parts.reporting.PartPatternTerminal; import appeng.tile.inventory.AppEngInternalInventory; import appeng.util.InventoryAdaptor; import appeng.util.Platform; import appeng.util.inv.AdaptorItemHandler; import appeng.util.inv.IAEAppEngInventory; import appeng.util.inv.InvOperation; import appeng.util.inv.WrapperCursorItemHandler; import appeng.util.item.AEItemStack; public class ContainerPatternTerm extends ContainerMEMonitorable implements IAEAppEngInventory, IOptionalSlotHost, IContainerCraftingPacket { private final PartPatternTerminal patternTerminal; private final AppEngInternalInventory cOut = new AppEngInternalInventory( null, 1 ); private final IItemHandler crafting; private final SlotFakeCraftingMatrix[] craftingSlots = new SlotFakeCraftingMatrix[9]; private final OptionalSlotFake[] outputSlots = new OptionalSlotFake[3]; private final SlotPatternTerm craftSlot; private final SlotRestrictedInput patternSlotIN; private final SlotRestrictedInput patternSlotOUT; private IRecipe currentRecipe; @GuiSync( 97 ) public boolean craftingMode = true; @GuiSync( 96 ) public boolean substitute = false; public ContainerPatternTerm( final InventoryPlayer ip, final ITerminalHost monitorable ) { super( ip, monitorable, false ); this.patternTerminal = (PartPatternTerminal) monitorable; final IItemHandler patternInv = this.getPatternTerminal().getInventoryByName( "pattern" ); final IItemHandler output = this.getPatternTerminal().getInventoryByName( "output" ); this.crafting = this.getPatternTerminal().getInventoryByName( "crafting" ); for( int y = 0; y < 3; y++ ) { for( int x = 0; x < 3; x++ ) { this.addSlotToContainer( this.craftingSlots[x + y * 3] = new SlotFakeCraftingMatrix( this.crafting, x + y * 3, 18 + x * 18, -76 + y * 18 ) ); } } this.addSlotToContainer( this.craftSlot = new SlotPatternTerm( ip.player, this.getActionSource(), this .getPowerSource(), monitorable, this.crafting, patternInv, this.cOut, 110, -76 + 18, this, 2, this ) ); this.craftSlot.setIIcon( -1 ); for( int y = 0; y < 3; y++ ) { this.addSlotToContainer( this.outputSlots[y] = new SlotPatternOutputs( output, this, y, 110, -76 + y * 18, 0, 0, 1 ) ); this.outputSlots[y].setRenderDisabled( false ); this.outputSlots[y].setIIcon( -1 ); } this.addSlotToContainer( this.patternSlotIN = new SlotRestrictedInput( SlotRestrictedInput.PlacableItemType.BLANK_PATTERN, patternInv, 0, 147, -72 - 9, this .getInventoryPlayer() ) ); this.addSlotToContainer( this.patternSlotOUT = new SlotRestrictedInput( SlotRestrictedInput.PlacableItemType.ENCODED_PATTERN, patternInv, 1, 147, -72 + 34, this .getInventoryPlayer() ) ); this.patternSlotOUT.setStackLimit( 1 ); this.bindPlayerInventory( ip, 0, 0 ); this.updateOrderOfOutputSlots(); } @Override public ItemStack transferStackInSlot( final EntityPlayer p, final int idx ) { if( Platform.isClient() ) { return ItemStack.EMPTY; } final AppEngSlot clickSlot = ( AppEngSlot ) this.inventorySlots.get( idx ); // require AE SLots! ItemStack itemStack = clickSlot.getStack(); if( AEApi.instance().definitions().materials().blankPattern().isSameAs( itemStack ) ) { IItemHandler patternInv = this.getPatternTerminal().getInventoryByName( "pattern" ); ItemStack remainder = patternInv.insertItem( 0, itemStack, false ); clickSlot.putStack( remainder ); } return super.transferStackInSlot( p, idx ); } private void updateOrderOfOutputSlots() { if( !this.isCraftingMode() ) { this.craftSlot.xPos = -9000; for( int y = 0; y < 3; y++ ) { this.outputSlots[y].xPos = this.outputSlots[y].getX(); } } else { this.craftSlot.xPos = this.craftSlot.getX(); for( int y = 0; y < 3; y++ ) { this.outputSlots[y].xPos = -9000; } } } @Override public void putStackInSlot( int slotID, ItemStack stack ) { super.putStackInSlot( slotID, stack ); this.getAndUpdateOutput(); } private ItemStack getAndUpdateOutput() { final World world = this.getPlayerInv().player.world; final InventoryCrafting ic = new InventoryCrafting( this, 3, 3 ); for( int x = 0; x < ic.getSizeInventory(); x++ ) { ic.setInventorySlotContents( x, this.crafting.getStackInSlot( x ) ); } if( this.currentRecipe == null || !this.currentRecipe.matches( ic, world ) ) { this.currentRecipe = CraftingManager.findMatchingRecipe( ic, world ); } final ItemStack is; if( this.currentRecipe == null ) { is = ItemStack.EMPTY; } else { is = this.currentRecipe.getCraftingResult( ic ); } this.cOut.setStackInSlot( 0, is ); return is; } @Override public void saveChanges() { } @Override public void onChangeInventory( final IItemHandler inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack ) { } public void encodeAndMoveToInventory() { encode(); ItemStack output = this.patternSlotOUT.getStack(); if ( !output.isEmpty() ) { if (!getPlayerInv().addItemStackToInventory( output )){ getPlayerInv().player.dropItem( output , false ); } this.patternSlotOUT.putStack( ItemStack.EMPTY ); } } public void encode() { ItemStack output = this.patternSlotOUT.getStack(); final ItemStack[] in = this.getInputs(); final ItemStack[] out = this.getOutputs(); // if there is no input, this would be silly. if( in == null || out == null ) { return; } // first check the output slots, should either be null, or a pattern if( !output.isEmpty() && !this.isPattern( output ) ) { return; } // if nothing is there we should snag a new pattern. else if( output.isEmpty() ) { output = this.patternSlotIN.getStack(); if( output.isEmpty() || !this.isPattern( output ) ) { return; // no blanks. } // remove one, and clear the input slot. output.setCount( output.getCount() - 1 ); if( output.getCount() == 0 ) { this.patternSlotIN.putStack( ItemStack.EMPTY ); } // add a new encoded pattern. Optional maybePattern = AEApi.instance().definitions().items().encodedPattern().maybeStack( 1 ); if( maybePattern.isPresent() ) { output = maybePattern.get(); this.patternSlotOUT.putStack( output ); } } // encode the slot. final NBTTagCompound encodedValue = new NBTTagCompound(); final NBTTagList tagIn = new NBTTagList(); final NBTTagList tagOut = new NBTTagList(); for( final ItemStack i : in ) { tagIn.appendTag( this.createItemTag( i ) ); } for( final ItemStack i : out ) { tagOut.appendTag( this.createItemTag( i ) ); } encodedValue.setTag( "in", tagIn ); encodedValue.setTag( "out", tagOut ); encodedValue.setBoolean( "crafting", this.isCraftingMode() ); encodedValue.setBoolean( "substitute", this.isSubstitute() ); output.setTagCompound( encodedValue ); } public void multiply(int multiple) { ItemStack[] input = new ItemStack[9]; boolean canMultiplyInputs = true; boolean canMultiplyOutputs = true; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() && input[x].getCount() * multiple > input[x].getMaxStackSize() ) { canMultiplyInputs = false; } } for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); if( !out.isEmpty() && out.getCount() * multiple > out.getMaxStackSize() ) { canMultiplyOutputs = false; } } if( canMultiplyInputs && canMultiplyOutputs ) { for( SlotFakeCraftingMatrix craftingSlot : this.craftingSlots ) { ItemStack stack = craftingSlot.getStack(); if( !stack.isEmpty() ) craftingSlot.getStack().setCount( stack.getCount() * multiple ); } for( OptionalSlotFake outputSlot : this.outputSlots ) { ItemStack stack = outputSlot.getStack(); if( !stack.isEmpty() ) outputSlot.getStack().setCount( stack.getCount() * multiple ); } } } public void divide(int divide) { ItemStack[] input = new ItemStack[9]; boolean canDivideInputs = true; boolean canDivideOutputs = true; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() && input[x].getCount() % divide != 0 ) { canDivideInputs = false; } } for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); if( !out.isEmpty() && out.getCount() % divide != 0 ) { canDivideOutputs = false; } } if( canDivideInputs && canDivideOutputs ) { for( SlotFakeCraftingMatrix craftingSlot : this.craftingSlots ) { ItemStack stack = craftingSlot.getStack(); if( !stack.isEmpty() ) craftingSlot.getStack().setCount( stack.getCount() / divide ); } for( OptionalSlotFake outputSlot : this.outputSlots ) { ItemStack stack = outputSlot.getStack(); if( !stack.isEmpty() ) outputSlot.getStack().setCount( stack.getCount() / divide ); } } } public void increase(int increase) { ItemStack[] input = new ItemStack[9]; boolean canIncreaseInputs = true; boolean canIncreaseOutputs = true; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() && input[x].getCount() + increase > input[x].getMaxStackSize() ) { canIncreaseInputs = false; } } for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); if( !out.isEmpty() && out.getCount() + increase > out.getMaxStackSize() ) { canIncreaseOutputs = false; } } if( canIncreaseInputs && canIncreaseOutputs ) { for( SlotFakeCraftingMatrix craftingSlot : this.craftingSlots ) { ItemStack stack = craftingSlot.getStack(); if( !stack.isEmpty() ) craftingSlot.getStack().setCount( stack.getCount() + increase ); } for( OptionalSlotFake outputSlot : this.outputSlots ) { ItemStack stack = outputSlot.getStack(); if( !stack.isEmpty() ) outputSlot.getStack().setCount( stack.getCount() + increase ); } } } public void decrease(int decrease) { ItemStack[] input = new ItemStack[9]; boolean canDecreaseInputs = true; boolean canDecreaseOutputs = true; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() && input[x].getCount() - decrease < 1 ) { canDecreaseInputs = false; } } for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); if( !out.isEmpty() && out.getCount() - decrease < 1 ) { canDecreaseOutputs = false; } } if( canDecreaseInputs && canDecreaseOutputs ) { for( SlotFakeCraftingMatrix craftingSlot : this.craftingSlots ) { ItemStack stack = craftingSlot.getStack(); if( !stack.isEmpty() ) craftingSlot.getStack().setCount( stack.getCount() - decrease ); } for( OptionalSlotFake outputSlot : this.outputSlots ) { ItemStack stack = outputSlot.getStack(); if( !stack.isEmpty() ) outputSlot.getStack().setCount( stack.getCount() - decrease ); } } } public void maximizeCount() { ItemStack[] input = new ItemStack[9]; boolean canGrowInputs = true; boolean canGrowOutputs = true; int maxInputStackGrowth = 0; int maxOutputStackGrowth = 0; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() && input[x].getMaxStackSize() - input[x].getCount() > maxInputStackGrowth ) { maxInputStackGrowth = input[x].getMaxStackSize() - input[x].getCount(); } if( !input[x].isEmpty() && input[x].getCount() + maxInputStackGrowth > input[x].getMaxStackSize() ) { canGrowInputs = false; } } for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); { maxOutputStackGrowth = out.getMaxStackSize() - out.getCount(); } if( !out.isEmpty() && out.getCount() + maxOutputStackGrowth > out.getMaxStackSize() ) { canGrowOutputs = false; } } if( canGrowInputs && canGrowOutputs ) { int maxStackGrowth = Math.min(maxInputStackGrowth,maxOutputStackGrowth); for( SlotFakeCraftingMatrix craftingSlot : this.craftingSlots ) { ItemStack stack = craftingSlot.getStack(); if( !stack.isEmpty() ) craftingSlot.getStack().setCount( stack.getCount() + maxStackGrowth ); } for( OptionalSlotFake outputSlot : this.outputSlots ) { ItemStack stack = outputSlot.getStack(); if( !stack.isEmpty() ) outputSlot.getStack().setCount( stack.getCount() + maxStackGrowth ); } } } private ItemStack[] getInputs() { final ItemStack[] input = new ItemStack[9]; boolean hasValue = false; for( int x = 0; x < this.craftingSlots.length; x++ ) { input[x] = this.craftingSlots[x].getStack(); if( !input[x].isEmpty() ) { hasValue = true; } } if( hasValue ) { return input; } return null; } private ItemStack[] getOutputs() { if( this.isCraftingMode() ) { final ItemStack out = this.getAndUpdateOutput(); if( !out.isEmpty() && out.getCount() > 0 ) { return new ItemStack[] { out }; } } else { final List list = new ArrayList<>( 3 ); boolean hasValue = false; for( final OptionalSlotFake outputSlot : this.outputSlots ) { final ItemStack out = outputSlot.getStack(); if( !out.isEmpty() && out.getCount() > 0 ) { list.add( out ); hasValue = true; } } if( hasValue ) { return list.toArray( new ItemStack[list.size()] ); } } return null; } private boolean isPattern( final ItemStack output ) { if( output.isEmpty() ) { return false; } final IDefinitions definitions = AEApi.instance().definitions(); boolean isPattern = definitions.items().encodedPattern().isSameAs( output ); isPattern |= definitions.materials().blankPattern().isSameAs( output ); return isPattern; } private NBTBase createItemTag( final ItemStack i ) { final NBTTagCompound c = new NBTTagCompound(); if( !i.isEmpty() ) { i.writeToNBT( c ); } return c; } @Override public boolean isSlotEnabled( final int idx ) { if( idx == 1 ) { return Platform.isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode(); } else if( idx == 2 ) { return Platform.isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode(); } else { return false; } } public void craftOrGetItem( final PacketPatternSlot packetPatternSlot ) { if( packetPatternSlot.slotItem != null && this.getCellInventory() != null ) { final IAEItemStack out = packetPatternSlot.slotItem.copy(); InventoryAdaptor inv = new AdaptorItemHandler( new WrapperCursorItemHandler( this.getPlayerInv().player.inventory ) ); final InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor( this.getPlayerInv().player ); if( packetPatternSlot.shift ) { inv = playerInv; } if( !inv.simulateAdd( out.createItemStack() ).isEmpty() ) { return; } final IAEItemStack extracted = Platform.poweredExtraction( this.getPowerSource(), this.getCellInventory(), out, this.getActionSource() ); final EntityPlayer p = this.getPlayerInv().player; if( extracted != null ) { inv.addItems( extracted.createItemStack() ); if( p instanceof EntityPlayerMP ) { this.updateHeld( (EntityPlayerMP) p ); } this.detectAndSendChanges(); return; } final InventoryCrafting ic = new InventoryCrafting( new ContainerNull(), 3, 3 ); final InventoryCrafting real = new InventoryCrafting( new ContainerNull(), 3, 3 ); for( int x = 0; x < 9; x++ ) { ic.setInventorySlotContents( x, packetPatternSlot.pattern[x] == null ? ItemStack.EMPTY : packetPatternSlot.pattern[x].createItemStack() ); } final IRecipe r = CraftingManager.findMatchingRecipe( ic, p.world ); if( r == null ) { return; } final IMEMonitor storage = this.getPatternTerminal() .getInventory( AEApi.instance().storage().getStorageChannel( IItemStorageChannel.class ) ); final IItemList all = storage.getStorageList(); final ItemStack is = r.getCraftingResult( ic ); for( int x = 0; x < ic.getSizeInventory(); x++ ) { if( !ic.getStackInSlot( x ).isEmpty() ) { final ItemStack pulled = Platform.extractItemsByRecipe( this.getPowerSource(), this.getActionSource(), storage, p.world, r, is, ic, ic.getStackInSlot( x ), x, all, Actionable.MODULATE, ItemViewCell.createFilter( this.getViewCells() ) ); real.setInventorySlotContents( x, pulled ); } } final IRecipe rr = CraftingManager.findMatchingRecipe( real, p.world ); if( rr == r && Platform.itemComparisons().isSameItem( rr.getCraftingResult( real ), is ) ) { final InventoryCraftResult craftingResult = new InventoryCraftResult(); craftingResult.setRecipeUsed( rr ); final SlotCrafting sc = new SlotCrafting( p, real, craftingResult, 0, 0, 0 ); sc.onTake( p, is ); for( int x = 0; x < real.getSizeInventory(); x++ ) { final ItemStack failed = playerInv.addItems( real.getStackInSlot( x ) ); if( !failed.isEmpty() ) { p.dropItem( failed, false ); } } inv.addItems( is ); if( p instanceof EntityPlayerMP ) { this.updateHeld( (EntityPlayerMP) p ); } this.detectAndSendChanges(); } else { for( int x = 0; x < real.getSizeInventory(); x++ ) { final ItemStack failed = real.getStackInSlot( x ); if( !failed.isEmpty() ) { this.getCellInventory() .injectItems( AEItemStack.fromItemStack( failed ), Actionable.MODULATE, new MachineSource( this.getPatternTerminal() ) ); } } } } } @Override public void detectAndSendChanges() { super.detectAndSendChanges(); if( Platform.isServer() ) { if( this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe() ) { this.setCraftingMode( this.getPatternTerminal().isCraftingRecipe() ); this.updateOrderOfOutputSlots(); } this.substitute = this.patternTerminal.isSubstitution(); } } @Override public void onUpdate( final String field, final Object oldValue, final Object newValue ) { super.onUpdate( field, oldValue, newValue ); if( field.equals( "craftingMode" ) ) { this.getAndUpdateOutput(); this.updateOrderOfOutputSlots(); } } @Override public void onSlotChange( final Slot s ) { if( s == this.patternSlotOUT && Platform.isServer() ) { for( final IContainerListener listener : this.listeners ) { for( final Slot slot : this.inventorySlots ) { if( slot instanceof OptionalSlotFake || slot instanceof SlotFakeCraftingMatrix ) { listener.sendSlotContents( this, slot.slotNumber, slot.getStack() ); } } if( listener instanceof EntityPlayerMP ) { ( (EntityPlayerMP) listener ).isChangingQuantityOnly = false; } } this.detectAndSendChanges(); } if( s == this.craftSlot && Platform.isClient() ) { this.getAndUpdateOutput(); } } public void clear() { for( final Slot s : this.craftingSlots ) { s.putStack( ItemStack.EMPTY ); } for( final Slot s : this.outputSlots ) { s.putStack( ItemStack.EMPTY ); } this.detectAndSendChanges(); this.getAndUpdateOutput(); } @Override public IItemHandler getInventoryByName( final String name ) { if( name.equals( "player" ) ) { return new PlayerInvWrapper( this.getInventoryPlayer() ); } return this.getPatternTerminal().getInventoryByName( name ); } @Override public boolean useRealItems() { return false; } public void toggleSubstitute() { this.substitute = !this.substitute; this.detectAndSendChanges(); this.getAndUpdateOutput(); } public boolean isCraftingMode() { return this.craftingMode; } private void setCraftingMode( final boolean craftingMode ) { this.craftingMode = craftingMode; } public PartPatternTerminal getPatternTerminal() { return this.patternTerminal; } private boolean isSubstitute() { return this.substitute; } public void setSubstitute( final boolean substitute ) { this.substitute = substitute; } }