Code/net/minecraft/world/level/block/BigDripleafBlock.java

282 lines
13 KiB
Java
Raw Normal View History

2025-07-01 06:20:03 +00:00
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<BigDripleafBlock> CODEC = simpleCodec(BigDripleafBlock::new);
private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private static final EnumProperty<Tilt> TILT = BlockStateProperties.TILT;
private static final int NO_TICK = -1;
private static final Object2IntMap<Tilt> 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<Tilt, VoxelShape> 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<BlockState, VoxelShape> shapes;
@Override
public MapCodec<BigDripleafBlock> 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<BlockState, VoxelShape> makeShapes() {
Map<Direction, VoxelShape> 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<Block, BlockState> p_152300_) {
p_152300_.add(WATERLOGGED, FACING, TILT);
}
}