package com.carpentersblocks.block; import java.util.ArrayList; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.Explosion; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import com.carpentersblocks.CarpentersBlocks; import com.carpentersblocks.data.GarageDoor; import com.carpentersblocks.tileentity.TEBase; import com.carpentersblocks.tileentity.TECarpentersGarageDoor; import com.carpentersblocks.util.EntityLivingUtil; import com.carpentersblocks.util.handler.ChatHandler; import com.carpentersblocks.util.registry.BlockRegistry; import com.carpentersblocks.util.registry.FeatureRegistry; import com.carpentersblocks.util.registry.IconRegistry; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; public class BlockCarpentersGarageDoor extends BlockCoverable { public final static String type[] = { "default", "glassTop", "glass", "siding", "hidden" }; private static GarageDoor data = new GarageDoor(); public BlockCarpentersGarageDoor(Material material) { super(material); } @SideOnly(Side.CLIENT) @Override /** * When this method is called, your block should register all the icons it needs with the given IconRegister. This * is the only chance you get to register icons. */ public void registerBlockIcons(IIconRegister iconRegister) { IconRegistry.icon_garage_glass_top = iconRegister.registerIcon(CarpentersBlocks.MODID + ":" + "garagedoor/glass_top"); IconRegistry.icon_garage_glass = iconRegister.registerIcon(CarpentersBlocks.MODID + ":" + "garagedoor/glass"); } @Override /** * Cycle forwards through types. */ protected boolean onHammerLeftClick(TEBase TE, EntityPlayer entityPlayer) { int temp = data.getType(TE); if (++temp > type.length - 1) { temp = 0; } Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase TE_block : pieces) { data.setType(TE_block, temp); } return true; } @Override /** * Cycle backwards through types. */ protected boolean onHammerRightClick(TEBase TE, EntityPlayer entityPlayer) { int temp = data.getType(TE); if (--temp < 0) { temp = type.length - 1; } if (entityPlayer.isSneaking()) { int rigidity = data.isRigid(TE) ? GarageDoor.DOOR_NONRIGID : GarageDoor.DOOR_RIGID; switch (rigidity) { case GarageDoor.DOOR_NONRIGID: ChatHandler.sendMessageToPlayer("message.activation_wood.name", entityPlayer); break; case GarageDoor.DOOR_RIGID: ChatHandler.sendMessageToPlayer("message.activation_iron.name", entityPlayer); } Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase piece : pieces) { data.setRigidity(piece, rigidity); } } else { Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase piece : pieces) { data.setType(piece, temp); } } return true; } @Override /** * Updates the blocks bounds based on its current state. Args: world, x, y, z */ public void setBlockBoundsBasedOnState(IBlockAccess blockAccess, int x, int y, int z) { TEBase TE = getTileEntity(blockAccess, x, y, z); if (TE != null) { float yMin = data.isHost(TE) && data.isOpen(TE) ? 0.5F : 0.0F; ForgeDirection dir = data.getDirection(TE); if (data.isVisible(TE)) { if (data.getType(TE) == GarageDoor.TYPE_HIDDEN) { setBlockBounds(0.0F, yMin, 0.0F, 1.0F, 1.0F, 0.125F, dir); } else { setBlockBounds(0.0F, yMin, 0.125F, 1.0F, 1.0F, 0.25F, dir); } } } } /** * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been * cleared to be reused) */ @Override public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z) { TEBase TE = getTileEntity(world, x, y, z); if (TE != null) { if (!data.isVisible(TE)) { return null; } } setBlockBoundsBasedOnState(world, x, y, z); return super.getCollisionBoundingBoxFromPool(world, x, y, z); } /** * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. Args: world, * x, y, z, startVec, endVec */ @Override public MovingObjectPosition collisionRayTrace(World world, int x, int y, int z, Vec3 startVec, Vec3 endVec) { TEBase TE = getTileEntity(world, x, y, z); if (TE != null) { if (!data.isVisible(TE)) { return null; } } return super.collisionRayTrace(world, x, y, z, startVec, endVec); } /** * Location sensitive version of getExplosionRestance * * @param par1Entity The entity that caused the explosion * @param world The current world * @param x X Position * @param y Y Position * @param z Z Position * @param explosionX Explosion source X Position * @param explosionY Explosion source X Position * @param explosionZ Explosion source X Position * @return The amount of the explosion absorbed. */ @Override public float getExplosionResistance(Entity entity, World world, int x, int y, int z, double explosionX, double explosionY, double explosionZ) { TEBase TE = getTileEntity(world, x, y, z); if (TE != null) { if (data.isOpen(TE) && !data.isHost(TE)) { return Blocks.bedrock.getExplosionResistance(entity); } } return getExplosionResistance(entity); } /** * Called upon the block being destroyed by an explosion */ @Override public void onBlockDestroyedByExplosion(World world, int x, int y, int z, Explosion explosion) { // Destroy entire stack of doors destroy(world, x, y, z, true); } private void destroyInDirection(World world, int x, int y, int z, boolean doDrop, ForgeDirection dir) { if (y < 0 || y > world.getHeight()) { return; } Block block = world.getBlock(x, y, z); if (block.equals(this)) { boolean dropBlock = false; if (doDrop) { TEBase temp = getTileEntity(world, x, y, z); if (temp != null && data.isHost(temp)) { dropBlock = true; } } destroyBlock(world, x, y, z, dropBlock); } else if (!block.equals(Blocks.air)) { return; } destroyInDirection(world, x, y + dir.offsetY, z, doDrop, dir); } /** * Will destroy a column of garage doors. * * @param world * @param x * @param y * @param z * @param dropSource whether player destroyed topmost block */ private void destroy(World world, int x, int y, int z, boolean doDrop) { if (!world.isRemote) { destroyInDirection(world, x, y, z, doDrop, ForgeDirection.UP); destroyInDirection(world, x, y - 1, z, false, ForgeDirection.DOWN); } } /** * Will create a column of garage doors beginning at given coordinates. * * @param world * @param x * @param y * @param z */ private void create(TEBase TE, World world, int x, int y, int z) { for (; canPlaceBlockAt(world, x, y, z); --y) { world.setBlock(x, y, z, this); TEBase temp = getTileEntity(world, x, y, z); if (temp != null) { data.replicate(TE, temp); } } } /** * Allows a tile entity called during block activation to be changed before * altering attributes like cover, dye, overlay, etc. * <p> * Primarily offered for the garage door, when open, to swap the top piece * with the bottom piece for consistency. * * @param TE the originating {@link TEBase} * @return a swapped in {@link TEBase}, or the passed in {@link TEBase} */ @Override protected TEBase getTileEntityForBlockActivation(TEBase TE) { return data.isOpen(TE) ? data.getBottommost(TE.getWorldObj(), TE.xCoord, TE.yCoord, TE.zCoord) : TE; } /** * Called if cover and decoration checks have been performed but * returned no changes. */ @Override protected void postOnBlockActivated(TEBase TE, EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ, ActionResult actionResult) { if (!data.isRigid(TE)) { int state = data.getState(TE) == GarageDoor.STATE_OPEN ? GarageDoor.STATE_CLOSED : GarageDoor.STATE_OPEN; Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase piece : pieces) { data.setState(piece, state); } actionResult.setAltered().setNoSound(); } } @Override /** * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are * their own) Args: x, y, z, neighbor blockID */ public void onNeighborBlockChange(World world, int x, int y, int z, Block block) { if (!world.isRemote) { TEBase TE = getTileEntity(world, x, y, z); if (TE != null) { if (data.isHost(TE) && !(canPlaceBlockOnSide(world, x, y, z, 0) || world.getBlock(x, y + 1, z).equals(this))) { destroy(world, x, y, z, true); } else { // Check for new door state (open or closed) int powerState = world.isBlockIndirectlyGettingPowered(x, y, z) ? GarageDoor.STATE_OPEN : GarageDoor.STATE_CLOSED; if (block != null && block.canProvidePower() && powerState != data.getState(TE)) { int old_state = data.getState(TE); int state = old_state; if (data.isOpen(TE)) { // Check if a garage door piece is still powered boolean garageHasPower = false; Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase piece : pieces) { if (world.isBlockIndirectlyGettingPowered(piece.xCoord, piece.yCoord, piece.zCoord)) { garageHasPower = true; break; } } if (!garageHasPower) { state = data.STATE_CLOSED; } } else { state = data.STATE_OPEN; } if (state != old_state) { data.setState(TE, state); Set<TEBase> pieces = data.getBlocks(TE, this); for (TEBase piece : pieces) { data.setState(piece, state); } } } } } // Create new door pieces below if bottom block turned to air if (FeatureRegistry.enableGarageDoorVoidFill) { if (block != this && world.getBlock(x, y - 1, z).equals(Blocks.air)) { create(TE, world, x, y - 1, z); } } } super.onNeighborBlockChange(world, x, y, z, block); } /** * Called when a player removes a block. This is responsible for * actually destroying the block, and the block is intact at time of call. * This is called regardless of whether the player can harvest the block or * not. * * Return true if the block is actually destroyed. * * Note: When used in multiplayer, this is called on both client and * server sides! * * @param world The current world * @param player The player damaging the block, may be null * @param x X Position * @param y Y position * @param z Z position * @param willHarvest True if Block.harvestBlock will be called after this, if the return in true. * Can be useful to delay the destruction of tile entities till after harvestBlock * @return True if the block is actually destroyed. */ @Override public boolean removedByPlayer(World world, EntityPlayer entityPlayer, int x, int y, int z) { if (!suppressDestroyBlock(entityPlayer)) { destroy(world, x, y, z, !entityPlayer.capabilities.isCreativeMode); return false; } return super.removedByPlayer(world, entityPlayer, x, y, z); } /** * This returns a complete list of items dropped from this block. * * @param world The current world * @param x X Position * @param y Y Position * @param z Z Position * @param metadata Current metadata * @param fortune Breakers fortune level * @return A ArrayList containing all items this block drops */ @Override public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune) { ArrayList<ItemStack> list = new ArrayList<ItemStack>(); TEBase TE = getTileEntity(world, x, y, z); if (TE != null && data.isHost(TE)) { list.add(getItemDrop(world, metadata)); } list.addAll(super.getDrops(world, x, y, z, METADATA_DROP_ATTR_ONLY, fortune)); return list; } @Override /** * Checks to see if you can place this block can be placed on that side of a block: BlockLever overrides */ public boolean canPlaceBlockOnSide(World world, int x, int y, int z, int side) { return world.isSideSolid(x, y + 1, z, ForgeDirection.DOWN); } /** * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z */ @Override public boolean canPlaceBlockAt(World world, int x, int y, int z) { if (super.canPlaceBlockAt(world, x, y, z)) { return canPlaceBlockOnSide(world, x, y, z, 0) || world.getBlock(x, y + 1, z).equals(this); } else { return false; } } @Override /** * Called when the block is placed in the world. */ public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase entityLiving, ItemStack itemStack) { super.onBlockPlacedBy(world, x, y, z, entityLiving, itemStack); TEBase TE = getTileEntity(world, x, y, z); // Set direction based on player facing ForgeDirection facing = EntityLivingUtil.getFacing(entityLiving).getOpposite(); data.setDirection(TE, facing); data.setHost(TE); // Find a nearby door to replicate properties TEBase temp = data.findReferencePiece(world, x, y, z, facing); if (temp != null) { data.replicate(temp, TE); } // Create remainder of stack below host create(TE, world, x, y - 1, z); } @Override /** * The type of render function that is called for this block */ public int getRenderType() { return BlockRegistry.carpentersGarageDoorRenderID; } @Override public ForgeDirection[] getValidRotations(World worldObj, int x, int y,int z) { ForgeDirection[] axises = {ForgeDirection.UP, ForgeDirection.DOWN}; return axises; } @Override public boolean rotateBlock(World world, int x, int y, int z, ForgeDirection axis) { // to correctly support archimedes' ships mod: // if Axis is DOWN, block rotates to the left, north -> west -> south -> east // if Axis is UP, block rotates to the right: north -> east -> south -> west TileEntity tile = world.getTileEntity(x, y, z); if (tile != null && tile instanceof TEBase) { TEBase cbTile = (TEBase)tile; int dataAngle = (cbTile.getData() & 0x70) >> 4; ForgeDirection direction = ForgeDirection.getOrientation(dataAngle); int newAngle = 0; switch (axis) { case UP: { switch (direction) { case WEST:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.NORTH.ordinal() << 4); break;} case NORTH:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.EAST.ordinal() << 4); break;} case EAST:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.SOUTH.ordinal() << 4); break;} case SOUTH:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.WEST.ordinal() << 4); break;} default: break; } cbTile.setData(newAngle); return true; } case DOWN: { switch (direction) { case WEST:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.SOUTH.ordinal() << 4); break;} case NORTH:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.EAST.ordinal() << 4); break;} case EAST:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.NORTH.ordinal() << 4); break;} case SOUTH:{newAngle = (cbTile.getData() & ~0x70) | (ForgeDirection.WEST.ordinal() << 4); break;} default: break; } cbTile.setData(newAngle); return true; } default: return false; } } return false; } @Override public TileEntity createNewTileEntity(World world, int metadata) { return new TECarpentersGarageDoor(); } }