package WayofTime.bloodmagic.tile; import WayofTime.bloodmagic.api.event.BloodMagicCraftedEvent; import WayofTime.bloodmagic.api.impl.BloodMagicAPI; import WayofTime.bloodmagic.api.impl.recipe.RecipeAlchemyTable; import WayofTime.bloodmagic.core.data.*; import WayofTime.bloodmagic.core.registry.AlchemyTableRecipeRegistry; import WayofTime.bloodmagic.iface.IBindable; import WayofTime.bloodmagic.iface.ICustomAlchemyConsumable; import WayofTime.bloodmagic.orb.BloodOrb; import WayofTime.bloodmagic.orb.IBloodOrb; import WayofTime.bloodmagic.recipe.alchemyTable.AlchemyTableRecipe; import WayofTime.bloodmagic.util.Constants; import WayofTime.bloodmagic.util.helper.NetworkHelper; import net.minecraft.block.state.IBlockState; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.BlockPos; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.ItemHandlerHelper; import org.apache.commons.lang3.ArrayUtils; import java.util.ArrayList; import java.util.List; public class TileAlchemyTable extends TileInventory implements ISidedInventory, ITickable { public static final int orbSlot = 6; public static final int toolSlot = 7; public static final int outputSlot = 8; public EnumFacing direction = EnumFacing.NORTH; public boolean isSlave = false; public int burnTime = 0; public int ticksRequired = 1; public BlockPos connectedPos = BlockPos.ORIGIN; public boolean[] blockedSlots = new boolean[]{false, false, false, false, false, false}; public TileAlchemyTable() { super(9, "alchemyTable"); } public void setInitialTableParameters(EnumFacing direction, boolean isSlave, BlockPos connectedPos) { this.isSlave = isSlave; this.connectedPos = connectedPos; if (!isSlave) { this.direction = direction; } } public boolean isInvisible() { return isSlave(); } public boolean isInputSlotAccessible(int slot) { return !(slot < 6 && slot >= 0) || !blockedSlots[slot]; } public void toggleInputSlotAccessible(int slot) { if (slot < 6 && slot >= 0) blockedSlots[slot] = !blockedSlots[slot]; } @Override public void deserialize(NBTTagCompound tag) { super.deserialize(tag); isSlave = tag.getBoolean("isSlave"); direction = EnumFacing.byIndex(tag.getInteger(Constants.NBT.DIRECTION)); connectedPos = new BlockPos(tag.getInteger(Constants.NBT.X_COORD), tag.getInteger(Constants.NBT.Y_COORD), tag.getInteger(Constants.NBT.Z_COORD)); burnTime = tag.getInteger("burnTime"); ticksRequired = tag.getInteger("ticksRequired"); byte[] array = tag.getByteArray("blockedSlots"); for (int i = 0; i < array.length; i++) blockedSlots[i] = array[i] != 0; } @Override public NBTTagCompound serialize(NBTTagCompound tag) { super.serialize(tag); tag.setBoolean("isSlave", isSlave); tag.setInteger(Constants.NBT.DIRECTION, direction.getIndex()); tag.setInteger(Constants.NBT.X_COORD, connectedPos.getX()); tag.setInteger(Constants.NBT.Y_COORD, connectedPos.getY()); tag.setInteger(Constants.NBT.Z_COORD, connectedPos.getZ()); tag.setInteger("burnTime", burnTime); tag.setInteger("ticksRequired", ticksRequired); byte[] blockedSlotArray = new byte[blockedSlots.length]; for (int i = 0; i < blockedSlots.length; i++) blockedSlotArray[i] = (byte) (blockedSlots[i] ? 1 : 0); tag.setByteArray("blockedSlots", blockedSlotArray); return tag; } @SuppressWarnings("unchecked") @Override public T getCapability(Capability capability, EnumFacing facing) { if (facing != null && capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { if (this.isSlave()) { TileEntity tile = getWorld().getTileEntity(connectedPos); if (tile instanceof TileAlchemyTable && !((TileAlchemyTable) tile).isSlave) { return (T) tile.getCapability(capability, facing); } } else { return super.getCapability(capability, facing); } } return super.getCapability(capability, facing); } @Override public int[] getSlotsForFace(EnumFacing side) { switch (side) { case DOWN: return new int[]{outputSlot}; case UP: return new int[]{orbSlot, toolSlot}; default: return new int[]{0, 1, 2, 3, 4, 5}; } } @Override public boolean canInsertItem(int index, ItemStack stack, EnumFacing direction) { switch (direction) { case DOWN: return index != outputSlot && index != orbSlot && index != toolSlot; case UP: if (index == orbSlot && !stack.isEmpty() && stack.getItem() instanceof IBloodOrb) { return true; } else if (index == toolSlot) { return false; //TODO: } else { return true; } default: if (this.isSlave) { TileEntity tile = getWorld().getTileEntity(connectedPos); if (tile instanceof TileAlchemyTable && !((TileAlchemyTable) tile).isSlave) { return ((TileAlchemyTable) tile).canInsertItem(index, stack, direction); } } return getAccessibleInputSlots(direction).contains(index); } } @Override public boolean canExtractItem(int index, ItemStack stack, EnumFacing direction) { switch (direction) { case DOWN: return index == outputSlot; case UP: if (index == orbSlot && !stack.isEmpty() && stack.getItem() instanceof IBloodOrb) { return true; } else if (index == toolSlot) { return true; //TODO: } else { return true; } default: if (this.isSlave) { TileEntity tile = getWorld().getTileEntity(connectedPos); if (tile instanceof TileAlchemyTable && !((TileAlchemyTable) tile).isSlave) { return ((TileAlchemyTable) tile).canExtractItem(index, stack, direction); } } return getAccessibleInputSlots(direction).contains(index); } } public List getAccessibleInputSlots(EnumFacing direction) { List list = new ArrayList<>(); for (int i = 0; i < 6; i++) { if (isInputSlotAccessible(i)) { list.add(i); } } return list; } @Override public void update() { if (isSlave()) { return; } List inputList = new ArrayList<>(); for (int i = 0; i < 6; i++) { if (!getStackInSlot(i).isEmpty()) { inputList.add(getStackInSlot(i)); } } int tier = getTierOfOrb(); // special recipes like dying AlchemyTableRecipe recipe = AlchemyTableRecipeRegistry.getMatchingRecipe(inputList, getWorld(), getPos()); if (recipe != null && (burnTime > 0 || (!getWorld().isRemote && tier >= recipe.getTierRequired() && this.getContainedLp() >= recipe.getLpDrained()))) { if (burnTime == 1) notifyUpdate(); if (canCraft(recipe.getRecipeOutput(inputList))) { ticksRequired = recipe.getTicksRequired(); burnTime++; if (burnTime == ticksRequired) { if (!getWorld().isRemote) { int requiredLp = recipe.getLpDrained(); if (requiredLp > 0) { if (!getWorld().isRemote) { consumeLp(requiredLp); } } if (!getWorld().isRemote) { craftItem(inputList, recipe); } } burnTime = 0; IBlockState state = getWorld().getBlockState(pos); getWorld().notifyBlockUpdate(getPos(), state, state, 3); } else if (burnTime > ticksRequired + 10) { burnTime = 0; } } else { burnTime = 0; } } else { // Simple recipes RecipeAlchemyTable recipeAlchemyTable = BloodMagicAPI.INSTANCE.getRecipeRegistrar().getAlchemyTable(inputList); if (recipeAlchemyTable != null && (burnTime > 0 || (!getWorld().isRemote && tier >= recipeAlchemyTable.getMinimumTier() && getContainedLp() >= recipeAlchemyTable.getSyphon()))) { if (burnTime == 1) notifyUpdate(); if (canCraft(recipeAlchemyTable.getOutput())) { ticksRequired = recipeAlchemyTable.getTicks(); burnTime++; if (burnTime >= ticksRequired) { if (!getWorld().isRemote) { if (recipeAlchemyTable.getSyphon() > 0) { if (consumeLp(recipeAlchemyTable.getSyphon()) < recipeAlchemyTable.getSyphon()) { //There was not enough LP to craft or there was no orb burnTime = 0; notifyUpdate(); return; } } ItemStack[] inputs = new ItemStack[0]; for (ItemStack stack : inputList) ArrayUtils.add(inputs, stack.copy()); BloodMagicCraftedEvent.AlchemyTable event = new BloodMagicCraftedEvent.AlchemyTable(recipeAlchemyTable.getOutput().copy(), inputs); MinecraftForge.EVENT_BUS.post(event); ItemStack outputSlotStack = getStackInSlot(outputSlot); if (outputSlotStack.isEmpty()) setInventorySlotContents(outputSlot, event.getOutput()); else outputSlotStack.grow(event.getOutput().getCount()); for (int i = 0; i < 6; i++) { ItemStack currentStack = getStackInSlot(i); if (currentStack.getItem().hasContainerItem(currentStack)) setInventorySlotContents(i, currentStack.getItem().getContainerItem(currentStack)); else if (currentStack.getItem() instanceof ICustomAlchemyConsumable) setInventorySlotContents(i, ((ICustomAlchemyConsumable) currentStack.getItem()).drainUseOnAlchemyCraft(currentStack)); else currentStack.shrink(1); } burnTime = 0; notifyUpdate(); } } } } else { burnTime = 0; } } } public double getProgressForGui() { return ((double) burnTime) / ticksRequired; } private boolean canCraft(ItemStack output) { ItemStack currentOutputStack = getStackInSlot(outputSlot); if (output.isEmpty()) return false; if (currentOutputStack.isEmpty()) return true; if (!ItemHandlerHelper.canItemStacksStack(output, currentOutputStack)) return false; int result = currentOutputStack.getCount() + output.getCount(); return result <= getInventoryStackLimit() && result <= currentOutputStack.getMaxStackSize(); } public int getTierOfOrb() { ItemStack orbStack = getStackInSlot(orbSlot); if (!orbStack.isEmpty()) { if (orbStack.getItem() instanceof IBloodOrb) { BloodOrb orb = ((IBloodOrb) orbStack.getItem()).getOrb(orbStack); return orb == null ? 0 : orb.getTier(); } } return 0; } public int getContainedLp() { ItemStack orbStack = getStackInSlot(orbSlot); if (!orbStack.isEmpty()) { if (orbStack.getItem() instanceof IBloodOrb) { Binding binding = ((IBindable) orbStack.getItem()).getBinding(orbStack); if (binding == null) { return 0; } SoulNetwork network = NetworkHelper.getSoulNetwork(binding); return network.getCurrentEssence(); } } return 0; } public void craftItem(List inputList, AlchemyTableRecipe recipe) { ItemStack outputStack = recipe.getRecipeOutput(inputList); if (this.canCraft(outputStack)) { ItemStack currentOutputStack = getStackInSlot(outputSlot); ItemStack[] inputs = new ItemStack[0]; for (ItemStack stack : inputList) ArrayUtils.add(inputs, stack.copy()); BloodMagicCraftedEvent.AlchemyTable event = new BloodMagicCraftedEvent.AlchemyTable(outputStack.copy(), inputs); MinecraftForge.EVENT_BUS.post(event); outputStack = event.getOutput(); if (currentOutputStack.isEmpty()) { setInventorySlotContents(outputSlot, outputStack); } else if (ItemHandlerHelper.canItemStacksStack(outputStack, currentOutputStack)) { currentOutputStack.grow(outputStack.getCount()); } consumeInventory(recipe); } } public int consumeLp(int requested) { ItemStack orbStack = getStackInSlot(orbSlot); if (!orbStack.isEmpty()) { if (orbStack.getItem() instanceof IBloodOrb) { if (NetworkHelper.syphonFromContainer(orbStack, SoulTicket.item(orbStack, world, pos, requested))) { return requested; } } } return 0; } public void consumeInventory(AlchemyTableRecipe recipe) { ItemStack[] input = new ItemStack[6]; for (int i = 0; i < 6; i++) { input[i] = getStackInSlot(i); } ItemStack[] result = recipe.getRemainingItems(input); for (int i = 0; i < 6; i++) { setInventorySlotContents(i, result[i]); } } public EnumFacing getDirection() { return direction; } public boolean isSlave() { return isSlave; } public int getBurnTime() { return burnTime; } public int getTicksRequired() { return ticksRequired; } public BlockPos getConnectedPos() { return connectedPos; } public boolean[] getBlockedSlots() { return blockedSlots; } public static int getOrbSlot() { return orbSlot; } public static int getToolSlot() { return toolSlot; } public static int getOutputSlot() { return outputSlot; } }