minecraft-source/src/net/minecraft/world/level/block/ChestBlock.java

375 lines
17 KiB
Java

package net.minecraft.world.level.block;
import net.minecraft.world.level.block.state.AbstractStateHolder;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.CompoundContainer;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.pathfinder.PathComputationType;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.inventory.AbstractContainerMenu;
import java.util.Iterator;
import java.util.List;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.animal.Cat;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.world.level.block.entity.LidBlockEntity;
import java.util.function.BiPredicate;
import net.minecraft.stats.Stats;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.stats.Stat;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.Containers;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.item.BlockPlaceContext;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.function.Supplier;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Container;
import java.util.Optional;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
public class ChestBlock extends AbstractChestBlock<ChestBlockEntity> implements SimpleWaterloggedBlock {
public static final DirectionProperty FACING;
public static final EnumProperty<ChestType> TYPE;
public static final BooleanProperty WATERLOGGED;
protected static final VoxelShape NORTH_AABB;
protected static final VoxelShape SOUTH_AABB;
protected static final VoxelShape WEST_AABB;
protected static final VoxelShape EAST_AABB;
protected static final VoxelShape AABB;
private static final DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<Container>> CHEST_COMBINER;
private static final DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>> MENU_PROVIDER_COMBINER;
protected ChestBlock(final Properties c, final Supplier<BlockEntityType<? extends ChestBlockEntity>> supplier) {
super(c, supplier);
this.registerDefaultState(((((AbstractStateHolder<O, BlockState>)this.stateDefinition.any()).setValue((Property<Comparable>)ChestBlock.FACING, Direction.NORTH)).setValue(ChestBlock.TYPE, ChestType.SINGLE)).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, false));
}
public static DoubleBlockCombiner.BlockType getBlockType(final BlockState byg) {
final ChestType byz2 = byg.<ChestType>getValue(ChestBlock.TYPE);
if (byz2 == ChestType.SINGLE) {
return DoubleBlockCombiner.BlockType.SINGLE;
}
if (byz2 == ChestType.RIGHT) {
return DoubleBlockCombiner.BlockType.FIRST;
}
return DoubleBlockCombiner.BlockType.SECOND;
}
@Override
public RenderShape getRenderShape(final BlockState byg) {
return RenderShape.ENTITYBLOCK_ANIMATED;
}
@Override
public BlockState updateShape(final BlockState byg1, final Direction fp, final BlockState byg3, final LevelAccessor bju, final BlockPos fk5, final BlockPos fk6) {
if (byg1.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
bju.getLiquidTicks().scheduleTick(fk5, Fluids.WATER, Fluids.WATER.getTickDelay(bju));
}
if (byg3.getBlock() == this && fp.getAxis().isHorizontal()) {
final ChestType byz8 = byg3.<ChestType>getValue(ChestBlock.TYPE);
if (byg1.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE && byz8 != ChestType.SINGLE && byg1.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) == byg3.<Comparable>getValue((Property<Comparable>)ChestBlock.FACING) && getConnectedDirection(byg3) == fp.getOpposite()) {
return ((AbstractStateHolder<O, BlockState>)byg1).<ChestType, ChestType>setValue(ChestBlock.TYPE, byz8.getOpposite());
}
}
else if (getConnectedDirection(byg1) == fp) {
return ((AbstractStateHolder<O, BlockState>)byg1).<ChestType, ChestType>setValue(ChestBlock.TYPE, ChestType.SINGLE);
}
return super.updateShape(byg1, fp, byg3, bju, fk5, fk6);
}
@Override
public VoxelShape getShape(final BlockState byg, final BlockGetter bjd, final BlockPos fk, final CollisionContext cvn) {
if (byg.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) {
return ChestBlock.AABB;
}
switch (getConnectedDirection(byg)) {
default: {
return ChestBlock.NORTH_AABB;
}
case SOUTH: {
return ChestBlock.SOUTH_AABB;
}
case WEST: {
return ChestBlock.WEST_AABB;
}
case EAST: {
return ChestBlock.EAST_AABB;
}
}
}
public static Direction getConnectedDirection(final BlockState byg) {
final Direction fp2 = byg.<Direction>getValue((Property<Direction>)ChestBlock.FACING);
return (byg.<ChestType>getValue(ChestBlock.TYPE) == ChestType.LEFT) ? fp2.getClockWise() : fp2.getCounterClockWise();
}
@Override
public BlockState getStateForPlacement(final BlockPlaceContext bcn) {
ChestType byz3 = ChestType.SINGLE;
Direction fp4 = bcn.getHorizontalDirection().getOpposite();
final FluidState cog5 = bcn.getLevel().getFluidState(bcn.getClickedPos());
final boolean boolean6 = bcn.isSecondaryUseActive();
final Direction fp5 = bcn.getClickedFace();
if (fp5.getAxis().isHorizontal() && boolean6) {
final Direction fp6 = this.candidatePartnerFacing(bcn, fp5.getOpposite());
if (fp6 != null && fp6.getAxis() != fp5.getAxis()) {
fp4 = fp6;
byz3 = ((fp4.getCounterClockWise() == fp5.getOpposite()) ? ChestType.RIGHT : ChestType.LEFT);
}
}
if (byz3 == ChestType.SINGLE && !boolean6) {
if (fp4 == this.candidatePartnerFacing(bcn, fp4.getClockWise())) {
byz3 = ChestType.LEFT;
}
else if (fp4 == this.candidatePartnerFacing(bcn, fp4.getCounterClockWise())) {
byz3 = ChestType.RIGHT;
}
}
return ((((AbstractStateHolder<O, BlockState>)this.defaultBlockState()).setValue((Property<Comparable>)ChestBlock.FACING, fp4)).setValue(ChestBlock.TYPE, byz3)).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, cog5.getType() == Fluids.WATER);
}
@Override
public FluidState getFluidState(final BlockState byg) {
if (byg.<Boolean>getValue((Property<Boolean>)ChestBlock.WATERLOGGED)) {
return Fluids.WATER.getSource(false);
}
return super.getFluidState(byg);
}
@Nullable
private Direction candidatePartnerFacing(final BlockPlaceContext bcn, final Direction fp) {
final BlockState byg4 = bcn.getLevel().getBlockState(bcn.getClickedPos().relative(fp));
return (byg4.getBlock() == this && byg4.<ChestType>getValue(ChestBlock.TYPE) == ChestType.SINGLE) ? byg4.<Direction>getValue((Property<Direction>)ChestBlock.FACING) : null;
}
@Override
public void setPlacedBy(final Level bjt, final BlockPos fk, final BlockState byg, final LivingEntity akw, final ItemStack bek) {
if (bek.hasCustomHoverName()) {
final BlockEntity bwi7 = bjt.getBlockEntity(fk);
if (bwi7 instanceof ChestBlockEntity) {
((ChestBlockEntity)bwi7).setCustomName(bek.getHoverName());
}
}
}
@Override
public void onRemove(final BlockState byg1, final Level bjt, final BlockPos fk, final BlockState byg4, final boolean boolean5) {
if (byg1.getBlock() == byg4.getBlock()) {
return;
}
final BlockEntity bwi7 = bjt.getBlockEntity(fk);
if (bwi7 instanceof Container) {
Containers.dropContents(bjt, fk, (Container)bwi7);
bjt.updateNeighbourForOutputSignal(fk, this);
}
super.onRemove(byg1, bjt, fk, byg4, boolean5);
}
@Override
public InteractionResult use(final BlockState byg, final Level bjt, final BlockPos fk, final Player ayg, final InteractionHand ajh, final BlockHitResult cvd) {
if (bjt.isClientSide) {
return InteractionResult.SUCCESS;
}
final MenuProvider ajl8 = this.getMenuProvider(byg, bjt, fk);
if (ajl8 != null) {
ayg.openMenu(ajl8);
ayg.awardStat(this.getOpenChestStat());
}
return InteractionResult.SUCCESS;
}
protected Stat<ResourceLocation> getOpenChestStat() {
return Stats.CUSTOM.get(Stats.OPEN_CHEST);
}
@Nullable
public static Container getContainer(final ChestBlock bpt, final BlockState byg, final Level bjt, final BlockPos fk, final boolean boolean5) {
return bpt.combine(byg, bjt, fk, boolean5).<Optional<Container>>apply(ChestBlock.CHEST_COMBINER).orElse(null);
}
@Override
public DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> combine(final BlockState byg, final Level bjt, final BlockPos fk, final boolean boolean4) {
BiPredicate<LevelAccessor, BlockPos> biPredicate6;
if (boolean4) {
biPredicate6 = ((bju, fk) -> false);
}
else {
biPredicate6 = ChestBlock::isChestBlockedAt;
}
return DoubleBlockCombiner.combineWithNeigbour(this.blockEntityType.get(), ChestBlock::getBlockType, ChestBlock::getConnectedDirection, ChestBlock.FACING, byg, bjt, fk, biPredicate6);
}
@Nullable
@Override
public MenuProvider getMenuProvider(final BlockState byg, final Level bjt, final BlockPos fk) {
return this.combine(byg, bjt, fk, false).<Optional<MenuProvider>>apply(ChestBlock.MENU_PROVIDER_COMBINER).orElse(null);
}
public static DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction> opennessCombiner(final LidBlockEntity bxb) {
return new DoubleBlockCombiner.Combiner<ChestBlockEntity, Float2FloatFunction>() {
@Override
public Float2FloatFunction acceptDouble(final ChestBlockEntity bwm1, final ChestBlockEntity bwm2) {
return float3 -> Math.max(bwm1.getOpenNess(float3), bwm2.getOpenNess(float3));
}
@Override
public Float2FloatFunction acceptSingle(final ChestBlockEntity bwm) {
return bwm::getOpenNess;
}
@Override
public Float2FloatFunction acceptNone() {
return bxb::getOpenNess;
}
};
}
@Override
public BlockEntity newBlockEntity(final BlockGetter bjd) {
return new ChestBlockEntity();
}
public static boolean isChestBlockedAt(final LevelAccessor bju, final BlockPos fk) {
return isBlockedChestByBlock(bju, fk) || isCatSittingOnChest(bju, fk);
}
private static boolean isBlockedChestByBlock(final BlockGetter bjd, final BlockPos fk) {
final BlockPos fk2 = fk.above();
return bjd.getBlockState(fk2).isRedstoneConductor(bjd, fk2);
}
private static boolean isCatSittingOnChest(final LevelAccessor bju, final BlockPos fk) {
final List<Cat> list3 = bju.<Cat>getEntitiesOfClass(Cat.class, new AABB(fk.getX(), fk.getY() + 1, fk.getZ(), fk.getX() + 1, fk.getY() + 2, fk.getZ() + 1));
if (!list3.isEmpty()) {
for (final Cat atb5 : list3) {
if (atb5.isSitting()) {
return true;
}
}
}
return false;
}
@Override
public boolean hasAnalogOutputSignal(final BlockState byg) {
return true;
}
@Override
public int getAnalogOutputSignal(final BlockState byg, final Level bjt, final BlockPos fk) {
return AbstractContainerMenu.getRedstoneSignalFromContainer(getContainer(this, byg, bjt, fk, false));
}
@Override
public BlockState rotate(final BlockState byg, final Rotation btr) {
return ((AbstractStateHolder<O, BlockState>)byg).<Comparable, Direction>setValue((Property<Comparable>)ChestBlock.FACING, btr.rotate(byg.<Direction>getValue((Property<Direction>)ChestBlock.FACING)));
}
@Override
public BlockState mirror(final BlockState byg, final Mirror bsr) {
return byg.rotate(bsr.getRotation(byg.<Direction>getValue((Property<Direction>)ChestBlock.FACING)));
}
@Override
protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> a) {
a.add(ChestBlock.FACING, ChestBlock.TYPE, ChestBlock.WATERLOGGED);
}
@Override
public boolean isPathfindable(final BlockState byg, final BlockGetter bjd, final BlockPos fk, final PathComputationType cqo) {
return false;
}
static {
FACING = HorizontalDirectionalBlock.FACING;
TYPE = BlockStateProperties.CHEST_TYPE;
WATERLOGGED = BlockStateProperties.WATERLOGGED;
NORTH_AABB = Block.box(1.0, 0.0, 0.0, 15.0, 14.0, 15.0);
SOUTH_AABB = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 16.0);
WEST_AABB = Block.box(0.0, 0.0, 1.0, 15.0, 14.0, 15.0);
EAST_AABB = Block.box(1.0, 0.0, 1.0, 16.0, 14.0, 15.0);
AABB = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 15.0);
CHEST_COMBINER = new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<Container>>() {
@Override
public Optional<Container> acceptDouble(final ChestBlockEntity bwm1, final ChestBlockEntity bwm2) {
return Optional.of(new CompoundContainer(bwm1, bwm2));
}
@Override
public Optional<Container> acceptSingle(final ChestBlockEntity bwm) {
return Optional.of(bwm);
}
@Override
public Optional<Container> acceptNone() {
return Optional.<Container>empty();
}
};
MENU_PROVIDER_COMBINER = new DoubleBlockCombiner.Combiner<ChestBlockEntity, Optional<MenuProvider>>() {
@Override
public Optional<MenuProvider> acceptDouble(final ChestBlockEntity bwm1, final ChestBlockEntity bwm2) {
final Container ajb4 = new CompoundContainer(bwm1, bwm2);
return Optional.of(new MenuProvider() {
@Nullable
@Override
public AbstractContainerMenu createMenu(final int integer, final Inventory ayf, final Player ayg) {
if (bwm1.canOpen(ayg) && bwm2.canOpen(ayg)) {
bwm1.unpackLootTable(ayf.player);
bwm2.unpackLootTable(ayf.player);
return ChestMenu.sixRows(integer, ayf, ajb4);
}
return null;
}
@Override
public Component getDisplayName() {
if (bwm1.hasCustomName()) {
return bwm1.getDisplayName();
}
if (bwm2.hasCustomName()) {
return bwm2.getDisplayName();
}
return new TranslatableComponent("container.chestDouble", new Object[0]);
}
});
}
@Override
public Optional<MenuProvider> acceptSingle(final ChestBlockEntity bwm) {
return Optional.of(bwm);
}
@Override
public Optional<MenuProvider> acceptNone() {
return Optional.<MenuProvider>empty();
}
};
}
}