217 lines
8.3 KiB
Java
217 lines
8.3 KiB
Java
package net.minecraft.world.level.block;
|
|
|
|
import com.mojang.serialization.MapCodec;
|
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.stats.Stats;
|
|
import net.minecraft.world.Containers;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
import net.minecraft.world.entity.monster.Shulker;
|
|
import net.minecraft.world.entity.monster.piglin.PiglinAi;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
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.Level;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.block.state.StateDefinition.Builder;
|
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
|
import net.minecraft.world.phys.AABB;
|
|
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;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class ShulkerBoxBlock extends BaseEntityBlock {
|
|
public static final MapCodec<ShulkerBoxBlock> CODEC = RecordCodecBuilder.mapCodec(
|
|
instance -> instance.group(
|
|
DyeColor.CODEC.optionalFieldOf("color").forGetter(shulkerBoxBlock -> Optional.ofNullable(shulkerBoxBlock.color)), propertiesCodec()
|
|
)
|
|
.apply(instance, (optional, properties) -> new ShulkerBoxBlock((DyeColor)optional.orElse(null), properties))
|
|
);
|
|
public static final Map<Direction, VoxelShape> SHAPES_OPEN_SUPPORT = Shapes.rotateAll(Block.boxZ(16.0, 0.0, 1.0));
|
|
public static final EnumProperty<Direction> FACING = DirectionalBlock.FACING;
|
|
public static final ResourceLocation CONTENTS = ResourceLocation.withDefaultNamespace("contents");
|
|
@Nullable
|
|
private final DyeColor color;
|
|
|
|
@Override
|
|
public MapCodec<ShulkerBoxBlock> codec() {
|
|
return CODEC;
|
|
}
|
|
|
|
public ShulkerBoxBlock(@Nullable DyeColor color, BlockBehaviour.Properties properties) {
|
|
super(properties);
|
|
this.color = color;
|
|
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.UP));
|
|
}
|
|
|
|
@Override
|
|
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
|
return new ShulkerBoxBlockEntity(this.color, pos, state);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
|
return createTickerHelper(blockEntityType, BlockEntityType.SHULKER_BOX, ShulkerBoxBlockEntity::tick);
|
|
}
|
|
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
|
|
if (level instanceof ServerLevel serverLevel
|
|
&& level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity
|
|
&& canOpen(state, level, pos, shulkerBoxBlockEntity)) {
|
|
player.openMenu(shulkerBoxBlockEntity);
|
|
player.awardStat(Stats.OPEN_SHULKER_BOX);
|
|
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
|
|
}
|
|
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
|
|
private static boolean canOpen(BlockState state, Level level, BlockPos pos, ShulkerBoxBlockEntity blockEntity) {
|
|
if (blockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
|
|
return true;
|
|
} else {
|
|
AABB aABB = Shulker.getProgressDeltaAabb(1.0F, state.getValue(FACING), 0.0F, 0.5F, pos.getBottomCenter()).deflate(1.0E-6);
|
|
return level.noCollision(aABB);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
|
return this.defaultBlockState().setValue(FACING, context.getClickedFace());
|
|
}
|
|
|
|
@Override
|
|
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
|
|
builder.add(FACING);
|
|
}
|
|
|
|
@Override
|
|
public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
|
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) {
|
|
if (!level.isClientSide && player.preventsBlockDrops() && !shulkerBoxBlockEntity.isEmpty()) {
|
|
ItemStack itemStack = getColoredItemStack(this.getColor());
|
|
itemStack.applyComponents(blockEntity.collectComponents());
|
|
ItemEntity itemEntity = new ItemEntity(level, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, itemStack);
|
|
itemEntity.setDefaultPickUpDelay();
|
|
level.addFreshEntity(itemEntity);
|
|
} else {
|
|
shulkerBoxBlockEntity.unpackLootTable(player);
|
|
}
|
|
}
|
|
|
|
return super.playerWillDestroy(level, pos, state, player);
|
|
}
|
|
|
|
@Override
|
|
protected List<ItemStack> getDrops(BlockState state, net.minecraft.world.level.storage.loot.LootParams.Builder params) {
|
|
BlockEntity blockEntity = params.getOptionalParameter(LootContextParams.BLOCK_ENTITY);
|
|
if (blockEntity instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity) {
|
|
params = params.withDynamicDrop(CONTENTS, consumer -> {
|
|
for (int i = 0; i < shulkerBoxBlockEntity.getContainerSize(); i++) {
|
|
consumer.accept(shulkerBoxBlockEntity.getItem(i));
|
|
}
|
|
});
|
|
}
|
|
|
|
return super.getDrops(state, params);
|
|
}
|
|
|
|
@Override
|
|
protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
|
|
Containers.updateNeighboursAfterDestroy(state, level, pos);
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getBlockSupportShape(BlockState state, BlockGetter level, BlockPos pos) {
|
|
return level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity && !shulkerBoxBlockEntity.isClosed()
|
|
? (VoxelShape)SHAPES_OPEN_SUPPORT.get(((Direction)state.getValue(FACING)).getOpposite())
|
|
: Shapes.block();
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
|
return level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity
|
|
? Shapes.create(shulkerBoxBlockEntity.getBoundingBox(state))
|
|
: Shapes.block();
|
|
}
|
|
|
|
@Override
|
|
protected boolean propagatesSkylightDown(BlockState state) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean hasAnalogOutputSignal(BlockState state) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) {
|
|
return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos));
|
|
}
|
|
|
|
public static Block getBlockByColor(@Nullable DyeColor color) {
|
|
if (color == null) {
|
|
return Blocks.SHULKER_BOX;
|
|
} else {
|
|
return switch (color) {
|
|
case WHITE -> Blocks.WHITE_SHULKER_BOX;
|
|
case ORANGE -> Blocks.ORANGE_SHULKER_BOX;
|
|
case MAGENTA -> Blocks.MAGENTA_SHULKER_BOX;
|
|
case LIGHT_BLUE -> Blocks.LIGHT_BLUE_SHULKER_BOX;
|
|
case YELLOW -> Blocks.YELLOW_SHULKER_BOX;
|
|
case LIME -> Blocks.LIME_SHULKER_BOX;
|
|
case PINK -> Blocks.PINK_SHULKER_BOX;
|
|
case GRAY -> Blocks.GRAY_SHULKER_BOX;
|
|
case LIGHT_GRAY -> Blocks.LIGHT_GRAY_SHULKER_BOX;
|
|
case CYAN -> Blocks.CYAN_SHULKER_BOX;
|
|
case BLUE -> Blocks.BLUE_SHULKER_BOX;
|
|
case BROWN -> Blocks.BROWN_SHULKER_BOX;
|
|
case GREEN -> Blocks.GREEN_SHULKER_BOX;
|
|
case RED -> Blocks.RED_SHULKER_BOX;
|
|
case BLACK -> Blocks.BLACK_SHULKER_BOX;
|
|
case PURPLE -> Blocks.PURPLE_SHULKER_BOX;
|
|
};
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public DyeColor getColor() {
|
|
return this.color;
|
|
}
|
|
|
|
public static ItemStack getColoredItemStack(@Nullable DyeColor color) {
|
|
return new ItemStack(getBlockByColor(color));
|
|
}
|
|
|
|
@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 state.rotate(mirror.getRotation(state.getValue(FACING)));
|
|
}
|
|
}
|