package choonster.testmod3.block;
import choonster.testmod3.util.VectorUtils;
import com.google.common.collect.ImmutableMap;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.IStringSerializable;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
import javax.vecmath.Matrix3d;
import java.util.List;
import java.util.Map;
/**
* A diagonal half-cube block, like the Carpenter's Slope from Carpenter's Blocks.
* <p>
* Test for this thread:
* http://www.minecraftforge.net/forum/index.php/topic,36713.0.html
*
* @author Choonster
*/
public class BlockPlane extends BlockTestMod3 {
/**
* The block's rotation around the y-axis.
*/
public static final IProperty<EnumFacing> HORIZONTAL_ROTATION = PropertyDirection.create("horizontal_rotation", EnumFacing.Plane.HORIZONTAL);
/**
* The block's rotation around the x-axis.
*/
public static final IProperty<EnumVerticalRotation> VERTICAL_ROTATION = PropertyEnum.create("vertical_rotation", EnumVerticalRotation.class);
public BlockPlane(Material material, MapColor mapColor, String blockName) {
super(material, mapColor, blockName);
}
public BlockPlane(Material materialIn, String blockName) {
super(materialIn, blockName);
}
{
setDefaultState(getBlockState().getBaseState().withProperty(HORIZONTAL_ROTATION, EnumFacing.NORTH).withProperty(VERTICAL_ROTATION, EnumVerticalRotation.UP));
}
/**
* The bounding boxes for each rotation.
* <ul>
* <li>Key: Left = Horizontal Rotation, Right = Vertical Rotation</li>
* <li>Value: Left = Base Bounding Box, Right = Top Bounding Box</li>
* </ul>
*/
private static final Map<Pair<EnumFacing, EnumVerticalRotation>, Pair<AxisAlignedBB, AxisAlignedBB>> ROTATED_BOUNDING_BOXES;
static {
final ImmutableMap.Builder<Pair<EnumFacing, EnumVerticalRotation>, Pair<AxisAlignedBB, AxisAlignedBB>> builder = ImmutableMap.builder();
// The base and top AABBs for the default rotation pair
final AxisAlignedBB baseBoundingBox = new AxisAlignedBB(0, 0, 0, 1, 0.5, 1);
final AxisAlignedBB topBoundingBox = new AxisAlignedBB(0, 0.5, 0, 1, 1, 0.5);
// For each horizontal and vertical rotation pair,
for (final EnumFacing horizontalRotation : HORIZONTAL_ROTATION.getAllowedValues()) {
for (final EnumVerticalRotation verticalRotation : VERTICAL_ROTATION.getAllowedValues()) {
// Get the horizontal (around the y axis) rotation angle and matrix
final double horizontalRotationAngle = VectorUtils.getHorizontalRotation(horizontalRotation);
final Matrix3d horizontalRotationMatrix = VectorUtils.getRotationMatrix(EnumFacing.Axis.Y, verticalRotation == EnumVerticalRotation.DOWN ? horizontalRotationAngle + Math.PI : horizontalRotationAngle);
// Get the vertical (around the x axis) rotation angle and matrix
final double verticalRotationAngle = verticalRotation.getAngle();
final Matrix3d verticalRotationMatrix = VectorUtils.getRotationMatrix(EnumFacing.Axis.X, verticalRotationAngle);
// Multiply the rotation matrices to combine them
final Matrix3d combinedRotationMatrix = new Matrix3d();
combinedRotationMatrix.mul(verticalRotationMatrix, horizontalRotationMatrix);
// Rotate the AABBs
final AxisAlignedBB rotatedBaseBoundingBox = VectorUtils.rotateAABB(baseBoundingBox, combinedRotationMatrix, true);
final AxisAlignedBB rotatedTopBoundingBox = VectorUtils.rotateAABB(topBoundingBox, combinedRotationMatrix, true);
// Add them to the map
builder.put(Pair.of(horizontalRotation, verticalRotation), Pair.of(rotatedBaseBoundingBox, rotatedTopBoundingBox));
}
}
ROTATED_BOUNDING_BOXES = builder.build();
}
/**
* The default bounding box.
*/
private static final AxisAlignedBB DEFAULT_BOUNDING_BOX = new AxisAlignedBB(0, 0, 0, 1, 1, 1);
@Override
protected BlockStateContainer createBlockState() {
return new BlockStateContainer(this, HORIZONTAL_ROTATION, VERTICAL_ROTATION);
}
@SuppressWarnings("deprecation")
@Override
public IBlockState getStateFromMeta(int meta) {
final EnumFacing horizontalRotation = EnumFacing.getHorizontal(meta & 3);
final EnumVerticalRotation verticalRotation = EnumVerticalRotation.fromIndex(meta >> 2);
return getDefaultState().withProperty(HORIZONTAL_ROTATION, horizontalRotation).withProperty(VERTICAL_ROTATION, verticalRotation);
}
@Override
public int getMetaFromState(IBlockState state) {
final int horizontalIndex = state.getValue(HORIZONTAL_ROTATION).getHorizontalIndex();
final int verticalIndex = state.getValue(VERTICAL_ROTATION).getIndex();
return (verticalIndex << 2) | horizontalIndex;
}
@Override
public boolean rotateBlock(World world, BlockPos pos, EnumFacing axis) {
final EnumFacing.Axis axisToRotate = axis.getAxis();
IBlockState state = world.getBlockState(pos);
switch (axisToRotate) {
case X:
case Z:
state = state.cycleProperty(VERTICAL_ROTATION);
break;
case Y:
final EnumFacing originalRotation = state.getValue(HORIZONTAL_ROTATION);
final EnumFacing newRotation = axis.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE ? originalRotation.rotateY() : originalRotation.rotateYCCW();
state = state.withProperty(HORIZONTAL_ROTATION, newRotation);
break;
}
return world.setBlockState(pos, state);
}
@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
return rotateBlock(worldIn, pos, side);
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer, EnumHand hand) {
final EnumFacing horizontalRotation = placer.getHorizontalFacing();
final EnumVerticalRotation verticalRotation = EnumVerticalRotation.fromFacing(facing);
return super.getStateForPlacement(world, pos, facing, hitX, hitY, hitZ, meta, placer, hand)
.withProperty(HORIZONTAL_ROTATION, horizontalRotation)
.withProperty(VERTICAL_ROTATION, verticalRotation);
}
@SideOnly(Side.CLIENT)
@Override
public BlockRenderLayer getBlockLayer() {
return BlockRenderLayer.CUTOUT;
}
@SuppressWarnings("deprecation")
@Override
public boolean isOpaqueCube(IBlockState state) {
return false;
}
@SuppressWarnings("deprecation")
@SideOnly(Side.CLIENT)
@Override
public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side) {
return true;
}
@SuppressWarnings("deprecation")
@Override
public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List<AxisAlignedBB> collidingBoxes, @Nullable Entity entityIn, boolean p_185477_7_) {
final Pair<EnumFacing, EnumVerticalRotation> key = Pair.of(state.getValue(HORIZONTAL_ROTATION), state.getValue(VERTICAL_ROTATION));
final Pair<AxisAlignedBB, AxisAlignedBB> boundingBoxes = ROTATED_BOUNDING_BOXES.get(key);
final AxisAlignedBB baseBoundingBox = boundingBoxes.getLeft();
addCollisionBoxToList(pos, entityBox, collidingBoxes, baseBoundingBox);
final AxisAlignedBB topBoundingBox = boundingBoxes.getRight();
addCollisionBoxToList(pos, entityBox, collidingBoxes, topBoundingBox);
}
/**
* A rotation around the x-axis.
*/
public enum EnumVerticalRotation implements IStringSerializable {
DOWN(0, "down", EnumFacing.DOWN, 2),
UP(1, "up", EnumFacing.UP, 0),
SIDE(2, "side", EnumFacing.NORTH, 1);
/**
* The values ordered by their index.
*/
private static final EnumVerticalRotation[] VALUES = new EnumVerticalRotation[values().length];
static {
for (final EnumVerticalRotation verticalRotation : values()) {
VALUES[verticalRotation.getIndex()] = verticalRotation;
}
}
/**
* Get the value with the specified index.
*
* @param index The index
* @return The value
*/
public static EnumVerticalRotation fromIndex(int index) {
return VALUES[MathHelper.abs(index % VALUES.length)];
}
/**
* Get the value corresponding to the specified facing.
*
* @param facing The facing
* @return The value
*/
public static EnumVerticalRotation fromFacing(EnumFacing facing) {
switch (facing) {
case DOWN:
return DOWN;
case UP:
return UP;
default:
return SIDE;
}
}
/**
* The index.
*/
private final int index;
/**
* The name.
*/
private final String name;
/**
* The corresponding {@link EnumFacing}.
*/
private final EnumFacing facing;
/**
* The angle of this rotation in radians
*/
private final double angle;
/**
* Construct a new vertical rotation.
*
* @param index The index
* @param name The name
* @param facing The corresponding facing
* @param numRotations The number of 90-degree rotations relative to {@link #SIDE}
*/
EnumVerticalRotation(int index, String name, EnumFacing facing, int numRotations) {
this.index = index;
this.name = name;
this.facing = facing;
this.angle = numRotations * Math.toRadians(90);
}
/**
* Get the index.
*
* @return The index
*/
public int getIndex() {
return index;
}
/**
* Get the name.
*
* @return The name
*/
@Override
public String getName() {
return name;
}
/**
* Get the corresponding {@link EnumFacing}.
*
* @return The corresponding {@link EnumFacing}
*/
public EnumFacing getFacing() {
return facing;
}
/**
* Get the angle of this vertical rotation relative to {@link #SIDE}.
*
* @return The angle
*/
public double getAngle() {
return angle;
}
@Override
public String toString() {
return name;
}
}
}