/**
* Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
* <p/>
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.factory;
import java.lang.ref.WeakReference;
import java.util.List;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.InventoryCraftResult;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.inventory.SlotCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.util.ForgeDirection;
import buildcraft.api.core.IInvSlot;
import buildcraft.api.power.IRedstoneEngine;
import buildcraft.api.power.IRedstoneEngineReceiver;
import buildcraft.api.tiles.IDebuggable;
import buildcraft.api.tiles.IHasWork;
import buildcraft.core.lib.RFBattery;
import buildcraft.core.lib.block.TileBuildCraft;
import buildcraft.core.lib.gui.ContainerDummy;
import buildcraft.core.lib.inventory.InvUtils;
import buildcraft.core.lib.inventory.InventoryConcatenator;
import buildcraft.core.lib.inventory.InventoryIterator;
import buildcraft.core.lib.inventory.SimpleInventory;
import buildcraft.core.lib.inventory.StackHelper;
import buildcraft.core.lib.utils.CraftingUtils;
import buildcraft.core.lib.utils.Utils;
import buildcraft.core.proxy.CoreProxy;
public class TileAutoWorkbench extends TileBuildCraft implements ISidedInventory, IHasWork, IRedstoneEngineReceiver, IDebuggable {
public static final int SLOT_RESULT = 9;
public static final int CRAFT_TIME = 256;
public static final int UPDATE_TIME = 16;
private static final int[] SLOTS = Utils.createSlotArray(0, 10);
public int progress = 0;
public LocalInventoryCrafting craftMatrix = new LocalInventoryCrafting();
private SimpleInventory resultInv = new SimpleInventory(1, "Auto Workbench", 64);
private SimpleInventory inputInv = new SimpleInventory(9, "Auto Workbench", 64) {
@Override
public void setInventorySlotContents(int slotId, ItemStack itemstack) {
super.setInventorySlotContents(slotId, itemstack);
if (craftMatrix.isInputMissing && getStackInSlot(slotId) != null) {
craftMatrix.isInputMissing = false;
}
}
@Override
public void markDirty() {
super.markDirty();
craftMatrix.isInputMissing = false;
}
};
private IInventory inv = InventoryConcatenator.make().add(inputInv).add(resultInv).add(craftMatrix);
private SlotCrafting craftSlot;
private InventoryCraftResult craftResult = new InventoryCraftResult();
private int[] bindings = new int[9];
private int[] bindingCounts = new int[9];
private int update = Utils.RANDOM.nextInt();
private boolean hasWork = false;
private boolean scheduledCacheRebuild = false;
public TileAutoWorkbench() {
super();
this.setBattery(new RFBattery(16, 16, 0));
}
@Override
public boolean hasWork() {
return hasWork;
}
@Override
public boolean canConnectRedstoneEngine(ForgeDirection side) {
return true;
}
@Override
public boolean canConnectEnergy(ForgeDirection side) {
TileEntity tile = worldObj.getTileEntity(xCoord + side.offsetX, yCoord + side.offsetY, zCoord + side.offsetZ);
return tile instanceof IRedstoneEngine;
}
@Override
public void getDebugInfo(List<String> info, ForgeDirection side, ItemStack debugger, EntityPlayer player) {
info.add("isInputMissing = " + craftMatrix.isInputMissing);
info.add("isOutputJammed = " + craftMatrix.isOutputJammed);
}
public class LocalInventoryCrafting extends InventoryCrafting {
public IRecipe currentRecipe;
public boolean useBindings, isOutputJammed, isInputMissing;
public LocalInventoryCrafting() {
super(new ContainerDummy(), 3, 3);
}
@Override
public ItemStack getStackInSlot(int slot) {
if (useBindings) {
if (slot >= 0 && slot < 9 && bindings[slot] >= 0) {
return inputInv.getStackInSlot(bindings[slot]);
} else {
return null;
}
} else {
return super.getStackInSlot(slot);
}
}
public ItemStack getRecipeOutput() {
currentRecipe = findRecipe(); // Fixes repair recipe handling (why is it not dynamic?)
if (currentRecipe == null) {
return null;
}
ItemStack result = currentRecipe.getCraftingResult(this);
if (result != null) {
result = result.copy();
}
return result;
}
private IRecipe findRecipe() {
for (IInvSlot slot : InventoryIterator.getIterable(this, ForgeDirection.UP)) {
ItemStack stack = slot.getStackInSlot();
if (stack == null) {
continue;
}
if (stack.getItem().hasContainerItem(stack)) {
return null;
}
}
return CraftingUtils.findMatchingRecipe(craftMatrix, worldObj);
}
public void rebuildCache() {
currentRecipe = findRecipe();
hasWork = currentRecipe != null && currentRecipe.getRecipeOutput() != null;
ItemStack result = getRecipeOutput();
ItemStack resultInto = resultInv.getStackInSlot(0);
if (resultInto != null && (
!StackHelper.canStacksMerge(resultInto, result)
|| resultInto.stackSize + result.stackSize > resultInto.getMaxStackSize())
) {
isOutputJammed = true;
} else {
isOutputJammed = false;
}
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
if (useBindings) {
if (slot >= 0 && slot < 9 && bindings[slot] >= 0) {
inputInv.setInventorySlotContents(bindings[slot], stack);
}
return;
}
super.setInventorySlotContents(slot, stack);
scheduledCacheRebuild = true;
}
@Override
public void markDirty() {
super.markDirty();
scheduledCacheRebuild = true;
}
@Override
public ItemStack decrStackSize(int slot, int amount) {
if (useBindings) {
if (slot >= 0 && slot < 9 && bindings[slot] >= 0) {
return inputInv.decrStackSize(bindings[slot], amount);
} else {
return null;
}
}
scheduledCacheRebuild = true;
return decrStackSize(slot, amount);
}
public void setUseBindings(boolean use) {
useBindings = use;
}
}
public WeakReference<EntityPlayer> getInternalPlayer() {
return CoreProxy.proxy.getBuildCraftPlayer((WorldServer) worldObj, xCoord, yCoord + 1, zCoord);
}
@Override
public void markDirty() {
super.markDirty();
inv.markDirty();
}
@Override
public int getSizeInventory() {
return 10;
}
@Override
public ItemStack getStackInSlot(int slot) {
return inv.getStackInSlot(slot);
}
@Override
public ItemStack decrStackSize(int slot, int count) {
return inv.decrStackSize(slot, count);
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
inv.setInventorySlotContents(slot, stack);
}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
return inv.getStackInSlotOnClosing(slot);
}
@Override
public String getInventoryName() {
return "";
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return worldObj.getTileEntity(xCoord, yCoord, zCoord) == this && player.getDistanceSq(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D) <= 64.0D;
}
@Override
public void readFromNBT(NBTTagCompound data) {
super.readFromNBT(data);
resultInv.readFromNBT(data);
if (data.hasKey("input")) {
InvUtils.readInvFromNBT(inputInv, "input", data);
InvUtils.readInvFromNBT(craftMatrix, "matrix", data);
} else {
InvUtils.readInvFromNBT(inputInv, "matrix", data);
for (int i = 0; i < 9; i++) {
ItemStack inputStack = inputInv.getStackInSlot(i);
if (inputStack != null) {
ItemStack matrixStack = inputStack.copy();
matrixStack.stackSize = 1;
craftMatrix.setInventorySlotContents(i, matrixStack);
}
}
}
craftMatrix.rebuildCache();
}
@Override
public void writeToNBT(NBTTagCompound data) {
super.writeToNBT(data);
resultInv.writeToNBT(data);
InvUtils.writeInvToNBT(inputInv, "input", data);
InvUtils.writeInvToNBT(craftMatrix, "matrix", data);
}
@Override
public boolean canUpdate() {
return true;
}
@Override
public void updateEntity() {
super.updateEntity();
if (worldObj.isRemote) {
return;
}
if (scheduledCacheRebuild) {
craftMatrix.rebuildCache();
scheduledCacheRebuild = false;
}
if (craftMatrix.isOutputJammed || craftMatrix.isInputMissing || craftMatrix.currentRecipe == null) {
progress = 0;
return;
}
if (craftSlot == null) {
craftSlot = new SlotCrafting(getInternalPlayer().get(), craftMatrix, craftResult, 0, 0, 0);
}
if (!hasWork) {
return;
}
int updateNext = update + getBattery().getEnergyStored() + 1;
int updateThreshold = (update & ~15) + 16;
update = Math.min(updateThreshold, updateNext);
if ((update % UPDATE_TIME) == 0) {
updateCrafting();
}
getBattery().setEnergy(0);
}
public int getProgressScaled(int i) {
return (progress * i) / CRAFT_TIME;
}
/**
* Increment craft job, find recipes, produce output
*/
private void updateCrafting() {
progress += UPDATE_TIME;
for (int i = 0; i < 9; i++) {
bindingCounts[i] = 0;
}
for (int i = 0; i < 9; i++) {
ItemStack comparedStack = craftMatrix.getStackInSlot(i);
if (comparedStack == null || comparedStack.getItem() == null) {
bindings[i] = -1;
continue;
}
if (bindings[i] == -1 || !StackHelper.isMatchingItem(inputInv.getStackInSlot(bindings[i]), comparedStack, true, true)) {
boolean found = false;
for (int j = 0; j < 9; j++) {
if (j == bindings[i]) {
continue;
}
ItemStack inputInvStack = inputInv.getStackInSlot(j);
if (StackHelper.isMatchingItem(inputInvStack, comparedStack, true, false)
&& inputInvStack.stackSize > bindingCounts[j]) {
found = true;
bindings[i] = j;
bindingCounts[j]++;
break;
}
}
if (!found) {
craftMatrix.isInputMissing = true;
progress = 0;
return;
}
} else {
bindingCounts[bindings[i]]++;
}
}
for (int i = 0; i < 9; i++) {
if (bindingCounts[i] > 0) {
ItemStack stack = inputInv.getStackInSlot(i);
if (stack != null && stack.stackSize < bindingCounts[i]) {
// Do not break progress yet, instead give it a chance to rebuild
// It will quit when trying to find a valid binding to "fit in"
for (int j = 0; j < 9; j++) {
if (bindings[j] == i) {
bindings[j] = -1;
}
}
return;
}
}
}
if (progress < CRAFT_TIME) {
return;
}
progress = 0;
craftMatrix.setUseBindings(true);
ItemStack result = craftMatrix.getRecipeOutput();
if (result != null && result.stackSize > 0) {
ItemStack resultInto = resultInv.getStackInSlot(0);
craftSlot.onPickupFromSlot(getInternalPlayer().get(), result);
if (resultInto == null) {
resultInv.setInventorySlotContents(0, result);
} else {
resultInto.stackSize += result.stackSize;
}
}
craftMatrix.setUseBindings(false);
craftMatrix.rebuildCache();
}
@Override
public void openInventory() {
}
@Override
public void closeInventory() {
}
@Override
public boolean isItemValidForSlot(int slot, ItemStack stack) {
if (slot == SLOT_RESULT) {
return false;
}
if (stack.getItem().hasContainerItem(stack)) {
return false;
}
return true;
}
@Override
public int[] getAccessibleSlotsFromSide(int var1) {
return SLOTS;
}
@Override
public boolean canInsertItem(int slot, ItemStack stack, int side) {
if (slot >= 9) {
return false;
}
ItemStack slotStack = inv.getStackInSlot(slot);
if (StackHelper.canStacksMerge(stack, slotStack)) {
return true;
}
for (int i = 0; i < 9; i++) {
ItemStack inputStack = craftMatrix.getStackInSlot(i);
if (inputStack != null && StackHelper.isMatchingItem(inputStack, stack, true, false)) {
return true;
}
}
return false;
}
@Override
public boolean canExtractItem(int slot, ItemStack stack, int side) {
return slot == SLOT_RESULT;
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
}