package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import java.util.function.BiConsumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; public class LeverBlock extends FaceAttachedHorizontalDirectionalBlock { public static final MapCodec CODEC = simpleCodec(LeverBlock::new); public static final BooleanProperty POWERED = BlockStateProperties.POWERED; protected static final int DEPTH = 6; protected static final int WIDTH = 6; protected static final int HEIGHT = 8; protected static final VoxelShape NORTH_AABB = Block.box(5.0, 4.0, 10.0, 11.0, 12.0, 16.0); protected static final VoxelShape SOUTH_AABB = Block.box(5.0, 4.0, 0.0, 11.0, 12.0, 6.0); protected static final VoxelShape WEST_AABB = Block.box(10.0, 4.0, 5.0, 16.0, 12.0, 11.0); protected static final VoxelShape EAST_AABB = Block.box(0.0, 4.0, 5.0, 6.0, 12.0, 11.0); protected static final VoxelShape UP_AABB_Z = Block.box(5.0, 0.0, 4.0, 11.0, 6.0, 12.0); protected static final VoxelShape UP_AABB_X = Block.box(4.0, 0.0, 5.0, 12.0, 6.0, 11.0); protected static final VoxelShape DOWN_AABB_Z = Block.box(5.0, 10.0, 4.0, 11.0, 16.0, 12.0); protected static final VoxelShape DOWN_AABB_X = Block.box(4.0, 10.0, 5.0, 12.0, 16.0, 11.0); @Override public MapCodec codec() { return CODEC; } protected LeverBlock(BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(POWERED, false).setValue(FACE, AttachFace.WALL)); } @Override protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { switch ((AttachFace)state.getValue(FACE)) { case FLOOR: switch (((Direction)state.getValue(FACING)).getAxis()) { case X: return UP_AABB_X; case Z: default: return UP_AABB_Z; } case WALL: switch ((Direction)state.getValue(FACING)) { case EAST: return EAST_AABB; case WEST: return WEST_AABB; case SOUTH: return SOUTH_AABB; case NORTH: default: return NORTH_AABB; } case CEILING: default: switch (((Direction)state.getValue(FACING)).getAxis()) { case X: return DOWN_AABB_X; case Z: default: return DOWN_AABB_Z; } } } @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { if (level.isClientSide) { BlockState blockState = state.cycle(POWERED); if ((Boolean)blockState.getValue(POWERED)) { makeParticle(blockState, level, pos, 1.0F); } } else { this.pull(state, level, pos, null); } return InteractionResult.SUCCESS; } @Override protected void onExplosionHit( BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, Explosion explosion, BiConsumer biConsumer ) { if (explosion.canTriggerBlocks()) { this.pull(blockState, serverLevel, blockPos, null); } super.onExplosionHit(blockState, serverLevel, blockPos, explosion, biConsumer); } public void pull(BlockState state, Level level, BlockPos pos, @Nullable Player player) { state = state.cycle(POWERED); level.setBlock(pos, state, 3); this.updateNeighbours(state, level, pos); playSound(player, level, pos, state); level.gameEvent(player, state.getValue(POWERED) ? GameEvent.BLOCK_ACTIVATE : GameEvent.BLOCK_DEACTIVATE, pos); } protected static void playSound(@Nullable Player player, LevelAccessor level, BlockPos pos, BlockState state) { float f = state.getValue(POWERED) ? 0.6F : 0.5F; level.playSound(player, pos, SoundEvents.LEVER_CLICK, SoundSource.BLOCKS, 0.3F, f); } private static void makeParticle(BlockState state, LevelAccessor level, BlockPos pos, float alpha) { Direction direction = ((Direction)state.getValue(FACING)).getOpposite(); Direction direction2 = getConnectedDirection(state).getOpposite(); double d = pos.getX() + 0.5 + 0.1 * direction.getStepX() + 0.2 * direction2.getStepX(); double e = pos.getY() + 0.5 + 0.1 * direction.getStepY() + 0.2 * direction2.getStepY(); double f = pos.getZ() + 0.5 + 0.1 * direction.getStepZ() + 0.2 * direction2.getStepZ(); level.addParticle(new DustParticleOptions(16711680, alpha), d, e, f, 0.0, 0.0, 0.0); } @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { if ((Boolean)state.getValue(POWERED) && random.nextFloat() < 0.25F) { makeParticle(state, level, pos, 0.5F); } } @Override protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!movedByPiston && !state.is(newState.getBlock())) { if ((Boolean)state.getValue(POWERED)) { this.updateNeighbours(state, level, pos); } super.onRemove(state, level, pos, newState, movedByPiston); } } @Override protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { return state.getValue(POWERED) ? 15 : 0; } @Override protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { return state.getValue(POWERED) && getConnectedDirection(state) == direction ? 15 : 0; } @Override protected boolean isSignalSource(BlockState state) { return true; } private void updateNeighbours(BlockState state, Level level, BlockPos pos) { Direction direction = getConnectedDirection(state).getOpposite(); Orientation orientation = ExperimentalRedstoneUtils.initialOrientation( level, direction, direction.getAxis().isHorizontal() ? Direction.UP : state.getValue(FACING) ); level.updateNeighborsAt(pos, this, orientation); level.updateNeighborsAt(pos.relative(direction), this, orientation); } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(FACE, FACING, POWERED); } }