minecraft-src/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
2025-07-04 03:15:13 +03:00

263 lines
8.3 KiB
Java

package net.minecraft.world.level.block.entity;
import java.util.List;
import java.util.stream.IntStream;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.core.HolderLookup.Provider;
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;
import org.jetbrains.annotations.Nullable;
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<ItemStack> 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 color, BlockPos pos, BlockState blockState) {
super(BlockEntityType.SHULKER_BOX, pos, blockState);
this.color = color;
}
public ShulkerBoxBlockEntity(BlockPos pos, BlockState blockState) {
super(BlockEntityType.SHULKER_BOX, pos, blockState);
this.color = blockState.getBlock() instanceof ShulkerBoxBlock shulkerBoxBlock ? shulkerBoxBlock.getColor() : null;
}
public static void tick(Level level, BlockPos pos, BlockState state, ShulkerBoxBlockEntity blockEntity) {
blockEntity.updateAnimation(level, pos, state);
}
private void updateAnimation(Level level, BlockPos pos, BlockState state) {
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(level, pos, state);
}
if (this.progress >= 1.0F) {
this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.OPENED;
this.progress = 1.0F;
doNeighborUpdates(level, pos, state);
}
this.moveCollidedEntities(level, pos, state);
break;
case OPENED:
this.progress = 1.0F;
break;
case CLOSING:
this.progress -= 0.1F;
if (this.progressOld == 1.0F) {
doNeighborUpdates(level, pos, state);
}
if (this.progress <= 0.0F) {
this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSED;
this.progress = 0.0F;
doNeighborUpdates(level, pos, state);
}
}
}
public ShulkerBoxBlockEntity.AnimationStatus getAnimationStatus() {
return this.animationStatus;
}
public AABB getBoundingBox(BlockState state) {
Vec3 vec3 = new Vec3(0.5, 0.0, 0.5);
return Shulker.getProgressAabb(1.0F, state.getValue(ShulkerBoxBlock.FACING), 0.5F * this.getProgress(1.0F), vec3);
}
private void moveCollidedEntities(Level level, BlockPos pos, BlockState state) {
if (state.getBlock() instanceof ShulkerBoxBlock) {
Direction direction = state.getValue(ShulkerBoxBlock.FACING);
AABB aABB = Shulker.getProgressDeltaAabb(1.0F, direction, this.progressOld, this.progress, pos.getBottomCenter());
List<Entity> list = level.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 id, int type) {
if (id == 1) {
this.openCount = type;
if (type == 0) {
this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSING;
}
if (type == 1) {
this.animationStatus = ShulkerBoxBlockEntity.AnimationStatus.OPENING;
}
return true;
} else {
return super.triggerEvent(id, type);
}
}
private static void doNeighborUpdates(Level level, BlockPos pos, BlockState state) {
state.updateNeighbourShapes(level, pos, 3);
level.updateNeighborsAt(pos, state.getBlock());
}
@Override
public void startOpen(Player player) {
if (!this.remove && !player.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(player, 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 player) {
if (!this.remove && !player.isSpectator()) {
this.openCount--;
this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
if (this.openCount <= 0) {
this.level.gameEvent(player, 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 tag, Provider registries) {
super.loadAdditional(tag, registries);
this.loadFromTag(tag, registries);
}
@Override
protected void saveAdditional(CompoundTag tag, Provider registries) {
super.saveAdditional(tag, registries);
if (!this.trySaveLootTable(tag)) {
ContainerHelper.saveAllItems(tag, this.itemStacks, false, registries);
}
}
public void loadFromTag(CompoundTag tag, Provider levelRegistry) {
this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
if (!this.tryLoadLootTable(tag) && tag.contains("Items", 9)) {
ContainerHelper.loadAllItems(tag, this.itemStacks, levelRegistry);
}
}
@Override
protected NonNullList<ItemStack> getItems() {
return this.itemStacks;
}
@Override
protected void setItems(NonNullList<ItemStack> items) {
this.itemStacks = items;
}
@Override
public int[] getSlotsForFace(Direction side) {
return SLOTS;
}
@Override
public boolean canPlaceItemThroughFace(int index, ItemStack itemStack, @Nullable Direction direction) {
return !(Block.byItem(itemStack.getItem()) instanceof ShulkerBoxBlock);
}
@Override
public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) {
return true;
}
public float getProgress(float partialTicks) {
return Mth.lerp(partialTicks, this.progressOld, this.progress);
}
@Nullable
public DyeColor getColor() {
return this.color;
}
@Override
protected AbstractContainerMenu createMenu(int containerId, Inventory inventory) {
return new ShulkerBoxMenu(containerId, inventory, this);
}
public boolean isClosed() {
return this.animationStatus == ShulkerBoxBlockEntity.AnimationStatus.CLOSED;
}
public static enum AnimationStatus {
CLOSED,
OPENING,
OPENED,
CLOSING;
}
}