package net.minecraft.world.level.block; import com.google.common.collect.Maps; import com.mojang.serialization.MapCodec; import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.Map; import java.util.function.Function; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelHeightAccessor; 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.BooleanProperty; import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.Tilt; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; 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.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; public class BigDripleafBlock extends HorizontalDirectionalBlock implements BonemealableBlock, SimpleWaterloggedBlock { public static final MapCodec CODEC = simpleCodec(BigDripleafBlock::new); private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; private static final EnumProperty TILT = BlockStateProperties.TILT; private static final int NO_TICK = -1; private static final Object2IntMap DELAY_UNTIL_NEXT_TILT_STATE = Util.make(new Object2IntArrayMap<>(), p_152305_ -> { p_152305_.defaultReturnValue(-1); p_152305_.put(Tilt.UNSTABLE, 10); p_152305_.put(Tilt.PARTIAL, 10); p_152305_.put(Tilt.FULL, 100); }); private static final int MAX_GEN_HEIGHT = 5; private static final int ENTITY_DETECTION_MIN_Y = 11; private static final int LOWEST_LEAF_TOP = 13; private static final Map SHAPE_LEAF = Maps.newEnumMap( Map.of( Tilt.NONE, Block.column(16.0, 11.0, 15.0), Tilt.UNSTABLE, Block.column(16.0, 11.0, 15.0), Tilt.PARTIAL, Block.column(16.0, 11.0, 13.0), Tilt.FULL, Shapes.empty() ) ); private final Function shapes; @Override public MapCodec codec() { return CODEC; } protected BigDripleafBlock(BlockBehaviour.Properties p_152214_) { super(p_152214_); this.registerDefaultState(this.stateDefinition.any().setValue(WATERLOGGED, false).setValue(FACING, Direction.NORTH).setValue(TILT, Tilt.NONE)); this.shapes = this.makeShapes(); } private Function makeShapes() { Map map = Shapes.rotateHorizontal(Block.column(6.0, 0.0, 13.0).move(0.0, 0.0, 0.25).optimize()); return this.getShapeForEachState( p_390895_ -> Shapes.or(SHAPE_LEAF.get(p_390895_.getValue(TILT)), map.get(p_390895_.getValue(FACING))), new Property[]{WATERLOGGED} ); } public static void placeWithRandomHeight(LevelAccessor p_220793_, RandomSource p_220794_, BlockPos p_220795_, Direction p_220796_) { int i = Mth.nextInt(p_220794_, 2, 5); BlockPos.MutableBlockPos blockpos$mutableblockpos = p_220795_.mutable(); int j = 0; while (j < i && canPlaceAt(p_220793_, blockpos$mutableblockpos, p_220793_.getBlockState(blockpos$mutableblockpos))) { j++; blockpos$mutableblockpos.move(Direction.UP); } int k = p_220795_.getY() + j - 1; blockpos$mutableblockpos.setY(p_220795_.getY()); while (blockpos$mutableblockpos.getY() < k) { BigDripleafStemBlock.place(p_220793_, blockpos$mutableblockpos, p_220793_.getFluidState(blockpos$mutableblockpos), p_220796_); blockpos$mutableblockpos.move(Direction.UP); } place(p_220793_, blockpos$mutableblockpos, p_220793_.getFluidState(blockpos$mutableblockpos), p_220796_); } private static boolean canReplace(BlockState p_152320_) { return p_152320_.isAir() || p_152320_.is(Blocks.WATER) || p_152320_.is(Blocks.SMALL_DRIPLEAF); } protected static boolean canPlaceAt(LevelHeightAccessor p_152252_, BlockPos p_152253_, BlockState p_152254_) { return !p_152252_.isOutsideBuildHeight(p_152253_) && canReplace(p_152254_); } protected static boolean place(LevelAccessor p_152242_, BlockPos p_152243_, FluidState p_152244_, Direction p_152245_) { BlockState blockstate = Blocks.BIG_DRIPLEAF.defaultBlockState().setValue(WATERLOGGED, p_152244_.isSourceOfType(Fluids.WATER)).setValue(FACING, p_152245_); return p_152242_.setBlock(p_152243_, blockstate, 3); } @Override protected void onProjectileHit(Level p_152228_, BlockState p_152229_, BlockHitResult p_152230_, Projectile p_152231_) { this.setTiltAndScheduleTick(p_152229_, p_152228_, p_152230_.getBlockPos(), Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN); } @Override protected FluidState getFluidState(BlockState p_152312_) { return p_152312_.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(p_152312_); } @Override protected boolean canSurvive(BlockState p_152289_, LevelReader p_152290_, BlockPos p_152291_) { BlockPos blockpos = p_152291_.below(); BlockState blockstate = p_152290_.getBlockState(blockpos); return blockstate.is(this) || blockstate.is(Blocks.BIG_DRIPLEAF_STEM) || blockstate.is(BlockTags.BIG_DRIPLEAF_PLACEABLE); } @Override protected BlockState updateShape( BlockState p_152293_, LevelReader p_363105_, ScheduledTickAccess p_360715_, BlockPos p_152297_, Direction p_152294_, BlockPos p_152298_, BlockState p_152295_, RandomSource p_361614_ ) { if (p_152294_ == Direction.DOWN && !p_152293_.canSurvive(p_363105_, p_152297_)) { return Blocks.AIR.defaultBlockState(); } else { if (p_152293_.getValue(WATERLOGGED)) { p_360715_.scheduleTick(p_152297_, Fluids.WATER, Fluids.WATER.getTickDelay(p_363105_)); } return p_152294_ == Direction.UP && p_152295_.is(this) ? Blocks.BIG_DRIPLEAF_STEM.withPropertiesOf(p_152293_) : super.updateShape(p_152293_, p_363105_, p_360715_, p_152297_, p_152294_, p_152298_, p_152295_, p_361614_); } } @Override public boolean isValidBonemealTarget(LevelReader p_255698_, BlockPos p_256302_, BlockState p_255648_) { BlockState blockstate = p_255698_.getBlockState(p_256302_.above()); return canReplace(blockstate); } @Override public boolean isBonemealSuccess(Level p_220788_, RandomSource p_220789_, BlockPos p_220790_, BlockState p_220791_) { return true; } @Override public void performBonemeal(ServerLevel p_220783_, RandomSource p_220784_, BlockPos p_220785_, BlockState p_220786_) { BlockPos blockpos = p_220785_.above(); BlockState blockstate = p_220783_.getBlockState(blockpos); if (canPlaceAt(p_220783_, blockpos, blockstate)) { Direction direction = p_220786_.getValue(FACING); BigDripleafStemBlock.place(p_220783_, p_220785_, p_220786_.getFluidState(), direction); place(p_220783_, blockpos, blockstate.getFluidState(), direction); } } @Override protected void entityInside(BlockState p_152266_, Level p_152267_, BlockPos p_152268_, Entity p_152269_, InsideBlockEffectApplier p_393000_) { if (!p_152267_.isClientSide) { if (p_152266_.getValue(TILT) == Tilt.NONE && canEntityTilt(p_152268_, p_152269_) && !p_152267_.hasNeighborSignal(p_152268_)) { this.setTiltAndScheduleTick(p_152266_, p_152267_, p_152268_, Tilt.UNSTABLE, null); } } } @Override protected void tick(BlockState p_220798_, ServerLevel p_220799_, BlockPos p_220800_, RandomSource p_220801_) { if (p_220799_.hasNeighborSignal(p_220800_)) { resetTilt(p_220798_, p_220799_, p_220800_); } else { Tilt tilt = p_220798_.getValue(TILT); if (tilt == Tilt.UNSTABLE) { this.setTiltAndScheduleTick(p_220798_, p_220799_, p_220800_, Tilt.PARTIAL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN); } else if (tilt == Tilt.PARTIAL) { this.setTiltAndScheduleTick(p_220798_, p_220799_, p_220800_, Tilt.FULL, SoundEvents.BIG_DRIPLEAF_TILT_DOWN); } else if (tilt == Tilt.FULL) { resetTilt(p_220798_, p_220799_, p_220800_); } } } @Override protected void neighborChanged(BlockState p_152271_, Level p_152272_, BlockPos p_152273_, Block p_152274_, @Nullable Orientation p_364652_, boolean p_152276_) { if (p_152272_.hasNeighborSignal(p_152273_)) { resetTilt(p_152271_, p_152272_, p_152273_); } } private static void playTiltSound(Level p_152233_, BlockPos p_152234_, SoundEvent p_152235_) { float f = Mth.randomBetween(p_152233_.random, 0.8F, 1.2F); p_152233_.playSound(null, p_152234_, p_152235_, SoundSource.BLOCKS, 1.0F, f); } private static boolean canEntityTilt(BlockPos p_152302_, Entity p_152303_) { return p_152303_.onGround() && p_152303_.position().y > p_152302_.getY() + 0.6875F; } private void setTiltAndScheduleTick(BlockState p_152283_, Level p_152284_, BlockPos p_152285_, Tilt p_152286_, @Nullable SoundEvent p_152287_) { setTilt(p_152283_, p_152284_, p_152285_, p_152286_); if (p_152287_ != null) { playTiltSound(p_152284_, p_152285_, p_152287_); } int i = DELAY_UNTIL_NEXT_TILT_STATE.getInt(p_152286_); if (i != -1) { p_152284_.scheduleTick(p_152285_, this, i); } } private static void resetTilt(BlockState p_152314_, Level p_152315_, BlockPos p_152316_) { setTilt(p_152314_, p_152315_, p_152316_, Tilt.NONE); if (p_152314_.getValue(TILT) != Tilt.NONE) { playTiltSound(p_152315_, p_152316_, SoundEvents.BIG_DRIPLEAF_TILT_UP); } } private static void setTilt(BlockState p_152278_, Level p_152279_, BlockPos p_152280_, Tilt p_152281_) { Tilt tilt = p_152278_.getValue(TILT); p_152279_.setBlock(p_152280_, p_152278_.setValue(TILT, p_152281_), 2); if (p_152281_.causesVibration() && p_152281_ != tilt) { p_152279_.gameEvent(null, GameEvent.BLOCK_CHANGE, p_152280_); } } @Override protected VoxelShape getCollisionShape(BlockState p_152307_, BlockGetter p_152308_, BlockPos p_152309_, CollisionContext p_152310_) { return SHAPE_LEAF.get(p_152307_.getValue(TILT)); } @Override protected VoxelShape getShape(BlockState p_152261_, BlockGetter p_152262_, BlockPos p_152263_, CollisionContext p_152264_) { return this.shapes.apply(p_152261_); } @Override public BlockState getStateForPlacement(BlockPlaceContext p_152221_) { BlockState blockstate = p_152221_.getLevel().getBlockState(p_152221_.getClickedPos().below()); FluidState fluidstate = p_152221_.getLevel().getFluidState(p_152221_.getClickedPos()); boolean flag = blockstate.is(Blocks.BIG_DRIPLEAF) || blockstate.is(Blocks.BIG_DRIPLEAF_STEM); return this.defaultBlockState() .setValue(WATERLOGGED, fluidstate.isSourceOfType(Fluids.WATER)) .setValue(FACING, flag ? blockstate.getValue(FACING) : p_152221_.getHorizontalDirection().getOpposite()); } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_152300_) { p_152300_.add(WATERLOGGED, FACING, TILT); } }