diff --git a/helpers/PatternHelper.java b/helpers/PatternHelper.java index 1b561f0d5..3a75dcdd0 100644 --- a/helpers/PatternHelper.java +++ b/helpers/PatternHelper.java @@ -1,12 +1,18 @@ package appeng.helpers; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; +import appeng.api.AEApi; import appeng.api.crafting.ICraftingPatternDetails; import appeng.api.storage.data.IAEItemStack; import appeng.container.ContainerNull; @@ -21,8 +27,80 @@ public class PatternHelper implements ICraftingPatternDetails ItemStack correctOutput; IRecipe standardRecipe; + IAEItemStack inputs[]; + IAEItemStack outputs[]; + boolean isCrafting = false; + class TestLookup + { + + final int slot; + final int ref; + final int hash; + + public TestLookup(int slot, ItemStack i) { + this( slot, i.getItem(), i.getItemDamage() ); + } + + public TestLookup(int slot, Item item, int dmg) { + this.slot = slot; + ref = (dmg << Platform.DEF_OFFSET) | (Item.getIdFromItem( item ) & 0xffff); + int offset = 3 * slot; + hash = (ref << offset) | (ref >> (offset + 32)); + } + + @Override + public int hashCode() + { + return hash; + } + + @Override + public boolean equals(Object obj) + { + TestLookup b = (TestLookup) obj; + return b.slot == slot && b.ref == ref; + } + + }; + + enum TestStatus + { + ACCEPT, DECLINE, TEST + }; + + HashSet failCache = new HashSet(); + HashSet passCache = new HashSet(); + + private void markItemAs(int slotIndex, ItemStack i, TestStatus b) + { + if ( b == TestStatus.TEST ||i.hasTagCompound()) + return; + + (b == TestStatus.ACCEPT ? passCache : failCache).add( new TestLookup( slotIndex, i ) ); + } + + private TestStatus getStatus(int slotIndex, ItemStack i) + { + if ( crafting.getStackInSlot( slotIndex ) == null ) + return i == null ? TestStatus.ACCEPT : TestStatus.DECLINE; + + if ( i == null ) + return TestStatus.DECLINE; + + if ( i.hasTagCompound() ) + return TestStatus.TEST; + + if ( passCache.contains( new TestLookup( slotIndex, i ) ) ) + return TestStatus.ACCEPT; + + if ( failCache.contains( new TestLookup( slotIndex, i ) ) ) + return TestStatus.DECLINE; + + return TestStatus.TEST; + } + public PatternHelper(ItemStack is, World w) { NBTTagCompound encodedValue = is.getTagCompound(); @@ -36,29 +114,76 @@ public class PatternHelper implements ICraftingPatternDetails if ( isCrafting == false ) throw new RuntimeException( "Only crafting recipes supported." ); + List in = new ArrayList(); + List out = new ArrayList(); + for (int x = 0; x < inTag.tagCount(); x++) { ItemStack gs = ItemStack.loadItemStackFromNBT( inTag.getCompoundTagAt( x ) ); crafting.setInventorySlotContents( x, gs ); + + if ( gs != null && (!isCrafting || !gs.hasTagCompound()) ) + { + markItemAs( x, gs, TestStatus.ACCEPT ); + in.add( AEApi.instance().storage().createItemStack( gs ) ); + } + testFrame.setInventorySlotContents( x, gs ); } standardRecipe = Platform.findMatchingRecipe( crafting, w ); correctOutput = standardRecipe.getCraftingResult( crafting ); + + if ( isCrafting ) + { + out.add( AEApi.instance().storage().createItemStack( correctOutput ) ); + } + else + { + for (int x = 0; x < outTag.tagCount(); x++) + { + ItemStack gs = ItemStack.loadItemStackFromNBT( inTag.getCompoundTagAt( x ) ); + if ( gs != null ) + out.add( AEApi.instance().storage().createItemStack( gs ) ); + } + } + + outputs = out.toArray( new IAEItemStack[out.size()] ); + inputs = in.toArray( new IAEItemStack[in.size()] ); + } public boolean isValidItemForSlot(int slotIndex, ItemStack i, World w) { - testFrame.setInventorySlotContents( slotIndex, i ); - boolean result = standardRecipe.matches( testFrame, w ); + TestStatus result = getStatus( slotIndex, i ); - if ( result ) + switch (result) + { + case ACCEPT: + return true; + case DECLINE: + return false; + case TEST: + default: + break; + } + + if ( !isCrafting ) + return false; + + for (int x = 0; x < crafting.getSizeInventory(); x++) + testFrame.setInventorySlotContents( x, crafting.getStackInSlot( x ) ); + + testFrame.setInventorySlotContents( slotIndex, i ); + + if ( standardRecipe.matches( testFrame, w ) ) { ItemStack testOutput = standardRecipe.getCraftingResult( testFrame ); if ( Platform.isSameItemPrecise( correctOutput, testOutput ) ) { testFrame.setInventorySlotContents( slotIndex, crafting.getStackInSlot( slotIndex ) ); + markItemAs( slotIndex, i, TestStatus.ACCEPT ); return true; } } @@ -69,13 +194,30 @@ public class PatternHelper implements ICraftingPatternDetails if ( Platform.isSameItemPrecise( correctOutput, testOutput ) ) { testFrame.setInventorySlotContents( slotIndex, crafting.getStackInSlot( slotIndex ) ); + markItemAs( slotIndex, i, TestStatus.ACCEPT ); return true; } } + markItemAs( slotIndex, i, TestStatus.DECLINE ); return false; } + @Override + public ItemStack getOutput(InventoryCrafting craftingInv, World w) + { + for (int x = 0; x < craftingInv.getSizeInventory(); x++) + { + if ( !isValidItemForSlot( x, craftingInv.getStackInSlot( x ), w ) ) + return null; + } + + if ( outputs != null && outputs.length > 0 ) + return outputs[0].getItemStack(); + + return null; + } + @Override public boolean canSubstitute() { @@ -86,21 +228,19 @@ public class PatternHelper implements ICraftingPatternDetails @Override public boolean isCraftable() { - // TODO Auto-generated method stub - return false; + return isCrafting; } @Override public IAEItemStack[] getInputs() { - // TODO Auto-generated method stub - return null; + return inputs; } @Override public IAEItemStack[] getOutputs() { - // TODO Auto-generated method stub - return null; + return outputs; } + } diff --git a/tile/crafting/TileMolecularAssembler.java b/tile/crafting/TileMolecularAssembler.java index df0e21d52..647edfdb1 100644 --- a/tile/crafting/TileMolecularAssembler.java +++ b/tile/crafting/TileMolecularAssembler.java @@ -2,17 +2,28 @@ package appeng.tile.crafting; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; +import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import appeng.api.AEApi; import appeng.api.config.RedstoneMode; import appeng.api.config.Settings; import appeng.api.config.Upgrades; +import appeng.api.crafting.ICraftingPatternDetails; import appeng.api.implementations.IUpgradeableHost; +import appeng.api.networking.IGridNode; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; import appeng.api.util.AECableType; import appeng.api.util.DimensionalCoord; import appeng.api.util.IConfigManager; +import appeng.container.ContainerNull; +import appeng.items.misc.ItemEncodedPattern; +import appeng.me.GridAccessException; import appeng.parts.automation.UpgradeInventory; import appeng.tile.events.AETileEventHandler; import appeng.tile.events.TileEventType; @@ -22,17 +33,75 @@ import appeng.tile.inventory.IAEAppEngInventory; import appeng.tile.inventory.InvOperation; import appeng.util.ConfigManager; import appeng.util.IConfigManagerHost; +import appeng.util.InventoryAdaptor; +import appeng.util.Platform; -public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEngInventory, ISidedInventory, IUpgradeableHost, IConfigManagerHost +public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEngInventory, ISidedInventory, IUpgradeableHost, IConfigManagerHost, + IGridTickable { static final int[] sides = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; static final ItemStack is = AEApi.instance().blocks().blockMolecularAssembler.stack( 1 ); + private InventoryCrafting craftingInv = new InventoryCrafting( new ContainerNull(), 3, 3 ); private AppEngInternalInventory inv = new AppEngInternalInventory( this, 9 + 2 ); private IConfigManager settings = new ConfigManager( this ); private UpgradeInventory upgrades = new UpgradeInventory( is, this, getUpgradeSlots() ); + private ForgeDirection pushDirection = ForgeDirection.UNKNOWN; + private ItemStack myPattern = null; + private ICraftingPatternDetails myPlan = null; + private int progress = 0; + private boolean isAwake = false; + + public boolean pushPattern(ItemStack pattern, ICraftingPatternDetails patternDetails, InventoryCrafting table, ForgeDirection where) + { + return false; + } + + private void recalculatePlan() + { + ItemStack is = inv.getStackInSlot( 10 ); + + boolean wasEnabled = isAwake; + + if ( is != null && is.getItem() instanceof ItemEncodedPattern ) + { + if ( !Platform.isSameItem( is, myPattern ) ) + { + World w = getWorldObj(); + ItemEncodedPattern iep = (ItemEncodedPattern) is.getItem(); + ICraftingPatternDetails ph = iep.getPatternForItem( is, w ); + + progress = 0; + myPattern = is; + myPlan = ph; + } + } + else + { + progress = 0; + myPlan = null; + myPattern = null; + } + + isAwake = myPlan != null && hasMats(); + if ( wasEnabled != isAwake ) + { + try + { + if ( isAwake ) + gridProxy.getTick().wakeDevice( gridProxy.getNode() ); + else + gridProxy.getTick().sleepDevice( gridProxy.getNode() ); + } + catch (GridAccessException e) + { + // :P + } + } + } + @Override public int getInstalledUpgrades(Upgrades u) { @@ -65,6 +134,7 @@ public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEn upgrades.readFromNBT( data, "upgrades" ); inv.readFromNBT( data, "inv" ); settings.readFromNBT( data ); + recalculatePlan(); } }; @@ -82,16 +152,14 @@ public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEn return false; if ( hasPattern() ) - { - - } + return myPlan.isValidItemForSlot( i, itemstack, getWorldObj() ); return false; } private boolean hasPattern() { - return false; + return myPlan != null && inv.getStackInSlot( 10 ) != null; } @Override @@ -109,7 +177,8 @@ public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEn @Override public void onChangeInventory(IInventory inv, int slot, InvOperation mc, ItemStack removed, ItemStack added) { - + if ( inv == this.inv ) + recalculatePlan(); } @Override @@ -154,9 +223,99 @@ public class TileMolecularAssembler extends AENetworkInvTile implements IAEAppEn } - public int getCraftingProgress() + @Override + public int getInventoryStackLimit() { - return 0; + return 1; } + public int getCraftingProgress() + { + return progress; + } + + @Override + public TickingRequest getTickingRequest(IGridNode node) + { + return new TickingRequest( 1, 5, isAwake = hasPattern() && hasMats(), false ); + } + + private boolean hasMats() + { + if ( myPlan == null ) + return false; + + for (int x = 0; x < craftingInv.getSizeInventory(); x++) + craftingInv.setInventorySlotContents( x, inv.getStackInSlot( x ) ); + + return myPlan.getOutput( craftingInv, getWorldObj() ) != null; + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int TicksSinceLastCall) + { + if ( inv.getStackInSlot( 9 ) != null ) + { + pushOut( inv.getStackInSlot( 9 ) ); + return inv.getStackInSlot( 9 ) == null ? TickRateModulation.SLEEP : TickRateModulation.IDLE; + } + + if ( myPlan == null ) + { + isAwake = false; + return TickRateModulation.SLEEP; + } + + progress += TicksSinceLastCall; + + if ( progress >= 100 ) + { + for (int x = 0; x < craftingInv.getSizeInventory(); x++) + craftingInv.setInventorySlotContents( x, inv.getStackInSlot( x ) ); + + ItemStack output = myPlan.getOutput( craftingInv, getWorldObj() ); + if ( output != null ) + { + for (int x = 0; x < craftingInv.getSizeInventory(); x++) + inv.setInventorySlotContents( x, null ); + + pushOut( output.copy() ); + isAwake = false; + return inv.getStackInSlot( 9 ) == null ? TickRateModulation.SLEEP : TickRateModulation.IDLE; + } + } + + return TickRateModulation.FASTER; + } + + private void pushOut(ItemStack output) + { + if ( pushDirection == ForgeDirection.UNKNOWN ) + { + for (ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) + output = pushTo( output, d ); + } + else + output = pushTo( output, pushDirection ); + + inv.setInventorySlotContents( 9, output ); + } + + private ItemStack pushTo(ItemStack output, ForgeDirection d) + { + if ( output == null ) + return output; + + TileEntity te = getWorldObj().getTileEntity( xCoord + d.offsetX, yCoord + d.offsetY, zCoord + d.offsetZ ); + + if ( te == null ) + return output; + + InventoryAdaptor adaptor = InventoryAdaptor.getAdaptor( te, d.getOpposite() ); + + if ( adaptor == null ) + return output; + + return adaptor.addItems( output ); + } }