504 lines
20 KiB
Java
504 lines
20 KiB
Java
package net.minecraft.block;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import java.util.List;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.block.material.EnumPushReaction;
|
|
import net.minecraft.block.material.Material;
|
|
import net.minecraft.block.properties.IProperty;
|
|
import net.minecraft.block.properties.PropertyBool;
|
|
import net.minecraft.block.state.BlockFaceShape;
|
|
import net.minecraft.block.state.BlockPistonStructureHelper;
|
|
import net.minecraft.block.state.BlockStateContainer;
|
|
import net.minecraft.block.state.IBlockState;
|
|
import net.minecraft.creativetab.CreativeTabs;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.EntityLivingBase;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.init.Blocks;
|
|
import net.minecraft.init.SoundEvents;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.tileentity.TileEntityPiston;
|
|
import net.minecraft.util.EnumFacing;
|
|
import net.minecraft.util.Mirror;
|
|
import net.minecraft.util.Rotation;
|
|
import net.minecraft.util.SoundCategory;
|
|
import net.minecraft.util.math.AxisAlignedBB;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.world.IBlockAccess;
|
|
import net.minecraft.world.World;
|
|
|
|
public class BlockPistonBase extends BlockDirectional
|
|
{
|
|
public static final PropertyBool EXTENDED = PropertyBool.create("extended");
|
|
protected static final AxisAlignedBB PISTON_BASE_EAST_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.75D, 1.0D, 1.0D);
|
|
protected static final AxisAlignedBB PISTON_BASE_WEST_AABB = new AxisAlignedBB(0.25D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
|
|
protected static final AxisAlignedBB PISTON_BASE_SOUTH_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.75D);
|
|
protected static final AxisAlignedBB PISTON_BASE_NORTH_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.25D, 1.0D, 1.0D, 1.0D);
|
|
protected static final AxisAlignedBB PISTON_BASE_UP_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
|
|
protected static final AxisAlignedBB PISTON_BASE_DOWN_AABB = new AxisAlignedBB(0.0D, 0.25D, 0.0D, 1.0D, 1.0D, 1.0D);
|
|
/** This piston is the sticky one? */
|
|
private final boolean isSticky;
|
|
|
|
public BlockPistonBase(boolean isSticky)
|
|
{
|
|
super(Material.PISTON);
|
|
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH).withProperty(EXTENDED, Boolean.valueOf(false)));
|
|
this.isSticky = isSticky;
|
|
this.setSoundType(SoundType.STONE);
|
|
this.setHardness(0.5F);
|
|
this.setCreativeTab(CreativeTabs.REDSTONE);
|
|
}
|
|
|
|
public boolean causesSuffocation(IBlockState state)
|
|
{
|
|
return !((Boolean)state.getValue(EXTENDED)).booleanValue();
|
|
}
|
|
|
|
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
|
|
{
|
|
if (((Boolean)state.getValue(EXTENDED)).booleanValue())
|
|
{
|
|
switch ((EnumFacing)state.getValue(FACING))
|
|
{
|
|
case DOWN:
|
|
return PISTON_BASE_DOWN_AABB;
|
|
case UP:
|
|
default:
|
|
return PISTON_BASE_UP_AABB;
|
|
case NORTH:
|
|
return PISTON_BASE_NORTH_AABB;
|
|
case SOUTH:
|
|
return PISTON_BASE_SOUTH_AABB;
|
|
case WEST:
|
|
return PISTON_BASE_WEST_AABB;
|
|
case EAST:
|
|
return PISTON_BASE_EAST_AABB;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FULL_BLOCK_AABB;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the block is solid enough on the top side to support other blocks, like redstone components.
|
|
*/
|
|
public boolean isTopSolid(IBlockState state)
|
|
{
|
|
return !((Boolean)state.getValue(EXTENDED)).booleanValue() || state.getValue(FACING) == EnumFacing.DOWN;
|
|
}
|
|
|
|
public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List<AxisAlignedBB> collidingBoxes, @Nullable Entity entityIn, boolean isActualState)
|
|
{
|
|
addCollisionBoxToList(pos, entityBox, collidingBoxes, state.getBoundingBox(worldIn, pos));
|
|
}
|
|
|
|
/**
|
|
* Used to determine ambient occlusion and culling when rebuilding chunks for render
|
|
*/
|
|
public boolean isOpaqueCube(IBlockState state)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Called by ItemBlocks after a block is set in the world, to allow post-place logic
|
|
*/
|
|
public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
|
|
{
|
|
worldIn.setBlockState(pos, state.withProperty(FACING, EnumFacing.getDirectionFromEntityLiving(pos, placer)), 2);
|
|
|
|
if (!worldIn.isRemote)
|
|
{
|
|
this.checkForMove(worldIn, pos, state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a neighboring block was changed and marks that this state should perform any checks during a neighbor
|
|
* change. Cases may include when redstone power is updated, cactus blocks popping off due to a neighboring solid
|
|
* block, etc.
|
|
*/
|
|
public void neighborChanged(IBlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos)
|
|
{
|
|
if (!worldIn.isRemote)
|
|
{
|
|
this.checkForMove(worldIn, pos, state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called after the block is set in the Chunk data, but before the Tile Entity is set
|
|
*/
|
|
public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state)
|
|
{
|
|
if (!worldIn.isRemote && worldIn.getTileEntity(pos) == null)
|
|
{
|
|
this.checkForMove(worldIn, pos, state);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called by ItemBlocks just before a block is actually set in the world, to allow for adjustments to the
|
|
* IBlockstate
|
|
*/
|
|
public IBlockState getStateForPlacement(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
|
|
{
|
|
return this.getDefaultState().withProperty(FACING, EnumFacing.getDirectionFromEntityLiving(pos, placer)).withProperty(EXTENDED, Boolean.valueOf(false));
|
|
}
|
|
|
|
private void checkForMove(World worldIn, BlockPos pos, IBlockState state)
|
|
{
|
|
EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);
|
|
boolean flag = this.shouldBeExtended(worldIn, pos, enumfacing);
|
|
|
|
if (flag && !((Boolean)state.getValue(EXTENDED)).booleanValue())
|
|
{
|
|
if ((new BlockPistonStructureHelper(worldIn, pos, enumfacing, true)).canMove())
|
|
{
|
|
worldIn.addBlockEvent(pos, this, 0, enumfacing.getIndex());
|
|
}
|
|
}
|
|
else if (!flag && ((Boolean)state.getValue(EXTENDED)).booleanValue())
|
|
{
|
|
worldIn.addBlockEvent(pos, this, 1, enumfacing.getIndex());
|
|
}
|
|
}
|
|
|
|
private boolean shouldBeExtended(World worldIn, BlockPos pos, EnumFacing facing)
|
|
{
|
|
for (EnumFacing enumfacing : EnumFacing.values())
|
|
{
|
|
if (enumfacing != facing && worldIn.isSidePowered(pos.offset(enumfacing), enumfacing))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (worldIn.isSidePowered(pos, EnumFacing.DOWN))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
BlockPos blockpos = pos.up();
|
|
|
|
for (EnumFacing enumfacing1 : EnumFacing.values())
|
|
{
|
|
if (enumfacing1 != EnumFacing.DOWN && worldIn.isSidePowered(blockpos.offset(enumfacing1), enumfacing1))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called on server when World#addBlockEvent is called. If server returns true, then also called on the client. On
|
|
* the Server, this may perform additional changes to the world, like pistons replacing the block with an extended
|
|
* base. On the client, the update may involve replacing tile entities or effects such as sounds or particles
|
|
*/
|
|
public boolean eventReceived(IBlockState state, World worldIn, BlockPos pos, int id, int param)
|
|
{
|
|
EnumFacing enumfacing = (EnumFacing)state.getValue(FACING);
|
|
|
|
if (!worldIn.isRemote)
|
|
{
|
|
boolean flag = this.shouldBeExtended(worldIn, pos, enumfacing);
|
|
|
|
if (flag && id == 1)
|
|
{
|
|
worldIn.setBlockState(pos, state.withProperty(EXTENDED, Boolean.valueOf(true)), 2);
|
|
return false;
|
|
}
|
|
|
|
if (!flag && id == 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (id == 0)
|
|
{
|
|
if (!this.doMove(worldIn, pos, enumfacing, true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
worldIn.setBlockState(pos, state.withProperty(EXTENDED, Boolean.valueOf(true)), 3);
|
|
worldIn.playSound((EntityPlayer)null, pos, SoundEvents.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5F, worldIn.rand.nextFloat() * 0.25F + 0.6F);
|
|
}
|
|
else if (id == 1)
|
|
{
|
|
TileEntity tileentity1 = worldIn.getTileEntity(pos.offset(enumfacing));
|
|
|
|
if (tileentity1 instanceof TileEntityPiston)
|
|
{
|
|
((TileEntityPiston)tileentity1).clearPistonTileEntity();
|
|
}
|
|
|
|
worldIn.setBlockState(pos, Blocks.PISTON_EXTENSION.getDefaultState().withProperty(BlockPistonMoving.FACING, enumfacing).withProperty(BlockPistonMoving.TYPE, this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT), 3);
|
|
worldIn.setTileEntity(pos, BlockPistonMoving.createTilePiston(this.getStateFromMeta(param), enumfacing, false, true));
|
|
|
|
if (this.isSticky)
|
|
{
|
|
BlockPos blockpos = pos.add(enumfacing.getFrontOffsetX() * 2, enumfacing.getFrontOffsetY() * 2, enumfacing.getFrontOffsetZ() * 2);
|
|
IBlockState iblockstate = worldIn.getBlockState(blockpos);
|
|
Block block = iblockstate.getBlock();
|
|
boolean flag1 = false;
|
|
|
|
if (block == Blocks.PISTON_EXTENSION)
|
|
{
|
|
TileEntity tileentity = worldIn.getTileEntity(blockpos);
|
|
|
|
if (tileentity instanceof TileEntityPiston)
|
|
{
|
|
TileEntityPiston tileentitypiston = (TileEntityPiston)tileentity;
|
|
|
|
if (tileentitypiston.getFacing() == enumfacing && tileentitypiston.isExtending())
|
|
{
|
|
tileentitypiston.clearPistonTileEntity();
|
|
flag1 = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!flag1 && !iblockstate.getBlock().isAir(iblockstate, worldIn, blockpos) && canPush(iblockstate, worldIn, blockpos, enumfacing.getOpposite(), false, enumfacing) && (iblockstate.getMobilityFlag() == EnumPushReaction.NORMAL || block == Blocks.PISTON || block == Blocks.STICKY_PISTON))
|
|
{
|
|
this.doMove(worldIn, pos, enumfacing, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
worldIn.setBlockToAir(pos.offset(enumfacing));
|
|
}
|
|
|
|
worldIn.playSound((EntityPlayer)null, pos, SoundEvents.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5F, worldIn.rand.nextFloat() * 0.15F + 0.6F);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean isFullCube(IBlockState state)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Nullable
|
|
public static EnumFacing getFacing(int meta)
|
|
{
|
|
int i = meta & 7;
|
|
return i > 5 ? null : EnumFacing.getFront(i);
|
|
}
|
|
|
|
/**
|
|
* Checks if the piston can push the given BlockState.
|
|
*/
|
|
public static boolean canPush(IBlockState blockStateIn, World worldIn, BlockPos pos, EnumFacing facing, boolean destroyBlocks, EnumFacing p_185646_5_)
|
|
{
|
|
Block block = blockStateIn.getBlock();
|
|
|
|
if (block == Blocks.OBSIDIAN)
|
|
{
|
|
return false;
|
|
}
|
|
else if (!worldIn.getWorldBorder().contains(pos))
|
|
{
|
|
return false;
|
|
}
|
|
else if (pos.getY() >= 0 && (facing != EnumFacing.DOWN || pos.getY() != 0))
|
|
{
|
|
if (pos.getY() <= worldIn.getHeight() - 1 && (facing != EnumFacing.UP || pos.getY() != worldIn.getHeight() - 1))
|
|
{
|
|
if (block != Blocks.PISTON && block != Blocks.STICKY_PISTON)
|
|
{
|
|
if (blockStateIn.getBlockHardness(worldIn, pos) == -1.0F)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (blockStateIn.getMobilityFlag())
|
|
{
|
|
case BLOCK:
|
|
return false;
|
|
case DESTROY:
|
|
return destroyBlocks;
|
|
case PUSH_ONLY:
|
|
return facing == p_185646_5_;
|
|
}
|
|
}
|
|
else if (((Boolean)blockStateIn.getValue(EXTENDED)).booleanValue())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !block.hasTileEntity(blockStateIn);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private boolean doMove(World worldIn, BlockPos pos, EnumFacing direction, boolean extending)
|
|
{
|
|
if (!extending)
|
|
{
|
|
worldIn.setBlockToAir(pos.offset(direction));
|
|
}
|
|
|
|
BlockPistonStructureHelper blockpistonstructurehelper = new BlockPistonStructureHelper(worldIn, pos, direction, extending);
|
|
|
|
if (!blockpistonstructurehelper.canMove())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
List<BlockPos> list = blockpistonstructurehelper.getBlocksToMove();
|
|
List<IBlockState> list1 = Lists.<IBlockState>newArrayList();
|
|
|
|
for (int i = 0; i < list.size(); ++i)
|
|
{
|
|
BlockPos blockpos = list.get(i);
|
|
list1.add(worldIn.getBlockState(blockpos).getActualState(worldIn, blockpos));
|
|
}
|
|
|
|
List<BlockPos> list2 = blockpistonstructurehelper.getBlocksToDestroy();
|
|
int k = list.size() + list2.size();
|
|
IBlockState[] aiblockstate = new IBlockState[k];
|
|
EnumFacing enumfacing = extending ? direction : direction.getOpposite();
|
|
|
|
for (int j = list2.size() - 1; j >= 0; --j)
|
|
{
|
|
BlockPos blockpos1 = list2.get(j);
|
|
IBlockState iblockstate = worldIn.getBlockState(blockpos1);
|
|
// Forge: With our change to how snowballs are dropped this needs to disallow to mimic vanilla behavior.
|
|
float chance = iblockstate.getBlock() instanceof BlockSnow ? -1.0f : 1.0f;
|
|
iblockstate.getBlock().dropBlockAsItemWithChance(worldIn, blockpos1, iblockstate, chance, 0);
|
|
worldIn.setBlockState(blockpos1, Blocks.AIR.getDefaultState(), 4);
|
|
--k;
|
|
aiblockstate[k] = iblockstate;
|
|
}
|
|
|
|
for (int l = list.size() - 1; l >= 0; --l)
|
|
{
|
|
BlockPos blockpos3 = list.get(l);
|
|
IBlockState iblockstate2 = worldIn.getBlockState(blockpos3);
|
|
worldIn.setBlockState(blockpos3, Blocks.AIR.getDefaultState(), 2);
|
|
blockpos3 = blockpos3.offset(enumfacing);
|
|
worldIn.setBlockState(blockpos3, Blocks.PISTON_EXTENSION.getDefaultState().withProperty(FACING, direction), 4);
|
|
worldIn.setTileEntity(blockpos3, BlockPistonMoving.createTilePiston(list1.get(l), direction, extending, false));
|
|
--k;
|
|
aiblockstate[k] = iblockstate2;
|
|
}
|
|
|
|
BlockPos blockpos2 = pos.offset(direction);
|
|
|
|
if (extending)
|
|
{
|
|
BlockPistonExtension.EnumPistonType blockpistonextension$enumpistontype = this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT;
|
|
IBlockState iblockstate3 = Blocks.PISTON_HEAD.getDefaultState().withProperty(BlockPistonExtension.FACING, direction).withProperty(BlockPistonExtension.TYPE, blockpistonextension$enumpistontype);
|
|
IBlockState iblockstate1 = Blocks.PISTON_EXTENSION.getDefaultState().withProperty(BlockPistonMoving.FACING, direction).withProperty(BlockPistonMoving.TYPE, this.isSticky ? BlockPistonExtension.EnumPistonType.STICKY : BlockPistonExtension.EnumPistonType.DEFAULT);
|
|
worldIn.setBlockState(blockpos2, iblockstate1, 4);
|
|
worldIn.setTileEntity(blockpos2, BlockPistonMoving.createTilePiston(iblockstate3, direction, true, true));
|
|
}
|
|
|
|
for (int i1 = list2.size() - 1; i1 >= 0; --i1)
|
|
{
|
|
worldIn.notifyNeighborsOfStateChange(list2.get(i1), aiblockstate[k++].getBlock(), false);
|
|
}
|
|
|
|
for (int j1 = list.size() - 1; j1 >= 0; --j1)
|
|
{
|
|
worldIn.notifyNeighborsOfStateChange(list.get(j1), aiblockstate[k++].getBlock(), false);
|
|
}
|
|
|
|
if (extending)
|
|
{
|
|
worldIn.notifyNeighborsOfStateChange(blockpos2, Blocks.PISTON_HEAD, false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert the given metadata into a BlockState for this Block
|
|
*/
|
|
public IBlockState getStateFromMeta(int meta)
|
|
{
|
|
return this.getDefaultState().withProperty(FACING, getFacing(meta)).withProperty(EXTENDED, Boolean.valueOf((meta & 8) > 0));
|
|
}
|
|
|
|
/**
|
|
* Convert the BlockState into the correct metadata value
|
|
*/
|
|
public int getMetaFromState(IBlockState state)
|
|
{
|
|
int i = 0;
|
|
i = i | ((EnumFacing)state.getValue(FACING)).getIndex();
|
|
|
|
if (((Boolean)state.getValue(EXTENDED)).booleanValue())
|
|
{
|
|
i |= 8;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Returns the blockstate with the given rotation from the passed blockstate. If inapplicable, returns the passed
|
|
* blockstate.
|
|
*/
|
|
public IBlockState withRotation(IBlockState state, Rotation rot)
|
|
{
|
|
return state.withProperty(FACING, rot.rotate((EnumFacing)state.getValue(FACING)));
|
|
}
|
|
|
|
/**
|
|
* Returns the blockstate with the given mirror of the passed blockstate. If inapplicable, returns the passed
|
|
* blockstate.
|
|
*/
|
|
public IBlockState withMirror(IBlockState state, Mirror mirrorIn)
|
|
{
|
|
return state.withRotation(mirrorIn.toRotation((EnumFacing)state.getValue(FACING)));
|
|
}
|
|
|
|
protected BlockStateContainer createBlockState()
|
|
{
|
|
return new BlockStateContainer(this, new IProperty[] {FACING, EXTENDED});
|
|
}
|
|
|
|
/* ======================================== FORGE START =====================================*/
|
|
public boolean rotateBlock(World world, BlockPos pos, EnumFacing axis)
|
|
{
|
|
IBlockState state = world.getBlockState(pos);
|
|
return !state.getValue(EXTENDED) && super.rotateBlock(world, pos, axis);
|
|
}
|
|
|
|
/**
|
|
* Get the geometry of the queried face at the given position and state. This is used to decide whether things like
|
|
* buttons are allowed to be placed on the face, or how glass panes connect to the face, among other things.
|
|
* <p>
|
|
* Common values are {@code SOLID}, which is the default, and {@code UNDEFINED}, which represents something that
|
|
* does not fit the other descriptions and will generally cause other things not to connect to the face.
|
|
*
|
|
* @return an approximation of the form of the given face
|
|
*/
|
|
public BlockFaceShape getBlockFaceShape(IBlockAccess worldIn, IBlockState state, BlockPos pos, EnumFacing face)
|
|
{
|
|
state = this.getActualState(state, worldIn, pos);
|
|
return state.getValue(FACING) != face.getOpposite() && ((Boolean)state.getValue(EXTENDED)).booleanValue() ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID;
|
|
}
|
|
} |