package slimeknights.tconstruct.gadgets.block; import com.google.common.collect.ImmutableMap; import net.minecraft.block.BlockLever.EnumOrientation; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.PropertyBool; import net.minecraft.block.properties.PropertyEnum; import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumBlockRenderType; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; import net.minecraft.util.EnumHand; import net.minecraft.util.Mirror; import net.minecraft.util.Rotation; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.property.ExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.List; import javax.annotation.Nonnull; import slimeknights.tconstruct.gadgets.TinkerGadgets; import slimeknights.tconstruct.gadgets.tileentity.TileDryingRack; import slimeknights.tconstruct.gadgets.tileentity.TileItemRack; import slimeknights.tconstruct.library.TinkerRegistry; import slimeknights.tconstruct.shared.block.BlockTable; public class BlockRack extends BlockTable { // pull the facing enums from the lever, since the standard facing does not have quite enough, but the lever's facing is perfect public static final PropertyEnum<EnumOrientation> ORIENTATION = PropertyEnum.create("facing", EnumOrientation.class); public static final PropertyBool DRYING = PropertyBool.create("drying"); public BlockRack() { super(Material.WOOD); this.setSoundType(SoundType.WOOD); this.setCreativeTab(TinkerRegistry.tabGadgets); this.setHardness(2.0F); this.setDefaultState(getBlockState().getBaseState() .withProperty(ORIENTATION, EnumOrientation.NORTH) .withProperty(DRYING, false) ); } @SideOnly(Side.CLIENT) @Override public void getSubBlocks(@Nonnull Item itemIn, CreativeTabs tab, List<ItemStack> list) { list.add(createItemstack(this, 0, Blocks.WOODEN_SLAB, 0)); list.add(createItemstack(this, 1, Blocks.WOODEN_SLAB, 0)); } @Override public int damageDropped(IBlockState state) { if(state.getValue(DRYING)) { return 1; } else { return 0; } } @Override public boolean isSideSolid(@Nonnull IBlockState base_state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side) { // the center ones are considered solid at the top return side == EnumFacing.UP && base_state.getValue(ORIENTATION).getFacing() == EnumFacing.UP; } /* Inventory stuffs */ @Nonnull @Override public TileEntity createNewTileEntity(@Nonnull World worldIn, int meta) { if(getStateFromMeta(meta).getValue(DRYING)) { return new TileDryingRack(); } else { return new TileItemRack(); } } @Override public boolean openGui(EntityPlayer player, World world, BlockPos pos) { return false; } /* Activation */ @Override public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, ItemStack stack, EnumFacing side, float hitX, float hitY, float hitZ) { if(!world.isRemote) { ((TileItemRack) world.getTileEntity(pos)).interact(player); world.scheduleUpdate(pos, this, 0); } return true; } /* Block state */ @Nonnull @Override protected BlockStateContainer createBlockState() { // FACING is used to orientate the item, while ORIENTATION is the location of the rack. This mainly affects centered racks return new ExtendedBlockState(this, new IProperty[]{ORIENTATION, DRYING}, new IUnlistedProperty[]{TEXTURE, INVENTORY, FACING}); } /** * Called by ItemBlocks just before a block is actually set in the world, to allow for adjustments to the * IBlockstate */ @Override public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer, ItemStack stack) { IBlockState state = this.getDefaultState(); // playing a drying rack instead of an item rack if((meta & 1) == 1) { state = state.withProperty(DRYING, true); } IBlockState placedOn = world.getBlockState(pos.offset(facing.getOpposite())); // if placing on another item rack or drying rack, use the same orientation to make building easier // this is for the sake of making elevated, standalone rows of racks or easier building up on walls if(placedOn.getBlock() == TinkerGadgets.rack) { return state.withProperty(ORIENTATION, placedOn.getValue(ORIENTATION)); // otherwise place it based on side/player facing } else { return state.withProperty(ORIENTATION, EnumOrientation.forFacings(facing.getOpposite(), placer.getHorizontalFacing())); } } @Override public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) { super.onBlockPlacedBy(world, pos, state, placer, stack); TileEntity te = world.getTileEntity(pos); if(te != null && te instanceof TileItemRack) { TileItemRack rack = (TileItemRack) te; EnumOrientation orientation = state.getValue(ORIENTATION); switch(orientation) { // change the facing value if the state is one of the sides case NORTH: case EAST: case SOUTH: case WEST: rack.setFacing(orientation.getFacing().getOpposite()); break; // if not on a side, make sure the facing is allowed for the state of rack, as the state will be the same as the rack its placed on case UP_X: case DOWN_X: if(placer.getHorizontalFacing().getAxis() != Axis.X) { rack.setFacing(placer.getHorizontalFacing().rotateY()); } break; case UP_Z: case DOWN_Z: if(placer.getHorizontalFacing().getAxis() != Axis.Z) { rack.setFacing(placer.getHorizontalFacing().rotateY()); } break; } } } /** * Convert the given metadata into a BlockState for this Block */ @Nonnull @Override public IBlockState getStateFromMeta(int meta) { return this.getDefaultState() .withProperty(ORIENTATION, EnumOrientation.byMetadata(meta >> 1)) .withProperty(DRYING, (meta & 1) == 1); } /** * Convert the BlockState into the correct metadata value */ @Override public int getMetaFromState(IBlockState state) { int i = 0; i = i | state.getValue(ORIENTATION).getMetadata() << 1; if(state.getValue(DRYING)) { i |= 1; } return i; } /** * Returns the blockstate with the given rotation from the passed blockstate. If inapplicable, returns the passed * blockstate. */ @Nonnull @Override public IBlockState withRotation(@Nonnull IBlockState state, Rotation rot) { switch(rot) { case CLOCKWISE_180: switch(state.getValue(ORIENTATION)) { case EAST: return state.withProperty(ORIENTATION, EnumOrientation.WEST); case WEST: return state.withProperty(ORIENTATION, EnumOrientation.EAST); case SOUTH: return state.withProperty(ORIENTATION, EnumOrientation.NORTH); case NORTH: return state.withProperty(ORIENTATION, EnumOrientation.SOUTH); default: return state; } case COUNTERCLOCKWISE_90: switch(state.getValue(ORIENTATION)) { case EAST: return state.withProperty(ORIENTATION, EnumOrientation.NORTH); case WEST: return state.withProperty(ORIENTATION, EnumOrientation.SOUTH); case SOUTH: return state.withProperty(ORIENTATION, EnumOrientation.EAST); case NORTH: return state.withProperty(ORIENTATION, EnumOrientation.WEST); case UP_Z: return state.withProperty(ORIENTATION, EnumOrientation.UP_X); case UP_X: return state.withProperty(ORIENTATION, EnumOrientation.UP_Z); case DOWN_X: return state.withProperty(ORIENTATION, EnumOrientation.DOWN_Z); case DOWN_Z: return state.withProperty(ORIENTATION, EnumOrientation.DOWN_X); } case CLOCKWISE_90: switch(state.getValue(ORIENTATION)) { case EAST: return state.withProperty(ORIENTATION, EnumOrientation.SOUTH); case WEST: return state.withProperty(ORIENTATION, EnumOrientation.NORTH); case SOUTH: return state.withProperty(ORIENTATION, EnumOrientation.WEST); case NORTH: return state.withProperty(ORIENTATION, EnumOrientation.EAST); case UP_Z: return state.withProperty(ORIENTATION, EnumOrientation.UP_X); case UP_X: return state.withProperty(ORIENTATION, EnumOrientation.UP_Z); case DOWN_X: return state.withProperty(ORIENTATION, EnumOrientation.DOWN_Z); case DOWN_Z: return state.withProperty(ORIENTATION, EnumOrientation.DOWN_X); } default: return state; } } /** * Returns the blockstate with the given mirror of the passed blockstate. If inapplicable, returns the passed * blockstate. */ @Nonnull @Override public IBlockState withMirror(@Nonnull IBlockState state, Mirror mirrorIn) { return state.withRotation(mirrorIn.toRotation(state.getValue(ORIENTATION).getFacing())); } /* Bounding boxes */ private static final ImmutableMap<EnumOrientation, AxisAlignedBB> BOUNDS; static { ImmutableMap.Builder<EnumOrientation, AxisAlignedBB> builder = ImmutableMap.builder(); builder.put(EnumOrientation.DOWN_X, new AxisAlignedBB(0.375, 0, 0, 0.625, 0.25, 1)); builder.put(EnumOrientation.DOWN_Z, new AxisAlignedBB(0, 0, 0.375, 1, 0.25, 0.625)); builder.put(EnumOrientation.UP_X, new AxisAlignedBB(0.375, 0.75, 0, 0.625, 1, 1)); builder.put(EnumOrientation.UP_Z, new AxisAlignedBB(0, 0.75, 0.375, 1, 1, 0.625)); builder.put(EnumOrientation.NORTH, new AxisAlignedBB(0, 0.75, 0, 1, 1, 0.25)); builder.put(EnumOrientation.SOUTH, new AxisAlignedBB(0, 0.75, 0.75, 1, 1, 1)); builder.put(EnumOrientation.EAST, new AxisAlignedBB(0.75, 0.75, 0, 1, 1, 1)); builder.put(EnumOrientation.WEST, new AxisAlignedBB(0, 0.75, 0, 0.25, 1, 1)); BOUNDS = builder.build(); } @Override public AxisAlignedBB getCollisionBoundingBox(IBlockState state, @Nonnull World worldIn, @Nonnull BlockPos pos) { return BOUNDS.get(state.getValue(ORIENTATION)); } @Nonnull @Override public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) { return BOUNDS.get(state.getValue(ORIENTATION)); } @Nonnull @Override public EnumBlockRenderType getRenderType(IBlockState state) { return EnumBlockRenderType.MODEL; } @Override @SideOnly(Side.CLIENT) public boolean shouldSideBeRendered(IBlockState blockState, @Nonnull IBlockAccess blockAccess, @Nonnull BlockPos pos, EnumFacing side) { return true; } // restore default raytrace, as BlockTable changes that /** * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit. */ @Override public RayTraceResult collisionRayTrace(IBlockState blockState, @Nonnull World worldIn, @Nonnull BlockPos pos, @Nonnull Vec3d start, @Nonnull Vec3d end) { return this.rayTrace(pos, start, end, blockState.getBoundingBox(worldIn, pos)); } @Override public boolean hasComparatorInputOverride(IBlockState state) { return state.getValue(DRYING) == Boolean.TRUE; } // comparator stuffs @Override public int getComparatorInputOverride(IBlockState blockState, World world, BlockPos pos) { TileEntity te = world.getTileEntity(pos); if(!(te instanceof TileDryingRack)) { return 0; } return ((TileDryingRack) te).comparatorStrength(); } @Override public boolean rotateBlock(World world, BlockPos pos, EnumFacing axis) { // todo: implement this properly return false; } }