215 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.block;
 | |
| 
 | |
| import com.mojang.serialization.MapCodec;
 | |
| import java.util.List;
 | |
| import java.util.Optional;
 | |
| import java.util.OptionalInt;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Direction;
 | |
| import net.minecraft.server.level.ServerLevel;
 | |
| import net.minecraft.sounds.SoundEvent;
 | |
| import net.minecraft.sounds.SoundEvents;
 | |
| import net.minecraft.sounds.SoundSource;
 | |
| import net.minecraft.stats.Stats;
 | |
| import net.minecraft.tags.ItemTags;
 | |
| import net.minecraft.world.Containers;
 | |
| import net.minecraft.world.InteractionHand;
 | |
| import net.minecraft.world.InteractionResult;
 | |
| import net.minecraft.world.entity.player.Player;
 | |
| import net.minecraft.world.item.ItemStack;
 | |
| import net.minecraft.world.item.Items;
 | |
| import net.minecraft.world.item.context.BlockPlaceContext;
 | |
| import net.minecraft.world.level.Level;
 | |
| import net.minecraft.world.level.block.entity.BlockEntity;
 | |
| import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
 | |
| import net.minecraft.world.level.block.state.BlockBehaviour;
 | |
| 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.Property;
 | |
| import net.minecraft.world.level.gameevent.GameEvent;
 | |
| import net.minecraft.world.phys.BlockHitResult;
 | |
