269 lines
12 KiB
Java
269 lines
12 KiB
Java
package net.minecraft.world.level.block;
|
|
|
|
import com.mojang.serialization.MapCodec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import java.util.function.BiConsumer;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
|
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.LevelReader;
|
|
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.BlockSetType;
|
|
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.DirectionProperty;
|
|
import net.minecraft.world.level.block.state.properties.DoorHingeSide;
|
|
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.pathfinder.PathComputationType;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class DoorBlock extends Block {
|
|
public static final MapCodec<DoorBlock> CODEC = RecordCodecBuilder.mapCodec(
|
|
instance -> instance.group(BlockSetType.CODEC.fieldOf("block_set_type").forGetter(DoorBlock::type), propertiesCodec()).apply(instance, DoorBlock::new)
|
|
);
|
|
public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING;
|
|
public static final BooleanProperty OPEN = BlockStateProperties.OPEN;
|
|
public static final EnumProperty<DoorHingeSide> HINGE = BlockStateProperties.DOOR_HINGE;
|
|
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
|
|
public static final EnumProperty<DoubleBlockHalf> HALF = BlockStateProperties.DOUBLE_BLOCK_HALF;
|
|
protected static final float AABB_DOOR_THICKNESS = 3.0F;
|
|
protected static final VoxelShape SOUTH_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 16.0, 3.0);
|
|
protected static final VoxelShape NORTH_AABB = Block.box(0.0, 0.0, 13.0, 16.0, 16.0, 16.0);
|
|
protected static final VoxelShape WEST_AABB = Block.box(13.0, 0.0, 0.0, 16.0, 16.0, 16.0);
|
|
protected static final VoxelShape EAST_AABB = Block.box(0.0, 0.0, 0.0, 3.0, 16.0, 16.0);
|
|
private final BlockSetType type;
|
|
|
|
@Override
|
|
public MapCodec<? extends DoorBlock> codec() {
|
|
return CODEC;
|
|
}
|
|
|
|
protected DoorBlock(BlockSetType type, BlockBehaviour.Properties properties) {
|
|
super(properties.sound(type.soundType()));
|
|
this.type = type;
|
|
this.registerDefaultState(
|
|
this.stateDefinition
|
|
.any()
|
|
.setValue(FACING, Direction.NORTH)
|
|
.setValue(OPEN, false)
|
|
.setValue(HINGE, DoorHingeSide.LEFT)
|
|
.setValue(POWERED, false)
|
|
.setValue(HALF, DoubleBlockHalf.LOWER)
|
|
);
|
|
}
|
|
|
|
public BlockSetType type() {
|
|
return this.type;
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
|
Direction direction = state.getValue(FACING);
|
|
boolean bl = !(Boolean)state.getValue(OPEN);
|
|
boolean bl2 = state.getValue(HINGE) == DoorHingeSide.RIGHT;
|
|
|
|
return switch (direction) {
|
|
case SOUTH -> bl ? SOUTH_AABB : (bl2 ? EAST_AABB : WEST_AABB);
|
|
case WEST -> bl ? WEST_AABB : (bl2 ? SOUTH_AABB : NORTH_AABB);
|
|
case NORTH -> bl ? NORTH_AABB : (bl2 ? WEST_AABB : EAST_AABB);
|
|
default -> bl ? EAST_AABB : (bl2 ? NORTH_AABB : SOUTH_AABB);
|
|
};
|
|
}
|
|
|
|
@Override
|
|
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
|
DoubleBlockHalf doubleBlockHalf = state.getValue(HALF);
|
|
if (direction.getAxis() != Direction.Axis.Y || doubleBlockHalf == DoubleBlockHalf.LOWER != (direction == Direction.UP)) {
|
|
return doubleBlockHalf == DoubleBlockHalf.LOWER && direction == Direction.DOWN && !state.canSurvive(level, pos)
|
|
? Blocks.AIR.defaultBlockState()
|
|
: super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
|
} else {
|
|
return neighborState.getBlock() instanceof DoorBlock && neighborState.getValue(HALF) != doubleBlockHalf
|
|
? neighborState.setValue(HALF, doubleBlockHalf)
|
|
: Blocks.AIR.defaultBlockState();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onExplosionHit(BlockState state, Level level, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> dropConsumer) {
|
|
if (explosion.canTriggerBlocks() && state.getValue(HALF) == DoubleBlockHalf.LOWER && this.type.canOpenByWindCharge() && !(Boolean)state.getValue(POWERED)) {
|
|
this.setOpen(null, level, state, pos, !this.isOpen(state));
|
|
}
|
|
|
|
super.onExplosionHit(state, level, pos, explosion, dropConsumer);
|
|
}
|
|
|
|
@Override
|
|
public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
|
|
if (!level.isClientSide && (player.isCreative() || !player.hasCorrectToolForDrops(state))) {
|
|
DoublePlantBlock.preventDropFromBottomPart(level, pos, state, player);
|
|
}
|
|
|
|
return super.playerWillDestroy(level, pos, state, player);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) {
|
|
return switch (pathComputationType) {
|
|
case LAND, AIR -> state.getValue(OPEN);
|
|
case WATER -> false;
|
|
};
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
|
BlockPos blockPos = context.getClickedPos();
|
|
Level level = context.getLevel();
|
|
if (blockPos.getY() < level.getMaxBuildHeight() - 1 && level.getBlockState(blockPos.above()).canBeReplaced(context)) {
|
|
boolean bl = level.hasNeighborSignal(blockPos) || level.hasNeighborSignal(blockPos.above());
|
|
return this.defaultBlockState()
|
|
.setValue(FACING, context.getHorizontalDirection())
|
|
.setValue(HINGE, this.getHinge(context))
|
|
.setValue(POWERED, bl)
|
|
.setValue(OPEN, bl)
|
|
.setValue(HALF, DoubleBlockHalf.LOWER);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setPlacedBy(Level level, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
|
|
level.setBlock(pos.above(), state.setValue(HALF, DoubleBlockHalf.UPPER), 3);
|
|
}
|
|
|
|
private DoorHingeSide getHinge(BlockPlaceContext context) {
|
|
BlockGetter blockGetter = context.getLevel();
|
|
BlockPos blockPos = context.getClickedPos();
|
|
Direction direction = context.getHorizontalDirection();
|
|
BlockPos blockPos2 = blockPos.above();
|
|
Direction direction2 = direction.getCounterClockWise();
|
|
BlockPos blockPos3 = blockPos.relative(direction2);
|
|
BlockState blockState = blockGetter.getBlockState(blockPos3);
|
|
BlockPos blockPos4 = blockPos2.relative(direction2);
|
|
BlockState blockState2 = blockGetter.getBlockState(blockPos4);
|
|
Direction direction3 = direction.getClockWise();
|
|
BlockPos blockPos5 = blockPos.relative(direction3);
|
|
BlockState blockState3 = blockGetter.getBlockState(blockPos5);
|
|
BlockPos blockPos6 = blockPos2.relative(direction3);
|
|
BlockState blockState4 = blockGetter.getBlockState(blockPos6);
|
|
int i = (blockState.isCollisionShapeFullBlock(blockGetter, blockPos3) ? -1 : 0)
|
|
+ (blockState2.isCollisionShapeFullBlock(blockGetter, blockPos4) ? -1 : 0)
|
|
+ (blockState3.isCollisionShapeFullBlock(blockGetter, blockPos5) ? 1 : 0)
|
|
+ (blockState4.isCollisionShapeFullBlock(blockGetter, blockPos6) ? 1 : 0);
|
|
boolean bl = blockState.getBlock() instanceof DoorBlock && blockState.getValue(HALF) == DoubleBlockHalf.LOWER;
|
|
boolean bl2 = blockState3.getBlock() instanceof DoorBlock && blockState3.getValue(HALF) == DoubleBlockHalf.LOWER;
|
|
if ((!bl || bl2) && i <= 0) {
|
|
if ((!bl2 || bl) && i >= 0) {
|
|
int j = direction.getStepX();
|
|
int k = direction.getStepZ();
|
|
Vec3 vec3 = context.getClickLocation();
|
|
double d = vec3.x - blockPos.getX();
|
|
double e = vec3.z - blockPos.getZ();
|
|
return (j >= 0 || !(e < 0.5)) && (j <= 0 || !(e > 0.5)) && (k >= 0 || !(d > 0.5)) && (k <= 0 || !(d < 0.5)) ? DoorHingeSide.LEFT : DoorHingeSide.RIGHT;
|
|
} else {
|
|
return DoorHingeSide.LEFT;
|
|
}
|
|
} else {
|
|
return DoorHingeSide.RIGHT;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
|
|
if (!this.type.canOpenByHand()) {
|
|
return InteractionResult.PASS;
|
|
} else {
|
|
state = state.cycle(OPEN);
|
|
level.setBlock(pos, state, 10);
|
|
this.playSound(player, level, pos, (Boolean)state.getValue(OPEN));
|
|
level.gameEvent(player, this.isOpen(state) ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
|
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
|
}
|
|
}
|
|
|
|
public boolean isOpen(BlockState state) {
|
|
return (Boolean)state.getValue(OPEN);
|
|
}
|
|
|
|
public void setOpen(@Nullable Entity entity, Level level, BlockState state, BlockPos pos, boolean open) {
|
|
if (state.is(this) && (Boolean)state.getValue(OPEN) != open) {
|
|
level.setBlock(pos, state.setValue(OPEN, open), 10);
|
|
this.playSound(entity, level, pos, open);
|
|
level.gameEvent(entity, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) {
|
|
boolean bl = level.hasNeighborSignal(pos)
|
|
|| level.hasNeighborSignal(pos.relative(state.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
|
|
if (!this.defaultBlockState().is(neighborBlock) && bl != (Boolean)state.getValue(POWERED)) {
|
|
if (bl != (Boolean)state.getValue(OPEN)) {
|
|
this.playSound(null, level, pos, bl);
|
|
level.gameEvent(null, bl ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
|
|
}
|
|
|
|
level.setBlock(pos, state.setValue(POWERED, bl).setValue(OPEN, bl), 2);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
|
BlockPos blockPos = pos.below();
|
|
BlockState blockState = level.getBlockState(blockPos);
|
|
return state.getValue(HALF) == DoubleBlockHalf.LOWER ? blockState.isFaceSturdy(level, blockPos, Direction.UP) : blockState.is(this);
|
|
}
|
|
|
|
private void playSound(@Nullable Entity source, Level level, BlockPos pos, boolean isOpening) {
|
|
level.playSound(source, pos, isOpening ? this.type.doorOpen() : this.type.doorClose(), SoundSource.BLOCKS, 1.0F, level.getRandom().nextFloat() * 0.1F + 0.9F);
|
|
}
|
|
|
|
@Override
|
|
protected BlockState rotate(BlockState state, Rotation rotation) {
|
|
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
|
}
|
|
|
|
@Override
|
|
protected BlockState mirror(BlockState state, Mirror mirror) {
|
|
return mirror == Mirror.NONE ? state : state.rotate(mirror.getRotation(state.getValue(FACING))).cycle(HINGE);
|
|
}
|
|
|
|
@Override
|
|
protected long getSeed(BlockState state, BlockPos pos) {
|
|
return Mth.getSeed(pos.getX(), pos.below(state.getValue(HALF) == DoubleBlockHalf.LOWER ? 0 : 1).getY(), pos.getZ());
|
|
}
|
|
|
|
@Override
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(HALF, FACING, OPEN, HINGE, POWERED);
|
|
}
|
|
|
|
public static boolean isWoodenDoor(Level level, BlockPos pos) {
|
|
return isWoodenDoor(level.getBlockState(pos));
|
|
}
|
|
|
|
public static boolean isWoodenDoor(BlockState state) {
|
|
return state.getBlock() instanceof DoorBlock doorBlock && doorBlock.type().canOpenByHand();
|
|
}
|
|
}
|