/*
* Copyright (c) CovertJaguar, 2014 http://railcraft.info
*
* This code is the property of CovertJaguar
* and may only be used with explicit written
* permission unless otherwise specified on the
* license page at http://railcraft.info/wiki/info:license.
*/
package mods.railcraft.common.blocks.tracks;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.world.IBlockAccess;
import net.minecraft.util.IIcon;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import mods.railcraft.api.carts.CartTools;
import mods.railcraft.client.util.textures.TextureAtlasSheet;
import mods.railcraft.common.plugins.forge.PowerPlugin;
import mods.railcraft.common.plugins.forge.WorldPlugin;
import mods.railcraft.common.util.misc.Game;
import net.minecraft.block.BlockRailBase;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.EntityLivingBase;
/**
* Implementation of the iron ladder blocks. Iron ladders act much like normal
* (wooden) ladders. Climbing down iron ladders is a bit faster than climbing
* down normal ladders. Additionally, minecarts can run down vertically on iron
* ladders, as if they were vertical rail tracks.
*
* @author DizzyDragon
*/
public class BlockTrackElevator extends Block {
/**
* The upward velocity of an entity climbing the ladder.
*/
public static double CLIMB_UP_VELOCITY = 0.2;
/**
* The downward velocity of an entity climbing the ladder
*/
public static double CLIMB_DOWN_VELOCITY = -0.3;
/**
* The inverse of the downward motion an entity gets within a single update
* of the game engine due to gravity.
*/
public static double FALL_DOWN_CORRECTION = 0.039999999105930328D;
/**
* Metadata values for the direction the ladder is facing.
*/
public static final int FACING_EAST_METADATA_VALUE = 2,
FACING_WEST_METADATA_VALUE = 3,
FACING_NORTH_METADATA_VALUE = 4,
FACING_SOUTH_METADATA_VALUE = 5;
/**
* Velocity at which a minecart travels up on the rail when activated
*/
public static final double RIDE_UP_VELOCITY = +0.4;
/**
* Velocity at which a minecart travels down on the rail when not activated
*/
public static final double RIDE_DOWN_VELOCITY = -0.4;
/**
* The bits of the metadata that are used for storing the direction of the
* ladder. Use the bitwise-and operator (&) on the metadata to discard all
* other data from a metadata value.
*/
public static final int BLOCK_FACING_DATA_METADATA_MASK = 0x0007;
private final int renderType;
private IIcon[] texture;
public BlockTrackElevator(int renderId) {
super(new MaterialElevator());
// setBlockName(name);
setHardness(1.05F);
setStepSound(soundTypeMetal);
this.renderType = renderId;
setCreativeTab(CreativeTabs.tabTransport);
}
@Override
public MovingObjectPosition collisionRayTrace(World world, int i, int j, int k, Vec3 vec3d, Vec3 vec3d1) {
setBlockBoundsBasedOnState(world, i, j, k);
return super.collisionRayTrace(world, i, j, k, vec3d, vec3d1);
}
@Override
public void setBlockBoundsBasedOnState(IBlockAccess world, int i, int j, int k) {
int meta = getLadderFacingMetadata(world, i, j, k);
float f = 0.125F;
if (meta == 2)
setBlockBounds(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
if (meta == 3)
setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
if (meta == 4)
setBlockBounds(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
if (meta == 5)
setBlockBounds(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
}
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int i, int j, int k) {
return null;
}
@Override
public AxisAlignedBB getSelectedBoundingBoxFromPool(World world, int i, int j, int k) {
setBlockBoundsBasedOnState(world, i, j, k);
return AxisAlignedBB.getBoundingBox((double) i + minX, (double) j + minY, (double) k + minZ, (double) i + maxX, (double) j + maxY, (double) k + maxZ);
}
@Override
public int getRenderType() {
return renderType;
}
@Override
public boolean isLadder(IBlockAccess world, int x, int y, int z, EntityLivingBase entity) {
return true;
}
@Override
public boolean isOpaqueCube() {
return false;
}
@Override
public boolean renderAsNormalBlock() {
return false;
}
public boolean isACube() {
return false;
}
@Override
public IIcon getIcon(int side, int meta) {
boolean powered = (meta & 8) != 0;
if (powered)
return texture[0];
return texture[1];
}
@Override
public void registerBlockIcons(IIconRegister iconRegister) {
texture = TextureAtlasSheet.unstitchIcons(iconRegister, "railcraft:tracks/track.elevator", 2);
}
@Override
public boolean canPlaceBlockAt(World world, int i, int j, int k) {
if (world.isSideSolid(i - 1, j, k, ForgeDirection.EAST))
return true;
if (world.isSideSolid(i + 1, j, k, ForgeDirection.WEST))
return true;
if (world.isSideSolid(i, j, k - 1, ForgeDirection.SOUTH))
return true;
return world.isSideSolid(i, j, k + 1, ForgeDirection.NORTH);
}
@Override
public int onBlockPlaced(World world, int x, int y, int z, int side, float par6, float par7, float par8, int meta) {
if ((meta == 0 || side == 2) && world.isSideSolid(x, y, z + 1, ForgeDirection.NORTH))
meta = 2;
if ((meta == 0 || side == 3) && world.isSideSolid(x, y, z - 1, ForgeDirection.SOUTH))
meta = 3;
if ((meta == 0 || side == 4) && world.isSideSolid(x + 1, y, z, ForgeDirection.WEST))
meta = 4;
if ((meta == 0 || side == 5) && world.isSideSolid(x - 1, y, z, ForgeDirection.EAST))
meta = 5;
return meta;
}
@Override
public void onPostBlockPlaced(World world, int x, int y, int z, int meta) {
setBlockBoundsBasedOnState(world, x, y, z);
if (TrackTools.isRailBlockAt(world, x, y - 1, z)) {
Block block = WorldPlugin.getBlock(world, x, y - 1, z);
BlockRailBase railBlock = (BlockRailBase) block;
if (railBlock.canMakeSlopes(world, x, y - 1, z)) {
int trackMeta = railBlock.getBasicRailMetadata(world, null, x, y - 1, z);
int ladderMeta = getLadderFacingMetadata(world, x, y, z);
int outputMeta = 0;
if (trackMeta == 0 && ladderMeta == 2)
outputMeta = 5;
else if (trackMeta == 0 && ladderMeta == 3)
outputMeta = 4;
else if (trackMeta == 1 && ladderMeta == 4)
outputMeta = 2;
else if (trackMeta == 1 && ladderMeta == 5)
outputMeta = 3;
if (outputMeta != 0) {
if (railBlock.isPowered())
outputMeta = outputMeta | (world.getBlockMetadata(x, y - 1, z) & 8);
world.setBlockMetadataWithNotify(x, y - 1, z, outputMeta, 3);
}
}
}
meta = world.getBlockMetadata(x, y, z);
boolean powered = (meta & 8) != 0;
if (powered ^ isPowered(world, x, y, z))
world.setBlockMetadataWithNotify(x, y, z, meta ^ 8, 3);
world.markBlockForUpdate(x, y, z);
}
@Override
public void dropBlockAsItemWithChance(World world, int i, int j, int k, int l, float f, int i1) {
// System.out.println("dropping item");
super.dropBlockAsItemWithChance(world, i, j, k, l, f, i1);
}
@Override
public void onNeighborBlockChange(World world, int i, int j, int k, Block block) {
int meta = world.getBlockMetadata(i, j, k);
int ladderMeta = getLadderFacingMetadata(world, i, j, k);
boolean valid = false;
if (ladderMeta == 2 && world.isSideSolid(i, j, k + 1, ForgeDirection.NORTH))
valid = true;
if (ladderMeta == 3 && world.isSideSolid(i, j, k - 1, ForgeDirection.SOUTH))
valid = true;
if (ladderMeta == 4 && world.isSideSolid(i + 1, j, k, ForgeDirection.WEST))
valid = true;
if (ladderMeta == 5 && world.isSideSolid(i - 1, j, k, ForgeDirection.EAST))
valid = true;
if (!valid) {
dropBlockAsItem(world, i, j, k, ladderMeta, 0);
world.setBlockToAir(i, j, k);
} else {
boolean powered = (meta & 8) != 0;
if (powered ^ isPowered(world, i, j, k)) {
// System.out.println("Power change");
world.setBlockMetadataWithNotify(i, j, k, meta ^ 8, 3);
world.markBlockForUpdate(i, j, k);
}
}
}
@Override
public void onEntityCollidedWithBlock(World world, int i, int j, int k, Entity entity) {
entity.fallDistance = 0;
if (Game.isNotHost(world) || !(entity instanceof EntityMinecart))
return;
minecartInteraction(world, (EntityMinecart) entity, i, j, k);
}
public int getLadderFacingMetadata(IBlockAccess world, int i, int j, int k) {
return world.getBlockMetadata(i, j, k) & BLOCK_FACING_DATA_METADATA_MASK;
}
public boolean getPoweredBit(World world, int i, int j, int k) {
return (world.getBlockMetadata(i, j, k) & 8) != 0;
}
////////////////////////////////////////////////////////////////////////////
// PROTECTED //
////////////////////////////////////////////////////////////////////////////
protected boolean isPowered(World world, int x, int y, int z) {
int meta = getLadderFacingMetadata(world, x, y, z);
if (world.getBlock(x, y - 1, z) == this && meta == getLadderFacingMetadata(world, x, y - 1, z))
if (PowerPlugin.isBlockBeingPowered(world, x, y - 1, z))
return true;
if (PowerPlugin.isBlockBeingPowered(world, x, y, z))
return true;
return world.getBlock(x, y + 1, z) == this && meta == getLadderFacingMetadata(world, x, y + 1, z) && isPowered(world, x, y + 1, z);
}
/**
* Updates the state of a single minecart that is whithin the block's area
* of effect according to the state of the block.
*
* @param world the world in which the block resides
* @param i the x-coordinate of the block
* @param j the y-coordinate of the block
* @param k the z-coordinate of the block
* @param cart the minecart for which the state will be updated. It is
* assumed that the minecart is whithin the area of effect of the block
*/
protected void minecartInteraction(World world, EntityMinecart cart, int i, int j, int k) {
cart.getEntityData().setByte("elevator", (byte) 20);
boolean powered = getPoweredBit(world, i, j, k);
if (powered)
if (world.getBlock(i, j + 1, k) == this || isOffloadRail(world, i, j + 1, k)) {
boolean empty = true;
for (EntityMinecart c : CartTools.getMinecartsAt(world, i, j + 1, k, 0.2f)) {
if (c != cart)
empty = false;
}
if ((getPoweredBit(world, i, j + 1, k) || isOffloadRail(world, i, j + 1, k)) && empty)
cart.motionY = RIDE_UP_VELOCITY + FALL_DOWN_CORRECTION;
else
if (pushMinecartOntoRail(world, i, j, k, cart))
return;
else {
cart.setPosition(cart.posX, j + 0.5f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
}
} else
cart.setPosition(cart.posX, j + 0.5f, cart.posZ);
else
if (world.getBlock(i, j - 1, k) != this) {
pushMinecartOntoRail(world, i, j, k, cart);
return;
} else {
boolean empty = true;
for (EntityMinecart c : CartTools.getMinecartsAt(world, i, j - 1, k, 0.2f)) {
if (c != cart)
empty = false;
}
if (empty)
cart.motionY = RIDE_DOWN_VELOCITY + FALL_DOWN_CORRECTION;
else {
cart.setPosition(cart.posX, j + 0.5f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
}
}
if (powered || !TrackTools.isRailBlockAt(world, i, j - 1, k)) {
if (TrackTools.isRailBlockAt(world, i, j - 1, k) || TrackTools.isRailBlockAt(world, i, j - 2, k))
cart.setCanUseRail(false);
else
cart.setCanUseRail(true);
keepMinecartConnected(world, i, j, k, cart);
} else
cart.setCanUseRail(true);
// RailcraftUtils.resetFallDistance(cart);
if (powered)
pushMinecartOnSupportingBlockIfPossible(world, i, j, k, cart);
}
/**
* Adjusts the motion and rotationyaw of a minecart so that it stays in
* position and alligned to the iron ladder.
*
* @param world the world in which the block resides
* @param x the x-coordinate of the block
* @param y the y-coordinate of the block
* @param z the z-coordinate of the block
* @param minecart the minecart for which motion and rotation will be
* adjusted
*/
protected void keepMinecartConnected(World world, int x, int y, int z, EntityMinecart minecart) {
minecart.motionX = (x + 0.5) - minecart.posX;
minecart.motionZ = (z + 0.5) - minecart.posZ;
allignMinecart(world, x, y, z, minecart);
}
/**
* Alligns the minecart to the ladder to the ladder
*
* @param world the world in which the block resides
* @param x the x-coordinate of the block
* @param y the y-coordinate of the block
* @param z the z-coordinate of the block
* @param minecart the minecart for which motion and rotation will be
* adjusted
*/
protected void allignMinecart(World world, int x, int y, int z, EntityMinecart minecart) {
switch (getLadderFacingMetadata(world, x, y, z)) {
case FACING_NORTH_METADATA_VALUE:
case FACING_SOUTH_METADATA_VALUE:
if (minecart.rotationYaw <= 90.0f || minecart.rotationYaw > 270.0f)
minecart.rotationYaw = 0.0f;
else
minecart.rotationYaw = 180.0f;
return;
case FACING_EAST_METADATA_VALUE:
case FACING_WEST_METADATA_VALUE:
if (minecart.rotationYaw > 180.0f)
minecart.rotationYaw = 270.0f;
else
minecart.rotationYaw = 90.0f;
}
}
private boolean isOffloadRail(World world, int x, int y, int z) {
if (world.getBlock(x, y, z) != this)
switch (world.getBlockMetadata(x, y - 1, z) & BLOCK_FACING_DATA_METADATA_MASK) {
case FACING_EAST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, x, y, z + 1))
return true;
case FACING_WEST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, x, y, z - 1))
return true;
case FACING_NORTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, x + 1, y, z))
return true;
case FACING_SOUTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, x - 1, y, z))
return true;
default:
return false;
}
else
return false;
}
/**
* Pushes a minecart onto the block on which the ladder is placed if it is
* possible. It is only possible to push the minecart if there is air
* directly above the ladder block and if the block directly above the
* supporting block is a rail.
*
* @param world the world in which the block resides
* @param i the x-coordinate of the ladder block
* @param j the y-coordinate of the ladder block
* @param k the z-coordinate of the ladder block
* @param minecart the minecart that is pushed which onto the block if
* possible
* @return true if the minecart can be pushed onto the supporting block,
* otherwise false
*/
private boolean pushMinecartOnSupportingBlockIfPossible(World world, int i, int j, int k, EntityMinecart minecart) {
if (!world.getBlock(i, j, k).getMaterial().isSolid())
switch (world.getBlockMetadata(i, j, k) & BLOCK_FACING_DATA_METADATA_MASK) {
case FACING_EAST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i, j + 1, k + 1)) {
minecart.motionY = RIDE_UP_VELOCITY;
minecart.motionZ = RIDE_UP_VELOCITY;
}
return true;
case FACING_WEST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i, j + 1, k - 1)) {
minecart.motionY = RIDE_UP_VELOCITY;
minecart.motionZ = -RIDE_UP_VELOCITY;
}
return true;
case FACING_NORTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i + 1, j + 1, k)) {
minecart.motionY = RIDE_UP_VELOCITY;
minecart.motionX = RIDE_UP_VELOCITY;
}
return true;
case FACING_SOUTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i - 1, j + 1, k)) {
minecart.motionY = RIDE_UP_VELOCITY;
minecart.motionX = -RIDE_UP_VELOCITY;
}
return true;
default:
return false;
}
else
return false;
}
private boolean pushMinecartOntoRail(World world, int i, int j, int k, EntityMinecart cart) {
cart.setCanUseRail(true);
switch (world.getBlockMetadata(i, j, k) & BLOCK_FACING_DATA_METADATA_MASK) {
case FACING_EAST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i, j, k - 1)) {
cart.setPosition(cart.posX, j + 0.6f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
cart.motionZ = -RIDE_UP_VELOCITY;
return true;
}
break;
case FACING_WEST_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i, j, k + 1)) {
cart.setPosition(cart.posX, j + 0.6f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
cart.motionZ = RIDE_UP_VELOCITY;
return true;
}
break;
case FACING_NORTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i - 1, j, k)) {
cart.setPosition(cart.posX, j + 0.6f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
cart.motionX = -RIDE_UP_VELOCITY;
return true;
}
break;
case FACING_SOUTH_METADATA_VALUE:
if (TrackTools.isRailBlockAt(world, i + 1, j, k)) {
cart.setPosition(cart.posX, j + 0.6f, cart.posZ);
cart.motionY = FALL_DOWN_CORRECTION;
cart.motionX = RIDE_UP_VELOCITY;
return true;
}
break;
default:
return false;
}
return false;
}
@Override
public boolean canBeReplacedByLeaves(IBlockAccess world, int x, int y, int z) {
return false;
}
}