package com.team.futurecraft.tileentity; import com.team.futurecraft.block.EnumMachineSetting; import com.team.futurecraft.block.IElectric; import com.team.futurecraft.block.Machine; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumFacing; import net.minecraft.util.IChatComponent; /** * This is the class that implements EnergyContainer and adds functionality * usually needed in machines without having to implement them yourself, such as handling * in/out machine settings, transferring energy based on them, and dealing with inventory slots. * It's a bit of a mess right now and I need to organize and comment it all to make it an easy to use * api. * * @author Joseph */ public class TileEntityMachine extends EnergyContainer implements ISidedInventory { private Machine theBlock; private IBlockState state; private ItemStack[] slots; private String customName; public TileEntityMachine(int energyTransfer, int maxEnergy, IBlockState state, boolean isFull, int slots) { super(maxEnergy, energyTransfer); System.out.println("instantiating TilEntityMachine with parameters"); this.theBlock = (Machine)state.getBlock(); this.state = state; if (isFull) { this.setEnergy(maxEnergy); } this.slots = new ItemStack[slots]; } /** * reads TE data from an NBT tag for world loading. You'll need to read data tags * in here for any data you wrote in writeToNBT. * * Just like EnergyContainer, you MUST call super(tag) if you override this. */ @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); this.setEnergy(tag.getInteger("energy")); NBTTagList nbttaglist = tag.getTagList("Items", 10); this.slots = new ItemStack[this.getSizeInventory()]; for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(i); byte b0 = nbttagcompound1.getByte("Slot"); if (b0 >= 0 && b0 < this.slots.length) { this.slots[b0] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } } /** * Saves TE data to an NBT tag for world saving. You'll need to write data tags * in here for any data you dont want lost when the game is closed. * * Just like EnergyContainer, you MUST call super(tag) if you override this. */ @Override public void writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); tag.setInteger("energy", this.getEnergy()); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < this.slots.length; ++i) { if (this.slots[i] != null) { NBTTagCompound nbttagcompound = new NBTTagCompound(); nbttagcompound.setByte("Slot", (byte)i); this.slots[i].writeToNBT(nbttagcompound); nbttaglist.appendTag(nbttagcompound); } } tag.setTag("Items", nbttaglist); } /** * TE equivelant of IElectric's onPowered. Checks if the side is set to unput, if the energy * count is full, or if there's more energy sent in than maxEnergy - energy, and returns any unused energy. */ public int tryToPower(int amount, EnumFacing side) { boolean flag = false; if (this.theBlock.getSide(rotatedFace(side, (EnumFacing)this.state.getValue(Machine.FACING))) == EnumMachineSetting.energyInput) { if (this.getEnergy() < this.getMaxEnergy()) { if (this.getMaxEnergy() - this.getEnergy() >= amount) { this.addEnergy(amount); flag = true; return 0; } else { amount = this.getMaxEnergy() - this.getEnergy(); this.setEnergy(this.getMaxEnergy()); flag = true; return amount; } } } if (flag) { this.markDirty(); } return amount; } /** * Rotates a face by another face and returns the face rotated by the face! * example: west of west will be south. Used for computing in/out sides on rotated machines. */ private EnumFacing rotatedFace(EnumFacing face1, EnumFacing face2) { if (face2 == EnumFacing.NORTH) return face1; if (face2 == EnumFacing.SOUTH) return face1.getOpposite(); if (face2 == EnumFacing.WEST) return face1.rotateY(); if (face2 == EnumFacing.EAST) return face1.rotateY().rotateY().rotateY(); return face1; } /** * Updates the TE. This is usually what every TE will override, and doing so, * MUST call super() or else this class cant do it's job. */ @Override public void update() { int i = 0; while (this.getEnergy() > this.energyTransferred() && i < 4) { if (this.theBlock.getSide(rotatedFace(EnumFacing.getHorizontal(i), (EnumFacing)this.state.getValue(Machine.FACING))) == EnumMachineSetting.energyOutput) { this.powerNeighbor(EnumFacing.getHorizontal(i)); this.markDirty(); } i++; } } /** * Tries to power any connected blocks. Called by update() for each side set to output every tick. */ private void powerNeighbor(EnumFacing side) { int energyToTransfer; BlockPos dirPos = this.pos.offset(side); if (this.getEnergy() > 0) { if (this.getEnergy() < this.energyTransferred()) { energyToTransfer = this.getEnergy(); } else { energyToTransfer = this.energyTransferred(); } if (this.theBlock.canConnectTo(this.worldObj, this.pos, side)) { IElectric blockToPower = (IElectric)this.worldObj.getBlockState(dirPos).getBlock(); this.removeEnergy(energyToTransfer - blockToPower.onPowered(this.worldObj, dirPos, energyToTransfer, side.getOpposite())); } } } //<-------=======IInventory overrides=======-------> /** * Returns if the player is close enough to use this. */ public boolean isUseableByPlayer(EntityPlayer player) { return this.worldObj.getTileEntity(this.pos) != this ? false : player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D; } public void openInventory(EntityPlayer player) {} public void closeInventory(EntityPlayer player) {} /** * Returns true if automation is allowed to insert the given stack (ignoring stack size) into the given slot. */ public boolean isItemValidForSlot(int index, ItemStack stack) { return true; } /** * Returns slots that automation ties hoppers to from a certain side. */ public int[] getSlotsForFace(EnumFacing side) { return new int[] {}; } /** * Returns true if automation can insert the given item in the given slot from the given side. Args: slot, item, * side */ public boolean canInsertItem(int index, ItemStack itemStackIn, EnumFacing direction) { return this.isItemValidForSlot(index, itemStackIn); } /** * Returns true if automation can extract the given item in the given slot from the given side. Args: slot, item, * side */ public boolean canExtractItem(int index, ItemStack stack, EnumFacing direction) { return false; } /** * Used by vanilla to get certain tags from this TE. In this case it might be energy level or cook time. * This system is really messed up and i preffer to use stuff such as getEnergy() instead. */ public int getField(int id) { return 0; } /** * Used by vanilla to set certain tags from this TE. In this case it might be energy level or cook time. * This system is really messed up and i preffer to use stuff such as setEnergy() instead. */ public void setField(int id, int value) {} /** * We dont use the setField and getField system so this is useless now. */ public int getFieldCount() { return 0; } /** * Clears the inventory, pretty straightforward. */ public void clear() { for (int i = 0; i < this.slots.length; ++i) { this.slots[i] = null; } } /** * Returns the number of slots in the inventory. */ public int getSizeInventory() { return this.slots.length; } /** * Returns the stack in slot i */ public ItemStack getStackInSlot(int index) { return this.slots[index]; } /** * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a * new stack. */ public ItemStack decrStackSize(int index, int count) { if (this.slots[index] != null) { ItemStack itemstack; if (this.slots[index].stackSize <= count) { itemstack = this.slots[index]; this.slots[index] = null; return itemstack; } else { itemstack = this.slots[index].splitStack(count); if (this.slots[index].stackSize == 0) { this.slots[index] = null; } return itemstack; } } else { return null; } } /** * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem - * like when you close a workbench GUI. */ public ItemStack getStackInSlotOnClosing(int index) { if (this.slots[index] != null) { ItemStack itemstack = this.slots[index]; this.slots[index] = null; return itemstack; } else { return null; } } /** * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections). */ public void setInventorySlotContents(int index, ItemStack stack) { this.slots[index] = stack; if (stack != null && stack.stackSize > this.getInventoryStackLimit()) { stack.stackSize = this.getInventoryStackLimit(); } } /** * Gets the name of this command sender (usually username, but possibly "Rcon") */ public String getName() { return this.hasCustomName() ? this.customName : "container.machine"; } /** * Returns true if this thing is named */ public boolean hasCustomName() { return this.customName != null && this.customName.length() > 0; } /** * Sets the custom name of this TE. */ public void setCustomInventoryName(String name) { this.customName = name; } /** * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't * this more of a set than a get?* */ public int getInventoryStackLimit() { return 64; } /** * Gets this TE's name in chat, for those rare occasions when you may make TE's talk... */ @Override public IChatComponent getDisplayName() { return (IChatComponent)(this.hasCustomName() ? new ChatComponentText(this.getName()) : new ChatComponentTranslation(this.getName(), new Object[0])); } }