339 lines
16 KiB
Java
339 lines
16 KiB
Java
|
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<BedBlock> 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<BedPart> PART = BlockStateProperties.BED_PART;
|
||
|
public static final BooleanProperty OCCUPIED = BlockStateProperties.OCCUPIED;
|
||
|
private static final Map<Direction, VoxelShape> 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<BedBlock> 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<Villager> 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<Vec3> 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<Vec3> 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<Vec3> findBunkBedStandUpPosition(EntityType<?> p_49464_, CollisionGetter p_49465_, BlockPos p_49466_, Direction p_49467_, Direction p_49468_) {
|
||
|
int[][] aint = bedSurroundStandUpOffsets(p_49467_, p_49468_);
|
||
|
Optional<Vec3> optional = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint, true);
|
||
|
if (optional.isPresent()) {
|
||
|
return optional;
|
||
|
} else {
|
||
|
BlockPos blockpos = p_49466_.below();
|
||
|
Optional<Vec3> optional1 = findStandUpPositionAtOffset(p_49464_, p_49465_, blockpos, aint, true);
|
||
|
if (optional1.isPresent()) {
|
||
|
return optional1;
|
||
|
} else {
|
||
|
int[][] aint1 = bedAboveStandUpOffsets(p_49467_);
|
||
|
Optional<Vec3> optional2 = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint1, true);
|
||
|
if (optional2.isPresent()) {
|
||
|
return optional2;
|
||
|
} else {
|
||
|
Optional<Vec3> optional3 = findStandUpPositionAtOffset(p_49464_, p_49465_, p_49466_, aint, false);
|
||
|
if (optional3.isPresent()) {
|
||
|
return optional3;
|
||
|
} else {
|
||
|
Optional<Vec3> 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<Vec3> 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<Block, BlockState> 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()}};
|
||
|
}
|
||
|
}
|