package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; 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.BlockStateProperties; import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.shapes.VoxelShape; public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements BonemealableBlock { public static final IntegerProperty AGE = BlockStateProperties.AGE_25; public static final int MAX_AGE = 25; private final double growPerTickProbability; protected GrowingPlantHeadBlock( BlockBehaviour.Properties properties, Direction growthDirection, VoxelShape shape, boolean scheduleFluidTicks, double growPerTickProbability ) { super(properties, growthDirection, shape, scheduleFluidTicks); this.growPerTickProbability = growPerTickProbability; this.registerDefaultState(this.stateDefinition.any().setValue(AGE, 0)); } @Override protected abstract MapCodec codec(); @Override public BlockState getStateForPlacement(RandomSource randomSource) { return this.defaultBlockState().setValue(AGE, randomSource.nextInt(25)); } @Override protected boolean isRandomlyTicking(BlockState state) { return (Integer)state.getValue(AGE) < 25; } @Override protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { if ((Integer)state.getValue(AGE) < 25 && random.nextDouble() < this.growPerTickProbability) { BlockPos blockPos = pos.relative(this.growthDirection); if (this.canGrowInto(level.getBlockState(blockPos))) { level.setBlockAndUpdate(blockPos, this.getGrowIntoState(state, level.random)); } } } protected BlockState getGrowIntoState(BlockState state, RandomSource random) { return state.cycle(AGE); } public BlockState getMaxAgeState(BlockState state) { return state.setValue(AGE, 25); } public boolean isMaxAge(BlockState state) { return (Integer)state.getValue(AGE) == 25; } protected BlockState updateBodyAfterConvertedFromHead(BlockState head, BlockState body) { return body; } @Override protected BlockState updateShape( BlockState blockState, LevelReader levelReader, ScheduledTickAccess scheduledTickAccess, BlockPos blockPos, Direction direction, BlockPos blockPos2, BlockState blockState2, RandomSource randomSource ) { if (direction == this.growthDirection.getOpposite() && !blockState.canSurvive(levelReader, blockPos)) { scheduledTickAccess.scheduleTick(blockPos, this, 1); } if (direction != this.growthDirection || !blockState2.is(this) && !blockState2.is(this.getBodyBlock())) { if (this.scheduleFluidTicks) { scheduledTickAccess.scheduleTick(blockPos, Fluids.WATER, Fluids.WATER.getTickDelay(levelReader)); } return super.updateShape(blockState, levelReader, scheduledTickAccess, blockPos, direction, blockPos2, blockState2, randomSource); } else { return this.updateBodyAfterConvertedFromHead(blockState, this.getBodyBlock().defaultBlockState()); } } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(AGE); } @Override public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) { return this.canGrowInto(level.getBlockState(pos.relative(this.growthDirection))); } @Override public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) { return true; } @Override public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) { BlockPos blockPos = pos.relative(this.growthDirection); int i = Math.min((Integer)state.getValue(AGE) + 1, 25); int j = this.getBlocksToGrowWhenBonemealed(random); for (int k = 0; k < j && this.canGrowInto(level.getBlockState(blockPos)); k++) { level.setBlockAndUpdate(blockPos, state.setValue(AGE, i)); blockPos = blockPos.relative(this.growthDirection); i = Math.min(i + 1, 25); } } protected abstract int getBlocksToGrowWhenBonemealed(RandomSource random); protected abstract boolean canGrowInto(BlockState state); @Override protected GrowingPlantHeadBlock getHeadBlock() { return this; } }