| import net.minecraft.world.phys.Vec2;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class ChiseledBookShelfBlock extends BaseEntityBlock {
 | |
| 	public static final MapCodec<ChiseledBookShelfBlock> CODEC = simpleCodec(ChiseledBookShelfBlock::new);
 | |
| 	private static final int MAX_BOOKS_IN_STORAGE = 6;
 | |
| 	public static final int BOOKS_PER_ROW = 3;
 | |
| 	public static final List<BooleanProperty> SLOT_OCCUPIED_PROPERTIES = List.of(
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_0_OCCUPIED,
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_1_OCCUPIED,
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_2_OCCUPIED,
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_3_OCCUPIED,
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_4_OCCUPIED,
 | |
| 		BlockStateProperties.CHISELED_BOOKSHELF_SLOT_5_OCCUPIED
 | |
| 	);
 | |
| 
 | |
| 	@Override
 | |
| 	public MapCodec<ChiseledBookShelfBlock> codec() {
 | |
| 		return CODEC;
 | |
| 	}
 | |
| 
 | |
| 	public ChiseledBookShelfBlock(BlockBehaviour.Properties properties) {
 | |
| 		super(properties);
 | |
| 		BlockState blockState = this.stateDefinition.any().setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH);
 | |
| 
 | |
| 		for (BooleanProperty booleanProperty : SLOT_OCCUPIED_PROPERTIES) {
 | |
| 			blockState = blockState.setValue(booleanProperty, false);
 | |
| 		}
 | |
| 
 | |
| 		this.registerDefaultState(blockState);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected InteractionResult useItemOn(
 | |
| 		ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult
 | |
| 	) {
 | |
| 		if (level.getBlockEntity(pos) instanceof ChiseledBookShelfBlockEntity chiseledBookShelfBlockEntity) {
 | |
| 			if (!stack.is(ItemTags.BOOKSHELF_BOOKS)) {
 | |
| 				return InteractionResult.TRY_WITH_EMPTY_HAND;
 | |
| 			} else {
 | |
| 				OptionalInt optionalInt = this.getHitSlot(hitResult, state);
 | |
| 				if (optionalInt.isEmpty()) {
 | |
| 					return InteractionResult.PASS;
 | |
| 				} else if ((Boolean)state.getValue((Property)SLOT_OCCUPIED_PROPERTIES.get(optionalInt.getAsInt()))) {
 | |
| 					return InteractionResult.TRY_WITH_EMPTY_HAND;
 | |
| 				} else {
 | |
| 					addBook(level, pos, player, chiseledBookShelfBlockEntity, stack, optionalInt.getAsInt());
 | |
| 					return InteractionResult.SUCCESS;
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			return InteractionResult.PASS;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
 | |
| 		if (level.getBlockEntity(pos) instanceof ChiseledBookShelfBlockEntity chiseledBookShelfBlockEntity) {
 | |
| 			OptionalInt optionalInt = this.getHitSlot(hitResult, state);
 | |
| 			if (optionalInt.isEmpty()) {
 | |
| 				return InteractionResult.PASS;
 | |
| 			} else if (!(Boolean)state.getValue((Property)SLOT_OCCUPIED_PROPERTIES.get(optionalInt.getAsInt()))) {
 | |
| 				return InteractionResult.CONSUME;
 | |
| 			} else {
 | |
| 				removeBook(level, pos, player, chiseledBookShelfBlockEntity, optionalInt.getAsInt());
 | |
| 				return InteractionResult.SUCCESS;
 | |
| 			}
 | |
| 		} else {
 | |
| 			return InteractionResult.PASS;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private OptionalInt getHitSlot(BlockHitResult hitResult, BlockState state) {
 | |
| 		return (OptionalInt)getRelativeHitCoordinatesForBlockFace(hitResult, state.getValue(HorizontalDirectionalBlock.FACING)).map(vec2 -> {
 | |
| 			int i = vec2.y >= 0.5F ? 0 : 1;
 | |
| 			int j = getSection(vec2.x);
 | |
| 			return OptionalInt.of(j + i * 3);
 | |
| 		}).orElseGet(OptionalInt::empty);
 | |
| 	}
 | |
| 
 | |
| 	private static Optional<Vec2> getRelativeHitCoordinatesForBlockFace(BlockHitResult hitResult, Direction face) {
 | |
| 		Direction direction = hitResult.getDirection();
 | |
| 		if (face != direction) {
 | |
| 			return Optional.empty();
 | |
| 		} else {
 | |
| 			BlockPos blockPos = hitResult.getBlockPos().relative(direction);
 | |
| 			Vec3 vec3 = hitResult.getLocation().subtract(blockPos.getX(), blockPos.getY(), blockPos.getZ());
 | |
| 			double d = vec3.x();
 | |
| 			double e = vec3.y();
 | |
| 			double f = vec3.z();
 | |
| 
 | |
| 			return switch (direction) {
 | |
| 				case NORTH -> Optional.of(new Vec2((float)(1.0 - d), (float)e));
 | |
| 				case SOUTH -> Optional.of(new Vec2((float)d, (float)e));
 | |
| 				case WEST -> Optional.of(new Vec2((float)f, (float)e));
 | |
| 				case EAST -> Optional.of(new Vec2((float)(1.0 - f), (float)e));
 | |
| 				case DOWN, UP -> Optional.empty();
 | |
| 			};
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static int getSection(float x) {
 | |
| 		float f = 0.0625F;
 | |
| 		float g = 0.375F;
 | |
| 		if (x < 0.375F) {
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			float h = 0.6875F;
 | |
| 			return x < 0.6875F ? 1 : 2;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void addBook(Level level, BlockPos pos, Player player, ChiseledBookShelfBlockEntity blockEntity, ItemStack bookStack, int slot) {
 | |
| 		if (!level.isClientSide) {
 | |
| 			player.awardStat(Stats.ITEM_USED.get(bookStack.getItem()));
 | |
| 			SoundEvent soundEvent = bookStack.is(Items.ENCHANTED_BOOK) ? SoundEvents.CHISELED_BOOKSHELF_INSERT_ENCHANTED : SoundEvents.CHISELED_BOOKSHELF_INSERT;
 | |
| 			blockEntity.setItem(slot, bookStack.consumeAndReturn(1, player));
 | |
| 			level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void removeBook(Level level, BlockPos pos, Player player, ChiseledBookShelfBlockEntity blockEntity, int slot) {
 | |
| 		if (!level.isClientSide) {
 | |
| 			ItemStack itemStack = blockEntity.removeItem(slot, 1);
 | |
| 			SoundEvent soundEvent = itemStack.is(Items.ENCHANTED_BOOK) ? SoundEvents.CHISELED_BOOKSHELF_PICKUP_ENCHANTED : SoundEvents.CHISELED_BOOKSHELF_PICKUP;
 | |
| 			level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F);
 | |
| 			if (!player.getInventory().add(itemStack)) {
 | |
| 				player.drop(itemStack, false);
 | |
| 			}
 | |
| 
 | |
| 			level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
 | |
| 		return new ChiseledBookShelfBlockEntity(pos, state);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
 | |
| 		builder.add(HorizontalDirectionalBlock.FACING);
 | |
| 		SLOT_OCCUPIED_PROPERTIES.forEach(property -> builder.add(property));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
 | |
| 		Containers.updateNeighboursAfterDestroy(state, level, pos);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public BlockState getStateForPlacement(BlockPlaceContext context) {
 | |
| 		return this.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, context.getHorizontalDirection().getOpposite());
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public BlockState rotate(BlockState state, Rotation rotation) {
 | |
| 		return state.setValue(HorizontalDirectionalBlock.FACING, rotation.rotate(state.getValue(HorizontalDirectionalBlock.FACING)));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public BlockState mirror(BlockState state, Mirror mirror) {
 | |
| 		return state.rotate(mirror.getRotation(state.getValue(HorizontalDirectionalBlock.FACING)));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected boolean hasAnalogOutputSignal(BlockState state) {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) {
 | |
| 		if (level.isClientSide()) {
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			return level.getBlockEntity(pos) instanceof ChiseledBookShelfBlockEntity chiseledBookShelfBlockEntity
 | |
| 				? chiseledBookShelfBlockEntity.getLastInteractedSlot() + 1
 | |
| 				: 0;
 | |
| 		}
 | |
| 	}
 | |
| }
 |