package mcjty.rftools.blocks.crafter;
import mcjty.lib.container.InventoryHelper;
import mcjty.lib.entity.GenericEnergyReceiverTileEntity;
import mcjty.lib.network.Argument;
import mcjty.lib.varia.BlockTools;
import mcjty.lib.varia.Logging;
import mcjty.rftools.blocks.RedstoneMode;
import mcjty.rftools.items.storage.StorageFilterCache;
import mcjty.rftools.items.storage.StorageFilterItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.oredict.OreDictionary;
import java.util.HashMap;
import java.util.Map;
public class CrafterBaseTE extends GenericEnergyReceiverTileEntity implements ISidedInventory {
public static final int SPEED_SLOW = 0;
public static final int SPEED_FAST = 1;
public static final String CMD_MODE = "mode";
public static final String CMD_REMEMBER = "remember";
public static final String CMD_FORGET = "forget";
private InventoryHelper inventoryHelper = new InventoryHelper(this, CrafterContainer.factory,
10 + CrafterContainer.BUFFER_SIZE + CrafterContainer.BUFFEROUT_SIZE + 1);
private ItemStack[] ghostSlots = new ItemStack[CrafterContainer.BUFFER_SIZE + CrafterContainer.BUFFEROUT_SIZE];
private CraftingRecipe recipes[];
private int supportedRecipes;
private StorageFilterCache filterCache = null;
private RedstoneMode redstoneMode = RedstoneMode.REDSTONE_IGNORED;
private int speedMode = SPEED_SLOW;
private InventoryCrafting workInventory = new InventoryCrafting(new Container() {
@Override
public boolean canInteractWith(EntityPlayer var1) {
return false;
}
}, 3, 3);
public CrafterBaseTE() {
super(CrafterConfiguration.MAXENERGY, CrafterConfiguration.RECEIVEPERTICK);
setSupportedRecipes(8);
}
public ItemStack[] getGhostSlots() {
return ghostSlots;
}
public void setSupportedRecipes(int supportedRecipes) {
this.supportedRecipes = supportedRecipes;
recipes = new CraftingRecipe[supportedRecipes];
for (int i = 0 ; i < recipes.length ; i++) {
recipes[i] = new CraftingRecipe();
}
}
public int getSupportedRecipes() {
return supportedRecipes;
}
public RedstoneMode getRedstoneMode() {
return redstoneMode;
}
public void setRedstoneMode(RedstoneMode redstoneMode) {
this.redstoneMode = redstoneMode;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
public int getSpeedMode() {
return speedMode;
}
public void setSpeedMode(int speedMode) {
this.speedMode = speedMode;
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
public CraftingRecipe getRecipe(int index) {
return recipes[index];
}
@Override
public int getSizeInventory() {
return inventoryHelper.getCount();
}
@Override
public ItemStack getStackInSlot(int index) {
return inventoryHelper.getStackInSlot(index);
}
@Override
public ItemStack decrStackSize(int index, int amount) {
return inventoryHelper.decrStackSize(index, amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int index) {
return null;
}
@Override
public void setInventorySlotContents(int index, ItemStack stack) {
if (index == CrafterContainer.SLOT_FILTER_MODULE) {
filterCache = null;
}
inventoryHelper.setInventorySlotContents(getInventoryStackLimit(), index, stack);
}
@Override
public String getInventoryName() {
return "Crafter Inventory";
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return canPlayerAccess(player);
}
@Override
public void openInventory() {
}
@Override
public void closeInventory() {
}
private void getFilterCache() {
if (filterCache == null) {
filterCache = StorageFilterItem.getCache(inventoryHelper.getStackInSlot(CrafterContainer.SLOT_FILTER_MODULE));
}
}
@Override
public boolean isItemValidForSlot(int index, ItemStack stack) {
if (index >= CrafterContainer.SLOT_BUFFER && index < CrafterContainer.SLOT_BUFFEROUT) {
ItemStack ghostSlot = ghostSlots[index - CrafterContainer.SLOT_BUFFER];
if (ghostSlot != null) {
if (!ghostSlot.isItemEqual(stack)) {
return false;
}
}
if (inventoryHelper.containsItem(CrafterContainer.SLOT_FILTER_MODULE)) {
getFilterCache();
if (filterCache != null) {
return filterCache.match(stack);
}
}
} else if (index >= CrafterContainer.SLOT_BUFFEROUT && index < CrafterContainer.SLOT_FILTER_MODULE) {
ItemStack ghostSlot = ghostSlots[index - CrafterContainer.SLOT_BUFFEROUT + CrafterContainer.BUFFER_SIZE];
if (ghostSlot != null) {
if (!ghostSlot.isItemEqual(stack)) {
return false;
}
}
}
return true;
}
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return CrafterContainer.factory.getAccessibleSlots();
}
@Override
public boolean canInsertItem(int index, ItemStack item, int side) {
if (!isItemValidForSlot(index, item)) {
return false;
}
return CrafterContainer.factory.isInputSlot(index);
}
@Override
public boolean canExtractItem(int index, ItemStack item, int side) {
return CrafterContainer.factory.isOutputSlot(index);
}
@Override
public void readFromNBT(NBTTagCompound tagCompound) {
super.readFromNBT(tagCompound);
}
@Override
public void readRestorableFromNBT(NBTTagCompound tagCompound) {
super.readRestorableFromNBT(tagCompound);
readBufferFromNBT(tagCompound);
readGhostBufferFromNBT(tagCompound);
readRecipesFromNBT(tagCompound);
int m = tagCompound.getInteger("rsMode");
redstoneMode = RedstoneMode.values()[m];
speedMode = tagCompound.getByte("speedMode");
}
private void readBufferFromNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = tagCompound.getTagList("Items", Constants.NBT.TAG_COMPOUND);
for (int i = 0 ; i < bufferTagList.tagCount() ; i++) {
NBTTagCompound nbtTagCompound = bufferTagList.getCompoundTagAt(i);
inventoryHelper.setStackInSlot(i+CrafterContainer.SLOT_BUFFER, ItemStack.loadItemStackFromNBT(nbtTagCompound));
}
}
private void readGhostBufferFromNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = tagCompound.getTagList("GItems", Constants.NBT.TAG_COMPOUND);
for (int i = 0 ; i < bufferTagList.tagCount() ; i++) {
NBTTagCompound nbtTagCompound = bufferTagList.getCompoundTagAt(i);
ghostSlots[i] = ItemStack.loadItemStackFromNBT(nbtTagCompound);
}
}
private void readRecipesFromNBT(NBTTagCompound tagCompound) {
NBTTagList recipeTagList = tagCompound.getTagList("Recipes", Constants.NBT.TAG_COMPOUND);
for (int i = 0 ; i < recipeTagList.tagCount() ; i++) {
NBTTagCompound nbtTagCompound = recipeTagList.getCompoundTagAt(i);
recipes[i].readFromNBT(nbtTagCompound);
}
}
@Override
public void writeToNBT(NBTTagCompound tagCompound) {
super.writeToNBT(tagCompound);
}
@Override
public void writeRestorableToNBT(NBTTagCompound tagCompound) {
super.writeRestorableToNBT(tagCompound);
writeBufferToNBT(tagCompound);
writeGhostBufferToNBT(tagCompound);
writeRecipesToNBT(tagCompound);
tagCompound.setByte("rsMode", (byte)redstoneMode.ordinal());
tagCompound.setByte("speedMode", (byte) speedMode);
}
private void writeBufferToNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = new NBTTagList();
for (int i = CrafterContainer.SLOT_BUFFER ; i < inventoryHelper.getCount() ; i++) {
ItemStack stack = inventoryHelper.getStackInSlot(i);
NBTTagCompound nbtTagCompound = new NBTTagCompound();
if (stack != null) {
stack.writeToNBT(nbtTagCompound);
}
bufferTagList.appendTag(nbtTagCompound);
}
tagCompound.setTag("Items", bufferTagList);
}
private void writeGhostBufferToNBT(NBTTagCompound tagCompound) {
NBTTagList bufferTagList = new NBTTagList();
for (int i = 0 ; i < ghostSlots.length ; i++) {
ItemStack stack = ghostSlots[i];
NBTTagCompound nbtTagCompound = new NBTTagCompound();
if (stack != null) {
stack.writeToNBT(nbtTagCompound);
}
bufferTagList.appendTag(nbtTagCompound);
}
tagCompound.setTag("GItems", bufferTagList);
}
private void writeRecipesToNBT(NBTTagCompound tagCompound) {
NBTTagList recipeTagList = new NBTTagList();
for (CraftingRecipe recipe : recipes) {
NBTTagCompound nbtTagCompound = new NBTTagCompound();
recipe.writeToNBT(nbtTagCompound);
recipeTagList.appendTag(nbtTagCompound);
}
tagCompound.setTag("Recipes", recipeTagList);
}
@Override
protected void checkStateServer() {
super.checkStateServer();
if (redstoneMode != RedstoneMode.REDSTONE_IGNORED) {
int meta = worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
boolean rs = BlockTools.getRedstoneSignal(meta);
if (redstoneMode == RedstoneMode.REDSTONE_OFFREQUIRED && rs) {
return;
} else if (redstoneMode == RedstoneMode.REDSTONE_ONREQUIRED && !rs) {
return;
}
}
int steps = 1;
if (speedMode == SPEED_FAST) {
steps = CrafterConfiguration.speedOperations;
}
for (int i = 0 ; i < steps ; i++) {
craftOneCycle();
}
}
private void craftOneCycle() {
// 0%: rf -> rf
// 100%: rf -> rf / 2
int rf = (int) (CrafterConfiguration.rfPerOperation * (2.0f - getInfusedFactor()) / 2.0f);
if (getEnergyStored(ForgeDirection.DOWN) < rf) {
return;
}
boolean energyConsumed = false;
for (int index = 0 ; index < supportedRecipes ; index++) {
CraftingRecipe craftingRecipe = recipes[index];
if (craftingRecipe != null) {
if (craftOneItemNew(craftingRecipe)) {
energyConsumed = true;
}
}
}
if (energyConsumed) {
consumeEnergy(rf);
}
}
private boolean craftOneItemNew(CraftingRecipe craftingRecipe) {
IRecipe recipe = craftingRecipe.getCachedRecipe(worldObj);
if (recipe == null) {
return false;
}
Map<Integer,ItemStack> undo = new HashMap<Integer, ItemStack>();
if (!testAndConsumeCraftingItems(craftingRecipe, undo)) {
undo(undo);
return false;
}
// ItemStack result = recipe.getCraftingResult(craftingRecipe.getInventory());
ItemStack result = null;
try {
result = recipe.getCraftingResult(workInventory);
} catch (Exception e) {
// Ignore this error for now to make sure we don't crash on bad recipes.
Logging.log("Problem with recipe!");
}
// Try to merge the output. If there is something that doesn't fit we undo everything.
if (result != null && placeResult(craftingRecipe.isCraftInternal(), result, undo)) {
return true;
} else {
// We don't have place. Undo the operation.
undo(undo);
return false;
}
}
private boolean testAndConsumeCraftingItems(CraftingRecipe craftingRecipe, Map<Integer,ItemStack> undo) {
boolean internal = craftingRecipe.isCraftInternal();
int keep = craftingRecipe.isKeepOne() ? 1 : 0;
InventoryCrafting inventory = craftingRecipe.getInventory();
for (int i = 0 ; i < inventory.getSizeInventory() ; i++) {
ItemStack stack = inventory.getStackInSlot(i);
if (stack != null) {
int count = stack.stackSize;
for (int j = 0 ; j < CrafterContainer.BUFFER_SIZE ; j++) {
int slotIdx = CrafterContainer.SLOT_BUFFER + j;
ItemStack input = inventoryHelper.getStackInSlot(slotIdx);
if (input != null && input.stackSize > keep) {
if (OreDictionary.itemMatches(stack, input, false)) {
workInventory.setInventorySlotContents(i, input.copy());
if (input.getItem().hasContainerItem(input)) {
ItemStack containerItem = input.getItem().getContainerItem(input);
if (containerItem != null) {
if ((!containerItem.isItemStackDamageable()) || containerItem.getItemDamage() <= containerItem.getMaxDamage()) {
if (!placeResult(internal, containerItem, undo)) {
// Not enough room.
return false;
}
}
}
}
int ss = count;
if (input.stackSize - ss < keep) {
ss = input.stackSize - keep;
}
count -= ss;
if (!undo.containsKey(slotIdx)) {
undo.put(slotIdx, input.copy());
}
input.splitStack(ss); // This consumes the items
if (input.stackSize == 0) {
inventoryHelper.setStackInSlot(slotIdx, null);
}
}
}
if (count == 0) {
break;
}
}
if (count > 0) {
return false; // Couldn't find all items.
}
} else {
workInventory.setInventorySlotContents(i, null);
}
}
return true;
}
private void undo(Map<Integer,ItemStack> undo) {
for (Map.Entry<Integer, ItemStack> entry : undo.entrySet()) {
inventoryHelper.setStackInSlot(entry.getKey(), entry.getValue());
}
}
private boolean placeResult(boolean internal, ItemStack result, Map<Integer,ItemStack> undo) {
int start;
int stop;
if (internal) {
start = CrafterContainer.SLOT_BUFFER;
stop = CrafterContainer.SLOT_BUFFER + CrafterContainer.BUFFER_SIZE;
} else {
start = CrafterContainer.SLOT_BUFFEROUT;
stop = CrafterContainer.SLOT_BUFFEROUT + CrafterContainer.BUFFEROUT_SIZE;
}
return InventoryHelper.mergeItemStack(this, true, result, start, stop, undo) == 0;
}
private void rememberItems() {
for (int i = 0 ; i < ghostSlots.length ; i++) {
int slotIdx;
if (i < CrafterContainer.BUFFER_SIZE) {
slotIdx = i + CrafterContainer.SLOT_BUFFER;
} else {
slotIdx = i + CrafterContainer.SLOT_BUFFEROUT - CrafterContainer.BUFFER_SIZE;
}
if (inventoryHelper.containsItem(slotIdx)) {
ItemStack stack = inventoryHelper.getStackInSlot(slotIdx);
ghostSlots[i] = stack.copy();
ghostSlots[i].stackSize = 1;
}
}
markDirty();
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
private void forgetItems() {
for (int i = 0 ; i < ghostSlots.length ; i++) {
ghostSlots[i] = null;
}
markDirty();
worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}
@Override
public boolean execute(EntityPlayerMP playerMP, String command, Map<String, Argument> args) {
boolean rc = super.execute(playerMP, command, args);
if (rc) {
return true;
}
if (CMD_MODE.equals(command)) {
String m = args.get("rs").getString();
setRedstoneMode(RedstoneMode.getMode(m));
setSpeedMode(args.get("speed").getInteger());
return true;
} else if (CMD_REMEMBER.equals(command)) {
rememberItems();
return true;
} else if (CMD_FORGET.equals(command)) {
forgetItems();
return true;
}
return false;
}
}