package net.minecraft.tileentity; import javax.annotation.Nullable; import net.minecraft.block.Block; import net.minecraft.block.BlockChest; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.inventory.Container; import net.minecraft.inventory.ContainerChest; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryLargeChest; import net.minecraft.inventory.ItemStackHelper; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.NonNullList; import net.minecraft.util.SoundCategory; import net.minecraft.util.datafix.DataFixer; import net.minecraft.util.datafix.FixTypes; import net.minecraft.util.datafix.walkers.ItemStackDataLists; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; public class TileEntityChest extends TileEntityLockableLoot implements ITickable { private NonNullList chestContents = NonNullList.withSize(27, ItemStack.EMPTY); /** Determines if the check for adjacent chests has taken place. */ public boolean adjacentChestChecked; /** Contains the chest tile located adjacent to this one (if any) */ public TileEntityChest adjacentChestZNeg; /** Contains the chest tile located adjacent to this one (if any) */ public TileEntityChest adjacentChestXPos; /** Contains the chest tile located adjacent to this one (if any) */ public TileEntityChest adjacentChestXNeg; /** Contains the chest tile located adjacent to this one (if any) */ public TileEntityChest adjacentChestZPos; /** The current angle of the lid (between 0 and 1) */ public float lidAngle; /** The angle of the lid last tick */ public float prevLidAngle; /** The number of players currently using this chest */ public int numPlayersUsing; /** Server sync counter (once per 20 ticks) */ private int ticksSinceSync; private BlockChest.Type cachedChestType; public TileEntityChest() { } public TileEntityChest(BlockChest.Type typeIn) { this.cachedChestType = typeIn; } /** * Returns the number of slots in the inventory. */ public int getSizeInventory() { return 27; } public boolean isEmpty() { for (ItemStack itemstack : this.chestContents) { if (!itemstack.isEmpty()) { return false; } } return true; } /** * Get the name of this object. For players this returns their username */ public String getName() { return this.hasCustomName() ? this.customName : "container.chest"; } public static void registerFixesChest(DataFixer fixer) { fixer.registerWalker(FixTypes.BLOCK_ENTITY, new ItemStackDataLists(TileEntityChest.class, new String[] {"Items"})); } public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.chestContents = NonNullList.withSize(this.getSizeInventory(), ItemStack.EMPTY); if (!this.checkLootAndRead(compound)) { ItemStackHelper.loadAllItems(compound, this.chestContents); } if (compound.hasKey("CustomName", 8)) { this.customName = compound.getString("CustomName"); } } public NBTTagCompound writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); if (!this.checkLootAndWrite(compound)) { ItemStackHelper.saveAllItems(compound, this.chestContents); } if (this.hasCustomName()) { compound.setString("CustomName", this.customName); } return compound; } /** * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. */ public int getInventoryStackLimit() { return 64; } public void updateContainingBlockInfo() { super.updateContainingBlockInfo(); this.adjacentChestChecked = false; doubleChestHandler = null; } @SuppressWarnings("incomplete-switch") private void setNeighbor(TileEntityChest chestTe, EnumFacing side) { if (chestTe.isInvalid()) { this.adjacentChestChecked = false; } else if (this.adjacentChestChecked) { switch (side) { case NORTH: if (this.adjacentChestZNeg != chestTe) { this.adjacentChestChecked = false; } break; case SOUTH: if (this.adjacentChestZPos != chestTe) { this.adjacentChestChecked = false; } break; case EAST: if (this.adjacentChestXPos != chestTe) { this.adjacentChestChecked = false; } break; case WEST: if (this.adjacentChestXNeg != chestTe) { this.adjacentChestChecked = false; } } } } /** * Performs the check for adjacent chests to determine if this chest is double or not. */ public void checkForAdjacentChests() { if (!this.adjacentChestChecked) { if (this.world == null || !this.world.isAreaLoaded(this.pos, 1)) return; // Forge: prevent loading unloaded chunks when checking neighbors this.adjacentChestChecked = true; this.adjacentChestXNeg = this.getAdjacentChest(EnumFacing.WEST); this.adjacentChestXPos = this.getAdjacentChest(EnumFacing.EAST); this.adjacentChestZNeg = this.getAdjacentChest(EnumFacing.NORTH); this.adjacentChestZPos = this.getAdjacentChest(EnumFacing.SOUTH); } } @Nullable protected TileEntityChest getAdjacentChest(EnumFacing side) { BlockPos blockpos = this.pos.offset(side); if (this.isChestAt(blockpos)) { TileEntity tileentity = this.world.getTileEntity(blockpos); if (tileentity instanceof TileEntityChest) { TileEntityChest tileentitychest = (TileEntityChest)tileentity; tileentitychest.setNeighbor(this, side.getOpposite()); return tileentitychest; } } return null; } private boolean isChestAt(BlockPos posIn) { if (this.world == null) { return false; } else { Block block = this.world.getBlockState(posIn).getBlock(); return block instanceof BlockChest && ((BlockChest)block).chestType == this.getChestType(); } } /** * Like the old updateEntity(), except more generic. */ public void update() { this.checkForAdjacentChests(); int i = this.pos.getX(); int j = this.pos.getY(); int k = this.pos.getZ(); ++this.ticksSinceSync; if (!this.world.isRemote && this.numPlayersUsing != 0 && (this.ticksSinceSync + i + j + k) % 200 == 0) { this.numPlayersUsing = 0; float f = 5.0F; for (EntityPlayer entityplayer : this.world.getEntitiesWithinAABB(EntityPlayer.class, new AxisAlignedBB((double)((float)i - 5.0F), (double)((float)j - 5.0F), (double)((float)k - 5.0F), (double)((float)(i + 1) + 5.0F), (double)((float)(j + 1) + 5.0F), (double)((float)(k + 1) + 5.0F)))) { if (entityplayer.openContainer instanceof ContainerChest) { IInventory iinventory = ((ContainerChest)entityplayer.openContainer).getLowerChestInventory(); if (iinventory == this || iinventory instanceof InventoryLargeChest && ((InventoryLargeChest)iinventory).isPartOfLargeChest(this)) { ++this.numPlayersUsing; } } } } this.prevLidAngle = this.lidAngle; float f1 = 0.1F; if (this.numPlayersUsing > 0 && this.lidAngle == 0.0F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null) { double d1 = (double)i + 0.5D; double d2 = (double)k + 0.5D; if (this.adjacentChestZPos != null) { d2 += 0.5D; } if (this.adjacentChestXPos != null) { d1 += 0.5D; } this.world.playSound((EntityPlayer)null, d1, (double)j + 0.5D, d2, SoundEvents.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, 0.5F, this.world.rand.nextFloat() * 0.1F + 0.9F); } if (this.numPlayersUsing == 0 && this.lidAngle > 0.0F || this.numPlayersUsing > 0 && this.lidAngle < 1.0F) { float f2 = this.lidAngle; if (this.numPlayersUsing > 0) { this.lidAngle += 0.1F; } else { this.lidAngle -= 0.1F; } if (this.lidAngle > 1.0F) { this.lidAngle = 1.0F; } float f3 = 0.5F; if (this.lidAngle < 0.5F && f2 >= 0.5F && this.adjacentChestZNeg == null && this.adjacentChestXNeg == null) { double d3 = (double)i + 0.5D; double d0 = (double)k + 0.5D; if (this.adjacentChestZPos != null) { d0 += 0.5D; } if (this.adjacentChestXPos != null) { d3 += 0.5D; } this.world.playSound((EntityPlayer)null, d3, (double)j + 0.5D, d0, SoundEvents.BLOCK_CHEST_CLOSE, SoundCategory.BLOCKS, 0.5F, this.world.rand.nextFloat() * 0.1F + 0.9F); } if (this.lidAngle < 0.0F) { this.lidAngle = 0.0F; } } } public boolean receiveClientEvent(int id, int type) { if (id == 1) { this.numPlayersUsing = type; return true; } else { return super.receiveClientEvent(id, type); } } public void openInventory(EntityPlayer player) { if (!player.isSpectator()) { if (this.numPlayersUsing < 0) { this.numPlayersUsing = 0; } ++this.numPlayersUsing; this.world.addBlockEvent(this.pos, this.getBlockType(), 1, this.numPlayersUsing); this.world.notifyNeighborsOfStateChange(this.pos, this.getBlockType(), false); if (this.getChestType() == BlockChest.Type.TRAP) { this.world.notifyNeighborsOfStateChange(this.pos.down(), this.getBlockType(), false); } } } public void closeInventory(EntityPlayer player) { if (!player.isSpectator() && this.getBlockType() instanceof BlockChest) { --this.numPlayersUsing; this.world.addBlockEvent(this.pos, this.getBlockType(), 1, this.numPlayersUsing); this.world.notifyNeighborsOfStateChange(this.pos, this.getBlockType(), false); if (this.getChestType() == BlockChest.Type.TRAP) { this.world.notifyNeighborsOfStateChange(this.pos.down(), this.getBlockType(), false); } } } public net.minecraftforge.items.VanillaDoubleChestItemHandler doubleChestHandler; @SuppressWarnings("unchecked") @Override @Nullable public T getCapability(net.minecraftforge.common.capabilities.Capability capability, @Nullable net.minecraft.util.EnumFacing facing) { if (capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { if(doubleChestHandler == null || doubleChestHandler.needsRefresh()) doubleChestHandler = net.minecraftforge.items.VanillaDoubleChestItemHandler.get(this); if (doubleChestHandler != null && doubleChestHandler != net.minecraftforge.items.VanillaDoubleChestItemHandler.NO_ADJACENT_CHESTS_INSTANCE) return (T) doubleChestHandler; } return super.getCapability(capability, facing); } public net.minecraftforge.items.IItemHandler getSingleChestHandler() { return super.getCapability(net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null); } /** * invalidates a tile entity */ public void invalidate() { super.invalidate(); this.updateContainingBlockInfo(); this.checkForAdjacentChests(); } public BlockChest.Type getChestType() { if (this.cachedChestType == null) { if (this.world == null || !(this.getBlockType() instanceof BlockChest)) { return BlockChest.Type.BASIC; } this.cachedChestType = ((BlockChest)this.getBlockType()).chestType; } return this.cachedChestType; } public String getGuiID() { return "minecraft:chest"; } public Container createContainer(InventoryPlayer playerInventory, EntityPlayer playerIn) { this.fillWithLoot(playerIn); return new ContainerChest(playerInventory, this, playerIn); } protected NonNullList getItems() { return this.chestContents; } }