package com.carpentersblocks.block; import java.util.List; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.Vec3; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import com.carpentersblocks.CarpentersBlocks; import com.carpentersblocks.data.Slope; import com.carpentersblocks.data.Slope.Type; import com.carpentersblocks.tileentity.TEBase; import com.carpentersblocks.util.EntityLivingUtil; import com.carpentersblocks.util.handler.EventHandler; import com.carpentersblocks.util.registry.BlockRegistry; import com.carpentersblocks.util.registry.IconRegistry; import com.carpentersblocks.util.registry.ItemRegistry; import com.carpentersblocks.util.slope.SlopeTransform; import com.carpentersblocks.util.slope.SlopeUtil; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; public class BlockCarpentersSlope extends BlockCoverable { public final static String slopeType[] = { "wedge", "obliqueInterior", "obliqueExterior", "prism", "prismWedge" }; public final static int META_WEDGE = 0; public final static int META_OBLIQUE_INT = 1; public final static int META_OBLIQUE_EXT = 2; public final static int META_PRISM = 3; public final static int META_PRISM_SLOPE = 4; private boolean rayTracing; public BlockCarpentersSlope(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_uncovered_oblique_pos = iconRegister.registerIcon(CarpentersBlocks.MODID + ":" + "slope/oblique_pos"); IconRegistry.icon_uncovered_oblique_neg = iconRegister.registerIcon(CarpentersBlocks.MODID + ":" + "slope/oblique_neg"); } @Override @SideOnly(Side.CLIENT) /** * Returns a base icon that doesn't rely on blockIcon, which * is set prior to texture stitch events. */ public IIcon getIcon() { return IconRegistry.icon_uncovered_full; } @Override /** * Alters block direction. */ protected boolean onHammerLeftClick(TEBase TE, EntityPlayer entityPlayer) { return rotateBlock(TE.getWorldObj(), TE.xCoord, TE.yCoord, TE.zCoord, ForgeDirection.UP); } @Override /** * Alters block type. */ protected boolean onHammerRightClick(TEBase TE, EntityPlayer entityPlayer) { Slope slope = Slope.getSlope(TE); Slope newSlope = slope.slopeType.getNextSlopeType(slope); TE.setData(newSlope.slopeID); return true; } @SideOnly(Side.CLIENT) @Override /** * Returns a list of blocks with the same ID, but different meta (eg: wood returns 4 blocks) */ public void getSubBlocks(Item item, CreativeTabs creativeTabs, List list) { list.add(new ItemStack(item, 1, META_WEDGE )); list.add(new ItemStack(item, 1, META_OBLIQUE_INT)); list.add(new ItemStack(item, 1, META_OBLIQUE_EXT)); list.add(new ItemStack(item, 1, META_PRISM )); list.add(new ItemStack(item, 1, META_PRISM_SLOPE)); } @Override /** * Damages hammer with a chance to not damage. */ protected void damageItemWithChance(World world, EntityPlayer entityPlayer) { if (world.rand.nextFloat() <= ItemRegistry.itemHammerDamageChanceFromSlopes) { super.damageItemWithChance(world, entityPlayer); } } @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) { if (!rayTracing) { TEBase TE = getTileEntity(blockAccess, x, y, z); if (TE != null) { Slope slope = Slope.getSlope(TE); switch (slope.getPrimaryType()) { case PRISM: case PRISM_1P: case PRISM_2P: case PRISM_3P: case PRISM_4P: if (slope.isPositive) { setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.5F, 1.0F); } else { setBlockBounds(0.0F, 0.5F, 0.0F, 1.0F, 1.0F, 1.0F); } break; default: setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); break; } } } } @Override /** * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. Args: world, * x, y, z, startVec, endVec */ public MovingObjectPosition collisionRayTrace(World world, int x, int y, int z, Vec3 startVec, Vec3 endVec) { TEBase TE = getTileEntity(world, x, y, z); MovingObjectPosition finalTrace = null; if (TE != null) { Slope slope = Slope.getSlope(TE); SlopeUtil slopeUtil = new SlopeUtil(); int numPasses = slopeUtil.getNumPasses(slope); int precision = slopeUtil.getNumBoxesPerPass(slope); rayTracing = true; /* Determine if ray trace is a hit on slope. */ for (int pass = 0; pass < numPasses; ++pass) { for (int slice = 0; slice < precision && finalTrace == null; ++slice) { float[] box = slopeUtil.genBounds(slope, slice, precision, pass); if (box != null) { setBlockBounds(box[0], box[1], box[2], box[3], box[4], box[5]); finalTrace = super.collisionRayTrace(world, x, y, z, startVec, endVec); } } if (slope.type.equals(Type.OBLIQUE_EXT)) { --precision; } } rayTracing = false; /* Determine true face hit since sloped faces are two or more shared faces. */ if (finalTrace != null) { setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); finalTrace = super.collisionRayTrace(world, x, y, z, startVec, endVec); } } return finalTrace; } @Override /** * Adds all intersecting collision boxes to a list. (Be sure to only add boxes to the list if they intersect the * mask.) Parameters: World, X, Y, Z, mask, list, colliding entity */ public void addCollisionBoxesToList(World world, int x, int y, int z, AxisAlignedBB axisAlignedBB, List list, Entity entity) { TEBase TE = getTileEntity(world, x, y, z); if (TE != null) { AxisAlignedBB box = null; Slope slope = Slope.getSlope(TE); SlopeUtil slopeUtil = new SlopeUtil(); int precision = slopeUtil.getNumBoxesPerPass(slope); int numPasses = slopeUtil.getNumPasses(slope); for (int pass = 0; pass < numPasses; ++pass) { for (int slice = 0; slice < precision; ++slice) { float[] dim = slopeUtil.genBounds(slope, slice, precision, pass); if (dim != null) { box = AxisAlignedBB.getBoundingBox(x + dim[0], y + dim[1], z + dim[2], x + dim[3], y + dim[4], z + dim[5]); } if (box != null && axisAlignedBB.intersectsWith(box)) { list.add(box); } } if (slope.type.equals(Type.OBLIQUE_EXT)) { --precision; } } } } @Override /** * Checks if the block is a solid face on the given side, used by placement logic. */ public boolean isSideSolid(IBlockAccess blockAccess, int x, int y, int z, ForgeDirection side) { TEBase TE = getTileEntity(blockAccess, x, y, z); if (TE != null) { if (isBlockSolid(blockAccess, x, y, z)) { return Slope.getSlope(TE).isFaceFull(side); } } return false; } @Override /** * Returns whether sides share faces based on sloping property and face shape. */ protected boolean shareFaces(TEBase TE_adj, TEBase TE_src, ForgeDirection side_adj, ForgeDirection side_src) { if (TE_adj.getBlockType() == this) { Slope slope_src = Slope.getSlope(TE_src); Slope slope_adj = Slope.getSlope(TE_adj); if (!slope_adj.hasSide(side_adj)) { return false; } else if (slope_src.getFaceBias(side_src) == slope_adj.getFaceBias(side_adj)) { return true; } else { return false; } } return super.shareFaces(TE_adj, TE_src, side_adj, side_src); } @Override /** * Called when block is placed in world. */ public int onBlockPlaced(World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ, int metadata) { EventHandler.eventFace = side; EventHandler.hitX = hitX; EventHandler.hitY = hitY; EventHandler.hitZ = hitZ; return metadata; } /** * Returns wedge slope orientation based on side clicked and hit coordinates. */ private int getWedgeOrientation(ForgeDirection dir, int side, double hitX, double hitY, double hitZ) { switch (side) { case 2: hitX = 1.0F - hitX; break; case 4: hitX = hitZ; break; case 5: hitX = 1.0F - hitZ; break; } int slopeID; if (side > 1) { if (hitY > 0.5F && hitX > 1.0F - hitY && hitX < hitY) { slopeID = side + 2; } else if (hitY < 0.5F && hitX < 1.0F - hitY && hitX > hitY) { slopeID = side + 6; } else if (hitX < 0.2F) { slopeID = side == 2 ? 1 : side == 3 ? 0 : side == 4 ? 3 : 2; } else if (hitX > 0.8F){ slopeID = side == 2 ? 2 : side == 3 ? 3 : side == 4 ? 1 : 0; } else if (hitY > 0.5F) { slopeID = side + 2; } else { // hitY < 0.5F slopeID = side + 6; } } else { slopeID = side + 12; } if (slopeID > 11) { switch (dir) { case NORTH: return slopeID == 12 ? Slope.ID_WEDGE_NEG_S : Slope.ID_WEDGE_POS_S; case SOUTH: return slopeID == 12 ? Slope.ID_WEDGE_NEG_N : Slope.ID_WEDGE_POS_N; case WEST: return slopeID == 12 ? Slope.ID_WEDGE_NEG_E : Slope.ID_WEDGE_POS_E; case EAST: return slopeID == 12 ? Slope.ID_WEDGE_NEG_W : Slope.ID_WEDGE_POS_W; default: return 0; } } else { return slopeID; } } private final int CORNER_SE = 0; private final int CORNER_NE = 1; private final int CORNER_NW = 2; private final int CORNER_SW = 3; /** * Returns general slope orientation based on side clicked and hit coordinates. */ private int getCorner(float rotationYaw) { switch ((MathHelper.floor_double(rotationYaw * 4.0F / 360.0F) & 3) % 4) { case 0: return CORNER_NE; case 1: return CORNER_SE; case 2: return CORNER_SW; default: return CORNER_NW; } } @Override /** * Called when the block is placed in the world. * Uses cardinal direction to adjust metadata if player clicks top or bottom face of block. */ 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); if (TE != null) { int slopeID = 0; int metadata = world.getBlockMetadata(x, y, z); boolean isPositive = EventHandler.eventFace > 1 && EventHandler.hitY < 0.5F || EventHandler.eventFace == 1; int corner = getCorner(entityLiving.rotationYaw); ForgeDirection dir = EntityLivingUtil.getFacing(entityLiving).getOpposite(); switch (metadata) { case META_WEDGE: slopeID = getWedgeOrientation(dir, EventHandler.eventFace, EventHandler.hitX, EventHandler.hitY, EventHandler.hitZ); if (!entityLiving.isSneaking()) { slopeID = SlopeTransform.transformWedge(world, slopeID, x, y, z); TE.setData(slopeID); SlopeTransform.transformAdjacentWedges(world, slopeID, x, y, z); } break; case META_OBLIQUE_INT: switch (corner) { case CORNER_SE: slopeID = isPositive ? Slope.ID_OBL_INT_POS_SE : Slope.ID_OBL_INT_NEG_SE; break; case CORNER_NE: slopeID = isPositive ? Slope.ID_OBL_INT_POS_NE : Slope.ID_OBL_INT_NEG_NE; break; case CORNER_NW: slopeID = isPositive ? Slope.ID_OBL_INT_POS_NW : Slope.ID_OBL_INT_NEG_NW; break; case CORNER_SW: slopeID = isPositive ? Slope.ID_OBL_INT_POS_SW : Slope.ID_OBL_INT_NEG_SW; break; } break; case META_OBLIQUE_EXT: switch (corner) { case CORNER_SE: slopeID = isPositive ? Slope.ID_OBL_EXT_POS_SE : Slope.ID_OBL_EXT_NEG_SE; break; case CORNER_NE: slopeID = isPositive ? Slope.ID_OBL_EXT_POS_NE : Slope.ID_OBL_EXT_NEG_NE; break; case CORNER_NW: slopeID = isPositive ? Slope.ID_OBL_EXT_POS_NW : Slope.ID_OBL_EXT_NEG_NW; break; case CORNER_SW: slopeID = isPositive ? Slope.ID_OBL_EXT_POS_SW : Slope.ID_OBL_EXT_NEG_SW; break; } break; case META_PRISM: if (isPositive) { slopeID = Slope.ID_PRISM_POS; if (!entityLiving.isSneaking()) { slopeID = SlopeTransform.transformPrism(world, slopeID, x, y, z); TE.setData(slopeID); SlopeTransform.transformAdjacentPrisms(world, x, y, z); } } else { slopeID = Slope.ID_PRISM_NEG; } break; case META_PRISM_SLOPE: switch (dir) { case NORTH: slopeID = Slope.ID_PRISM_WEDGE_POS_S; break; case SOUTH: slopeID = Slope.ID_PRISM_WEDGE_POS_N; break; case WEST: slopeID = Slope.ID_PRISM_WEDGE_POS_E; break; case EAST: slopeID = Slope.ID_PRISM_WEDGE_POS_W; break; default: {} } break; } TE.setData(slopeID); } } @Override public boolean canCoverSide(TEBase TE, World world, int x, int y, int z, int side) { return super.canCoverSide(TE, world, x, y, z, side) || isSideSolid(world, x, y, z, ForgeDirection.getOrientation(side)); } @Override /** * The type of render function that is called for this block */ public int getRenderType() { return BlockRegistry.carpentersSlopeRenderID; } @Override public ForgeDirection[] getValidRotations(World worldObj, int x, int y,int z) { return new ForgeDirection[] { ForgeDirection.UP, ForgeDirection.DOWN }; } @Override public boolean rotateBlock(World world, int x, int y, int z, ForgeDirection axis) { return Slope.rotate(world, x, y, z, axis); } }