package lv.enes.mc.eris_alchemy.block; import jakarta.annotation.Nonnull; import lv.enes.mc.eris_alchemy.block.entity.ChestLikeEntity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; import net.minecraft.world.*; 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.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.DoubleBlockCombiner.NeighborCombineResult; 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.ChestBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; 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.material.FluidState; import net.minecraft.world.level.material.Fluids; 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 java.util.function.Supplier; public abstract class ChestLikeBlock extends AbstractChestBlock implements SimpleWaterloggedBlock { public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; public static final VoxelShape SHAPE = Block.box( 1.0, 0.0, 1.0, 15.0, 14.0, 15.0 ); protected ChestLikeBlock(Properties settings, Supplier> blockEntityTypeSupplier) { super(settings, blockEntityTypeSupplier); registerDefaultState(getStateDefinition().any() .setValue(FACING, Direction.NORTH) .setValue(WATERLOGGED, false) ); } @Nonnull protected abstract Component getContainerTitle(); @Nonnull @Override public NeighborCombineResult combine( BlockState state, Level world, BlockPos pos, boolean ignoreBlocked ) { return DoubleBlockCombiner.Combiner::acceptNone; } @SuppressWarnings("deprecation") @Override public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) { if (world.getBlockEntity(pos) instanceof Container container) { return AbstractContainerMenu.getRedstoneSignalFromContainer(container); } return 0; } @SuppressWarnings("deprecation") @Nonnull @Override public FluidState getFluidState(BlockState state) { return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); } @Override public MenuProvider getMenuProvider(BlockState state, Level world, BlockPos pos) { if (world.getBlockEntity(pos) instanceof ChestLikeEntity entity) { return new SimpleMenuProvider(entity, getContainerTitle()); } return null; } @Nonnull @Override public RenderShape getRenderShape(BlockState state) { return RenderShape.ENTITYBLOCK_ANIMATED; } @SuppressWarnings("deprecation") @Nonnull @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext ctx) { return SHAPE; } @Override public BlockState getStateForPlacement(BlockPlaceContext ctx) { return defaultBlockState() .setValue(FACING, ctx.getHorizontalDirection().getOpposite()) .setValue(WATERLOGGED, ctx.getLevel().getFluidState(ctx.getClickedPos()).is(Fluids.WATER)); } @Override public BlockEntityTicker getTicker( Level ignoredWorld, BlockState ignoredState, BlockEntityType type ) { return createTickerHelper( type, blockEntityType.get(), (world, pos, state, entity) -> entity.tick(world, pos, state) ); } @SuppressWarnings("deprecation") @Override public boolean hasAnalogOutputSignal(BlockState state) { return true; } @SuppressWarnings("deprecation") @Override public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) { return false; } @SuppressWarnings("deprecation") @Nonnull @Override public BlockState mirror(BlockState state, Mirror mirror) { return state.rotate(mirror.getRotation(state.getValue(FACING))); } @SuppressWarnings("deprecation") @Override public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moved) { if (!state.is(newState.getBlock())) { if (world.getBlockEntity(pos) instanceof Container container) { Containers.dropContents(world, pos, container); world.updateNeighbourForOutputSignal(pos, this); } super.onRemove(state, world, pos, newState, moved); } } @SuppressWarnings("deprecation") @Nonnull @Override public BlockState rotate(BlockState state, Rotation rotation) { return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); } @SuppressWarnings("deprecation") @Nonnull @Override public BlockState updateShape( BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos ) { if (state.getValue(WATERLOGGED)) { world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); } return super.updateShape(state, direction, neighborState, world, pos, neighborPos); } @SuppressWarnings("deprecation") @Nonnull @Override public InteractionResult use( BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit ) { if (world.isClientSide) { return InteractionResult.SUCCESS; } var provider = getMenuProvider(state, world, pos); if (provider != null) { player.openMenu(provider); // TODO: player.awardStat(getOpenChestStat); PiglinAi.angerNearbyPiglins(player, true); } return InteractionResult.CONSUME; } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(FACING, WATERLOGGED); } }