package net.minecraft.world.level.block.entity; import java.util.List; import java.util.stream.IntStream; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.ContainerHelper; import net.minecraft.world.WorldlyContainer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.monster.Shulker; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ShulkerBoxMenu; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.ShulkerBoxBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer { public static final int COLUMNS = 9; public static final int ROWS = 3; public static final int CONTAINER_SIZE = 27; public static final int EVENT_SET_OPEN_COUNT = 1; public static final int OPENING_TICK_LENGTH = 10; public static final float MAX_LID_HEIGHT = 0.5F; public static final float MAX_LID_ROTATION = 270.0F; private static final int[] SLOTS = IntStream.range(0, 27).toArray(); private NonNullList itemStacks = NonNullList.withSize(27, ItemStack.EMPTY); private int openCount; private ShulkerBoxBlockEntity.AnimationStatus animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSED; private float progress; private float progressOld; @Nullable private final DyeColor color; public ShulkerBoxBlockEntity(@Nullable DyeColor p_155666_, BlockPos p_155667_, BlockState p_155668_) { super(BlockEntityType.SHULKER_BOX, p_155667_, p_155668_); this.color = p_155666_; } public ShulkerBoxBlockEntity(BlockPos p_155670_, BlockState p_155671_) { super(BlockEntityType.SHULKER_BOX, p_155670_, p_155671_); this.color = p_155671_.getBlock() instanceof ShulkerBoxBlock shulkerboxblock ? shulkerboxblock.getColor() : null; } public static void tick(Level p_155673_, BlockPos p_155674_, BlockState p_155675_, ShulkerBoxBlockEntity p_155676_) { p_155676_.updateAnimation(p_155673_, p_155674_, p_155675_); } private void updateAnimation(Level p_155680_, BlockPos p_155681_, BlockState p_155682_) { this.progressOld = this.progress; switch (this.animationStatus) { case CLOSED: this.progress = 0.0F; break; case OPENING: this.progress += 0.1F; if (this.progressOld == 0.0F) { doNeighborUpdates(p_155680_, p_155681_, p_155682_); } if (this.progress >= 1.0F) { this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.OPENED; this.progress = 1.0F; doNeighborUpdates(p_155680_, p_155681_, p_155682_); } this.moveCollidedEntities(p_155680_, p_155681_, p_155682_); break; case OPENED: this.progress = 1.0F; break; case CLOSING: this.progress -= 0.1F; if (this.progressOld == 1.0F) { doNeighborUpdates(p_155680_, p_155681_, p_155682_); } if (this.progress <= 0.0F) { this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSED; this.progress = 0.0F; doNeighborUpdates(p_155680_, p_155681_, p_155682_); } } } public ShulkerBoxBlockEntity.AnimationStatus getAnimationStatus() { return this.animationStatus; } public AABB getBoundingBox(BlockState p_59667_) { Vec3 vec3 = new Vec3(0.5, 0.0, 0.5); return Shulker.getProgressAabb(1.0F, p_59667_.getValue(ShulkerBoxBlock.FACING), 0.5F * this.getProgress(1.0F), vec3); } private void moveCollidedEntities(Level p_155684_, BlockPos p_155685_, BlockState p_155686_) { if (p_155686_.getBlock() instanceof ShulkerBoxBlock) { Direction direction = p_155686_.getValue(ShulkerBoxBlock.FACING); AABB aabb = Shulker.getProgressDeltaAabb(1.0F, direction, this.progressOld, this.progress, p_155685_.getBottomCenter()); List list = p_155684_.getEntities(null, aabb); if (!list.isEmpty()) { for (Entity entity : list) { if (entity.getPistonPushReaction() != PushReaction.IGNORE) { entity.move( MoverType.SHULKER_BOX, new Vec3( (aabb.getXsize() + 0.01) * direction.getStepX(), (aabb.getYsize() + 0.01) * direction.getStepY(), (aabb.getZsize() + 0.01) * direction.getStepZ() ) ); } } } } } @Override public int getContainerSize() { return this.itemStacks.size(); } @Override public boolean triggerEvent(int p_59678_, int p_59679_) { if (p_59678_ == 1) { this.openCount = p_59679_; if (p_59679_ == 0) { this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSING; } if (p_59679_ == 1) { this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.OPENING; } return true; } else { return super.triggerEvent(p_59678_, p_59679_); } } private static void doNeighborUpdates(Level p_155688_, BlockPos p_155689_, BlockState p_155690_) { p_155690_.updateNeighbourShapes(p_155688_, p_155689_, 3); p_155688_.updateNeighborsAt(p_155689_, p_155690_.getBlock()); } @Override public void preRemoveSideEffects(BlockPos p_394542_, BlockState p_391727_) { } @Override public void startOpen(Player p_59692_) { if (!this.remove && !p_59692_.isSpectator()) { if (this.openCount < 0) { this.openCount = 0; } this.openCount++; this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount == 1) { this.level.gameEvent(p_59692_, GameEvent.CONTAINER_OPEN, this.worldPosition); this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F); } } } @Override public void stopOpen(Player p_59688_) { if (!this.remove && !p_59688_.isSpectator()) { this.openCount--; this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount <= 0) { this.level.gameEvent(p_59688_, GameEvent.CONTAINER_CLOSE, this.worldPosition); this.level.playSound(null, this.worldPosition, SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F); } } } @Override protected Component getDefaultName() { return Component.translatable("container.shulkerBox"); } @Override protected void loadAdditional(CompoundTag p_327918_, HolderLookup.Provider p_335601_) { super.loadAdditional(p_327918_, p_335601_); this.loadFromTag(p_327918_, p_335601_); } @Override protected void saveAdditional(CompoundTag p_187513_, HolderLookup.Provider p_334063_) { super.saveAdditional(p_187513_, p_334063_); if (!this.trySaveLootTable(p_187513_)) { ContainerHelper.saveAllItems(p_187513_, this.itemStacks, false, p_334063_); } } public void loadFromTag(CompoundTag p_59694_, HolderLookup.Provider p_333398_) { this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); if (!this.tryLoadLootTable(p_59694_)) { ContainerHelper.loadAllItems(p_59694_, this.itemStacks, p_333398_); } } @Override protected NonNullList getItems() { return this.itemStacks; } @Override protected void setItems(NonNullList p_59674_) { this.itemStacks = p_59674_; } @Override public int[] getSlotsForFace(Direction p_59672_) { return SLOTS; } @Override public boolean canPlaceItemThroughFace(int p_59663_, ItemStack p_59664_, @Nullable Direction p_59665_) { return !(Block.byItem(p_59664_.getItem()) instanceof ShulkerBoxBlock); } @Override public boolean canTakeItemThroughFace(int p_59682_, ItemStack p_59683_, Direction p_59684_) { return true; } public float getProgress(float p_59658_) { return Mth.lerp(p_59658_, this.progressOld, this.progress); } @Nullable public DyeColor getColor() { return this.color; } @Override protected AbstractContainerMenu createMenu(int p_59660_, Inventory p_59661_) { return new ShulkerBoxMenu(p_59660_, p_59661_, this); } public boolean isClosed() { return this.animationStatus == ShulkerBoxBlockEntity.AnimationStatus.CLOSED; } public static enum AnimationStatus { CLOSED, OPENING, OPENED, CLOSING; } }