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.Builder; 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 random) { return this.defaultBlockState().setValue(AGE, random.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 state, LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random ) { if (direction == this.growthDirection.getOpposite() && !state.canSurvive(level, pos)) { scheduledTickAccess.scheduleTick(pos, this, 1); } if (direction != this.growthDirection || !neighborState.is(this) && !neighborState.is(this.getBodyBlock())) { if (this.scheduleFluidTicks) { scheduledTickAccess.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); } return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); } else { return this.updateBodyAfterConvertedFromHead(state, this.getBodyBlock().defaultBlockState()); } } @Override protected void createBlockStateDefinition(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; } }