package net.minecraft.world.level.block; import com.mojang.math.OctahedralGroup; import com.mojang.math.Quadrant; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import java.util.List; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.DismountHelper; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.CollisionGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; import net.minecraft.world.level.block.entity.BedBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; 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.BedPart; 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.pathfinder.PathComputationType; import net.minecraft.world.phys.AABB; 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.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.apache.commons.lang3.ArrayUtils; public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( p_359966_ -> p_359966_.group(DyeColor.CODEC.fieldOf("color").forGetter(BedBlock::getColor), propertiesCodec()).apply(p_359966_, BedBlock::new) ); public static final EnumProperty PART = BlockStateProperties.BED_PART; public static final BooleanProperty OCCUPIED = BlockStateProperties.OCCUPIED; private static final Map SHAPES = Util.make(() -> { VoxelShape voxelshape = Block.box(0.0, 0.0, 0.0, 3.0, 3.0, 3.0); VoxelShape voxelshape1 = Shapes.rotate(voxelshape, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R90)); return Shapes.rotateHorizontal(Shapes.or(Block.column(16.0, 3.0, 9.0), voxelshape, voxelshape1)); }); private final DyeColor color; @Override public MapCodec codec() { return CODEC; } public BedBlock(DyeColor p_49454_, BlockBehaviour.Properties p_49455_) { super(p_49455_); this.color = p_49454_; this.registerDefaultState(this.stateDefinition.any().setValue(PART, BedPart.FOOT).setValue(OCCUPIED, false)); } @Nullable public static Direction getBedOrientation(BlockGetter p_49486_, BlockPos p_49487_) { BlockState blockstate = p_49486_.getBlockState(p_49487_); return blockstate.getBlock() instanceof BedBlock ? blockstate.getValue(FACING) : null; } @Override protected InteractionResult useWithoutItem(BlockState p_49515_, Level p_49516_, BlockPos p_49517_, Player p_49518_, BlockHitResult p_49520_) { if (p_49516_.isClientSide) { return InteractionResult.SUCCESS_SERVER; } else { if (p_49515_.getValue(PART) != BedPart.HEAD) { p_49517_ = p_49517_.relative(p_49515_.getValue(FACING)); p_49515_ = p_49516_.getBlockState(p_49517_); if (!p_49515_.is(this)) { return InteractionResult.CONSUME; } } if (!canSetSpawn(p_49516_)) { p_49516_.removeBlock(p_49517_, false); BlockPos blockpos = p_49517_.relative(p_49515_.getValue(FACING).getOpposite()); if (p_49516_.getBlockState(blockpos).is(this)) { p_49516_.removeBlock(blockpos, false); } Vec3 vec3 = p_49517_.getCenter(); p_49516_.explode(null, p_49516_.damageSources().badRespawnPointExplosion(vec3), null, vec3, 5.0F, true, Level.ExplosionInteraction.BLOCK); return InteractionResult.SUCCESS_SERVER; } else if (p_49515_.getValue(OCCUPIED)) { if (!this.kickVillagerOutOfBed(p_49516_, p_49517_)) { p_49518_.displayClientMessage(Component.translatable("block.minecraft.bed.occupied"), true); } return InteractionResult.SUCCESS_SERVER; } else { p_49518_.startSleepInBed(p_49517_).ifLeft(p_49477_ -> { if (p_49477_.getMessage() != null) { p_49518_.displayClientMessage(p_49477_.getMessage(), true); } }); return InteractionResult.SUCCESS_SERVER; } } } public static boolean canSetSpawn(Level p_49489_) { return p_49489_.dimensionType().bedWorks(); } private boolean kickVillagerOutOfBed(Level p_49491_, BlockPos p_49492_) { List list = p_49491_.getEntitiesOfClass(Villager.class, new AABB(p_49492_), LivingEntity::isSleeping); if (list.isEmpty()) { return false; } else { list.get(0).stopSleeping(); return true; } } @Override public void fallOn(Level p_152169_, BlockState p_152170_, BlockPos p_152171_, Entity p_152172_, double p_396743_) { super.fallOn(p_152169_, p_152170_, p_152171_, p_152172_, p_396743_ * 0.5); } @Override public void updateEntityMovementAfterFallOn(BlockGetter p_49483_, Entity p_49484_) { if (p_49484_.isSuppressingBounce()) { super.updateEntityMovementAfterFallOn(p_49483_, p_49484_); } else { this.bounceUp(p_49484_); } } private void bounceUp(Entity p_49457_) { Vec3 vec3 = p_49457_.getDeltaMovement(); if (vec3.y < 0.0) { double d0 = p_49457_ instanceof LivingEntity ? 1.0 : 0.8; p_49457_.setDeltaMovement(vec3.x, -vec3.y * 0.66F * d0, vec3.z); } } @Override protected BlockState updateShape( BlockState p_49525_, LevelReader p_367181_, ScheduledTickAccess p_361759_, BlockPos p_49529_, Direction p_49526_, BlockPos p_49530_, BlockState p_49527_, RandomSource p_361707_ ) { if (p_49526_ == getNeighbourDirection(p_49525_.getValue(PART), p_49525_.getValue(FACING))) { return p_49527_.is(this) && p_49527_.getValue(PART) != p_49525_.getValue(PART) ? p_49525_.setValue(OCCUPIED, p_49527_.getValue(OCCUPIED)) : Blocks.AIR.defaultBlockState(); } else { return super.updateShape(p_49525_, p_367181_, p_361759_, p_49529_, p_49526_, p_49530_, p_49527_, p_361707_); } } private static Direction getNeighbourDirection(BedPart p_49534_, Direction p_49535_) { return p_49534_ == BedPart.FOOT ? p_49535_ : p_49535_.getOpposite(); } @Override public BlockState playerWillDestroy(Level p_49505_, BlockPos p_49506_, BlockState p_49507_, Player p_49508_) { if (!p_49505_.isClientSide && p_49508_.preventsBlockDrops()) { BedPart bedpart = p_49507_.getValue(PART); if (bedpart == BedPart.FOOT) { BlockPos blockpos = p_49506_.relative(getNeighbourDirection(bedpart, p_49507_.getValue(FACING))); BlockState blockstate = p_49505_.getBlockState(blockpos); if (blockstate.is(this) && blockstate.getValue(PART) == BedPart.HEAD) { p_49505_.setBlock(blockpos, Blocks.AIR.defaultBlockState(), 35); p_49505_.levelEvent(p_49508_, 2001, blockpos, Block.getId(blockstate)); } } } return super.playerWillDestroy(p_49505_, p_49506_, p_49507_, p_49508_); } @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext p_49479_) { Direction direction = p_49479_.getHorizontalDirection(); BlockPos blockpos = p_49479_.getClickedPos(); BlockPos blockpos1 = blockpos.relative(direction); Level level = p_49479_.getLevel(); return level.getBlockState(blockpos1).canBeReplaced(p_49479_) && level.getWorldBorder().isWithinBounds(blockpos1) ? this.defaultBlockState().setValue(FACING, direction) : null; } @Override protected VoxelShape getShape(BlockState p_49547_, BlockGetter p_49548_, BlockPos p_49549_, CollisionContext p_49550_) { return SHAPES.get(getConnectedDirection(p_49547_).getOpposite()); } public static Direction getConnectedDirection(BlockState p_49558_) { Direction direction = p_49558_.getValue(FACING); return p_49558_.getValue(PART) == BedPart.HEAD ? direction.getOpposite() : direction; } public static DoubleBlockCombiner.BlockType getBlockType(BlockState p_49560_) { BedPart bedpart = p_49560_.getValue(PART); return bedpart == BedPart.HEAD ? DoubleBlockCombiner.BlockType.FIRST : DoubleBlockCombiner.BlockType.SECOND; } private static boolean isBunkBed(BlockGetter p_49542_, BlockPos p_49543_) { return p_49542_.getBlockState(p_49543_.below()).getBlock() instanceof BedBlock; } public static Optional findStandUpPosition(EntityType p_261547_, CollisionGetter p_261946_, BlockPos p_261614_, Direction p_261648_, float p_261680_) { Direction direction = p_261648_.getClockWise(); Direction direction1 = direction.isFacingAngle(p_261680_) ? direction.getOpposite() : direction; if (isBunkBed(p_261946_, p_261614_)) { return findBunkBedStandUpPosition(p_261547_, p_261946_, p_261614_, p_261648_, direction1); } else { int[][] aint = bedStandUpOffsets(p_261648_, direction1); Optional optional = findStandUpPositionAtOffset(p_261547_, p_261946_, p_261614_, aint, true); return optional.isPresent() ? optional : findStandUpPositionAtOffset(p_261547_, p_261946_, p_261614_, aint, false); } } private static Optional findBunkBedStandUpPosition(EntityType p_49464_, CollisionGetter p_49465_, BlockPos p_49466_, Direction p_49467_, Direction p_49468_) { int[][] aint = bedSurroundStandUpOffsets(p_49467_, p_49468_); Optional optional = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint, true); if (optional.isPresent()) { return optional; } else { BlockPos blockpos = p_49466_.below(); Optional optional1 = findStandUpPositionAtOffset(p_49464_, p_49465_, blockpos, aint, true); if (optional1.isPresent()) { return optional1; } else { int[][] aint1 = bedAboveStandUpOffsets(p_49467_); Optional optional2 = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint1, true); if (optional2.isPresent()) { return optional2; } else { Optional optional3 = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint, false); if (optional3.isPresent()) { return optional3; } else { Optional optional4 = findStandUpPositionAtOffset(p_49464_, p_49465_, blockpos, aint, false); return optional4.isPresent() ? optional4 : findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint1, false); } } } } } private static Optional findStandUpPositionAtOffset(EntityType p_49470_, CollisionGetter p_49471_, BlockPos p_49472_, int[][] p_49473_, boolean p_49474_) { BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (int[] aint : p_49473_) { blockpos$mutableblockpos.set(p_49472_.getX() + aint[0], p_49472_.getY(), p_49472_.getZ() + aint[1]); Vec3 vec3 = DismountHelper.findSafeDismountLocation(p_49470_, p_49471_, blockpos$mutableblockpos, p_49474_); if (vec3 != null) { return Optional.of(vec3); } } return Optional.empty(); } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_49532_) { p_49532_.add(FACING, PART, OCCUPIED); } @Override public BlockEntity newBlockEntity(BlockPos p_152175_, BlockState p_152176_) { return new BedBlockEntity(p_152175_, p_152176_, this.color); } @Override public void setPlacedBy(Level p_49499_, BlockPos p_49500_, BlockState p_49501_, @Nullable LivingEntity p_49502_, ItemStack p_49503_) { super.setPlacedBy(p_49499_, p_49500_, p_49501_, p_49502_, p_49503_); if (!p_49499_.isClientSide) { BlockPos blockpos = p_49500_.relative(p_49501_.getValue(FACING)); p_49499_.setBlock(blockpos, p_49501_.setValue(PART, BedPart.HEAD), 3); p_49499_.updateNeighborsAt(p_49500_, Blocks.AIR); p_49501_.updateNeighbourShapes(p_49499_, p_49500_, 3); } } public DyeColor getColor() { return this.color; } @Override protected long getSeed(BlockState p_49522_, BlockPos p_49523_) { BlockPos blockpos = p_49523_.relative(p_49522_.getValue(FACING), p_49522_.getValue(PART) == BedPart.HEAD ? 0 : 1); return Mth.getSeed(blockpos.getX(), p_49523_.getY(), blockpos.getZ()); } @Override protected boolean isPathfindable(BlockState p_49510_, PathComputationType p_49513_) { return false; } private static int[][] bedStandUpOffsets(Direction p_49539_, Direction p_49540_) { return ArrayUtils.addAll((int[][])bedSurroundStandUpOffsets(p_49539_, p_49540_), (int[][])bedAboveStandUpOffsets(p_49539_)); } private static int[][] bedSurroundStandUpOffsets(Direction p_49552_, Direction p_49553_) { return new int[][]{ {p_49553_.getStepX(), p_49553_.getStepZ()}, {p_49553_.getStepX() - p_49552_.getStepX(), p_49553_.getStepZ() - p_49552_.getStepZ()}, {p_49553_.getStepX() - p_49552_.getStepX() * 2, p_49553_.getStepZ() - p_49552_.getStepZ() * 2}, {-p_49552_.getStepX() * 2, -p_49552_.getStepZ() * 2}, {-p_49553_.getStepX() - p_49552_.getStepX() * 2, -p_49553_.getStepZ() - p_49552_.getStepZ() * 2}, {-p_49553_.getStepX() - p_49552_.getStepX(), -p_49553_.getStepZ() - p_49552_.getStepZ()}, {-p_49553_.getStepX(), -p_49553_.getStepZ()}, {-p_49553_.getStepX() + p_49552_.getStepX(), -p_49553_.getStepZ() + p_49552_.getStepZ()}, {p_49552_.getStepX(), p_49552_.getStepZ()}, {p_49553_.getStepX() + p_49552_.getStepX(), p_49553_.getStepZ() + p_49552_.getStepZ()} }; } private static int[][] bedAboveStandUpOffsets(Direction p_49537_) { return new int[][]{{0, 0}, {-p_49537_.getStepX(), -p_49537_.getStepZ()}}; } }