package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.chat.Component; import net.minecraft.tags.BlockTags; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.Nameable; import net.minecraft.world.SimpleMenuProvider; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.EnchantmentMenu; 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.EnchantingTableBlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; public class EnchantingTableBlock extends BaseEntityBlock { public static final MapCodec CODEC = simpleCodec(EnchantingTableBlock::new); protected static final VoxelShape SHAPE = Block.box(0.0, 0.0, 0.0, 16.0, 12.0, 16.0); public static final List BOOKSHELF_OFFSETS = BlockPos.betweenClosedStream(-2, 0, -2, 2, 1, 2) .filter(blockPos -> Math.abs(blockPos.getX()) == 2 || Math.abs(blockPos.getZ()) == 2) .map(BlockPos::immutable) .toList(); @Override public MapCodec codec() { return CODEC; } protected EnchantingTableBlock(BlockBehaviour.Properties properties) { super(properties); } public static boolean isValidBookShelf(Level level, BlockPos enchantingTablePos, BlockPos bookshelfPos) { return level.getBlockState(enchantingTablePos.offset(bookshelfPos)).is(BlockTags.ENCHANTMENT_POWER_PROVIDER) && level.getBlockState(enchantingTablePos.offset(bookshelfPos.getX() / 2, bookshelfPos.getY(), bookshelfPos.getZ() / 2)) .is(BlockTags.ENCHANTMENT_POWER_TRANSMITTER); } @Override protected boolean useShapeForLightOcclusion(BlockState state) { return true; } @Override protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { super.animateTick(state, level, pos, random); for (BlockPos blockPos : BOOKSHELF_OFFSETS) { if (random.nextInt(16) == 0 && isValidBookShelf(level, pos, blockPos)) { level.addParticle( ParticleTypes.ENCHANT, pos.getX() + 0.5, pos.getY() + 2.0, pos.getZ() + 0.5, blockPos.getX() + random.nextFloat() - 0.5, blockPos.getY() - random.nextFloat() - 1.0F, blockPos.getZ() + random.nextFloat() - 0.5 ); } } } @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new EnchantingTableBlockEntity(pos, state); } @Nullable @Override public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType blockEntityType) { return level.isClientSide ? createTickerHelper(blockEntityType, BlockEntityType.ENCHANTING_TABLE, EnchantingTableBlockEntity::bookAnimationTick) : null; } @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { if (!level.isClientSide) { player.openMenu(state.getMenuProvider(level, pos)); } return InteractionResult.SUCCESS; } @Nullable @Override protected MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) { BlockEntity blockEntity = level.getBlockEntity(pos); if (blockEntity instanceof EnchantingTableBlockEntity) { Component component = ((Nameable)blockEntity).getDisplayName(); return new SimpleMenuProvider((i, inventory, player) -> new EnchantmentMenu(i, inventory, ContainerLevelAccess.create(level, pos)), component); } else { return null; } } @Override protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { return false; } }