454 lines
15 KiB
Java
454 lines
15 KiB
Java
package net.minecraft.world.level.block;
|
|
|
|
import com.mojang.serialization.MapCodec;
|
|
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
|
|
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.stats.Stats;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.ItemInteractionResult;
|
|
import net.minecraft.world.SimpleContainer;
|
|
import net.minecraft.world.WorldlyContainer;
|
|
import net.minecraft.world.WorldlyContainerHolder;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.ItemLike;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
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.IntegerProperty;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.level.pathfinder.PathComputationType;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class ComposterBlock extends Block implements WorldlyContainerHolder {
|
|
public static final MapCodec<ComposterBlock> CODEC = simpleCodec(ComposterBlock::new);
|
|
public static final int READY = 8;
|
|
public static final int MIN_LEVEL = 0;
|
|
public static final int MAX_LEVEL = 7;
|
|
public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_COMPOSTER;
|
|
public static final Object2FloatMap<ItemLike> COMPOSTABLES = new Object2FloatOpenHashMap<>();
|
|
private static final int AABB_SIDE_THICKNESS = 2;
|
|
private static final VoxelShape OUTER_SHAPE = Shapes.block();
|
|
private static final VoxelShape[] SHAPES = Util.make(new VoxelShape[9], voxelShapes -> {
|
|
for (int i = 0; i < 8; i++) {
|
|
voxelShapes[i] = Shapes.join(OUTER_SHAPE, Block.box(2.0, Math.max(2, 1 + i * 2), 2.0, 14.0, 16.0, 14.0), BooleanOp.ONLY_FIRST);
|
|
}
|
|
|
|
voxelShapes[8] = voxelShapes[7];
|
|
});
|
|
|
|
@Override
|
|
public MapCodec<ComposterBlock> codec() {
|
|
return CODEC;
|
|
}
|
|
|
|
public static void bootStrap() {
|
|
COMPOSTABLES.defaultReturnValue(-1.0F);
|
|
float f = 0.3F;
|
|
float g = 0.5F;
|
|
float h = 0.65F;
|
|
float i = 0.85F;
|
|
float j = 1.0F;
|
|
add(0.3F, Items.JUNGLE_LEAVES);
|
|
add(0.3F, Items.OAK_LEAVES);
|
|
add(0.3F, Items.SPRUCE_LEAVES);
|
|
add(0.3F, Items.DARK_OAK_LEAVES);
|
|
add(0.3F, Items.ACACIA_LEAVES);
|
|
add(0.3F, Items.CHERRY_LEAVES);
|
|
add(0.3F, Items.BIRCH_LEAVES);
|
|
add(0.3F, Items.AZALEA_LEAVES);
|
|
add(0.3F, Items.MANGROVE_LEAVES);
|
|
add(0.3F, Items.OAK_SAPLING);
|
|
add(0.3F, Items.SPRUCE_SAPLING);
|
|
add(0.3F, Items.BIRCH_SAPLING);
|
|
add(0.3F, Items.JUNGLE_SAPLING);
|
|
add(0.3F, Items.ACACIA_SAPLING);
|
|
add(0.3F, Items.CHERRY_SAPLING);
|
|
add(0.3F, Items.DARK_OAK_SAPLING);
|
|
add(0.3F, Items.MANGROVE_PROPAGULE);
|
|
add(0.3F, Items.BEETROOT_SEEDS);
|
|
add(0.3F, Items.DRIED_KELP);
|
|
add(0.3F, Items.SHORT_GRASS);
|
|
add(0.3F, Items.KELP);
|
|
add(0.3F, Items.MELON_SEEDS);
|
|
add(0.3F, Items.PUMPKIN_SEEDS);
|
|
add(0.3F, Items.SEAGRASS);
|
|
add(0.3F, Items.SWEET_BERRIES);
|
|
add(0.3F, Items.GLOW_BERRIES);
|
|
add(0.3F, Items.WHEAT_SEEDS);
|
|
add(0.3F, Items.MOSS_CARPET);
|
|
add(0.3F, Items.PINK_PETALS);
|
|
add(0.3F, Items.SMALL_DRIPLEAF);
|
|
add(0.3F, Items.HANGING_ROOTS);
|
|
add(0.3F, Items.MANGROVE_ROOTS);
|
|
add(0.3F, Items.TORCHFLOWER_SEEDS);
|
|
add(0.3F, Items.PITCHER_POD);
|
|
add(0.5F, Items.DRIED_KELP_BLOCK);
|
|
add(0.5F, Items.TALL_GRASS);
|
|
add(0.5F, Items.FLOWERING_AZALEA_LEAVES);
|
|
add(0.5F, Items.CACTUS);
|
|
add(0.5F, Items.SUGAR_CANE);
|
|
add(0.5F, Items.VINE);
|
|
add(0.5F, Items.NETHER_SPROUTS);
|
|
add(0.5F, Items.WEEPING_VINES);
|
|
add(0.5F, Items.TWISTING_VINES);
|
|
add(0.5F, Items.MELON_SLICE);
|
|
add(0.5F, Items.GLOW_LICHEN);
|
|
add(0.65F, Items.SEA_PICKLE);
|
|
add(0.65F, Items.LILY_PAD);
|
|
add(0.65F, Items.PUMPKIN);
|
|
add(0.65F, Items.CARVED_PUMPKIN);
|
|
add(0.65F, Items.MELON);
|
|
add(0.65F, Items.APPLE);
|
|
add(0.65F, Items.BEETROOT);
|
|
add(0.65F, Items.CARROT);
|
|
add(0.65F, Items.COCOA_BEANS);
|
|
add(0.65F, Items.POTATO);
|
|
add(0.65F, Items.WHEAT);
|
|
add(0.65F, Items.BROWN_MUSHROOM);
|
|
add(0.65F, Items.RED_MUSHROOM);
|
|
add(0.65F, Items.MUSHROOM_STEM);
|
|
add(0.65F, Items.CRIMSON_FUNGUS);
|
|
add(0.65F, Items.WARPED_FUNGUS);
|
|
add(0.65F, Items.NETHER_WART);
|
|
add(0.65F, Items.CRIMSON_ROOTS);
|
|
add(0.65F, Items.WARPED_ROOTS);
|
|
add(0.65F, Items.SHROOMLIGHT);
|
|
add(0.65F, Items.DANDELION);
|
|
add(0.65F, Items.POPPY);
|
|
add(0.65F, Items.BLUE_ORCHID);
|
|
add(0.65F, Items.ALLIUM);
|
|
add(0.65F, Items.AZURE_BLUET);
|
|
add(0.65F, Items.RED_TULIP);
|
|
add(0.65F, Items.ORANGE_TULIP);
|
|
add(0.65F, Items.WHITE_TULIP);
|
|
add(0.65F, Items.PINK_TULIP);
|
|
add(0.65F, Items.OXEYE_DAISY);
|
|
add(0.65F, Items.CORNFLOWER);
|
|
add(0.65F, Items.LILY_OF_THE_VALLEY);
|
|
add(0.65F, Items.WITHER_ROSE);
|
|
add(0.65F, Items.FERN);
|
|
add(0.65F, Items.SUNFLOWER);
|
|
add(0.65F, Items.LILAC);
|
|
add(0.65F, Items.ROSE_BUSH);
|
|
add(0.65F, Items.PEONY);
|
|
add(0.65F, Items.LARGE_FERN);
|
|
add(0.65F, Items.SPORE_BLOSSOM);
|
|
add(0.65F, Items.AZALEA);
|
|
add(0.65F, Items.MOSS_BLOCK);
|
|
add(0.65F, Items.BIG_DRIPLEAF);
|
|
add(0.85F, Items.HAY_BLOCK);
|
|
add(0.85F, Items.BROWN_MUSHROOM_BLOCK);
|
|
add(0.85F, Items.RED_MUSHROOM_BLOCK);
|
|
add(0.85F, Items.NETHER_WART_BLOCK);
|
|
add(0.85F, Items.WARPED_WART_BLOCK);
|
|
add(0.85F, Items.FLOWERING_AZALEA);
|
|
add(0.85F, Items.BREAD);
|
|
add(0.85F, Items.BAKED_POTATO);
|
|
add(0.85F, Items.COOKIE);
|
|
add(0.85F, Items.TORCHFLOWER);
|
|
add(0.85F, Items.PITCHER_PLANT);
|
|
add(1.0F, Items.CAKE);
|
|
add(1.0F, Items.PUMPKIN_PIE);
|
|
}
|
|
|
|
private static void add(float chance, ItemLike item) {
|
|
COMPOSTABLES.put(item.asItem(), chance);
|
|
}
|
|
|
|
public ComposterBlock(BlockBehaviour.Properties properties) {
|
|
super(properties);
|
|
this.registerDefaultState(this.stateDefinition.any().setValue(LEVEL, 0));
|
|
}
|
|
|
|
public static void handleFill(Level level, BlockPos pos, boolean success) {
|
|
BlockState blockState = level.getBlockState(pos);
|
|
level.playLocalSound(pos, success ? SoundEvents.COMPOSTER_FILL_SUCCESS : SoundEvents.COMPOSTER_FILL, SoundSource.BLOCKS, 1.0F, 1.0F, false);
|
|
double d = blockState.getShape(level, pos).max(Direction.Axis.Y, 0.5, 0.5) + 0.03125;
|
|
double e = 0.13125F;
|
|
double f = 0.7375F;
|
|
RandomSource randomSource = level.getRandom();
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
double g = randomSource.nextGaussian() * 0.02;
|
|
double h = randomSource.nextGaussian() * 0.02;
|
|
double j = randomSource.nextGaussian() * 0.02;
|
|
level.addParticle(
|
|
ParticleTypes.COMPOSTER,
|
|
pos.getX() + 0.13125F + 0.7375F * randomSource.nextFloat(),
|
|
pos.getY() + d + randomSource.nextFloat() * (1.0 - d),
|
|
pos.getZ() + 0.13125F + 0.7375F * randomSource.nextFloat(),
|
|
g,
|
|
h,
|
|
j
|
|
);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
|
return SHAPES[state.getValue(LEVEL)];
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getInteractionShape(BlockState state, BlockGetter level, BlockPos pos) {
|
|
return OUTER_SHAPE;
|
|
}
|
|
|
|
@Override
|
|
protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
|
return SHAPES[0];
|
|
}
|
|
|
|
@Override
|
|
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
|
|
if ((Integer)state.getValue(LEVEL) == 7) {
|
|
level.scheduleTick(pos, state.getBlock(), 20);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected ItemInteractionResult useItemOn(
|
|
ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult
|
|
) {
|
|
int i = (Integer)state.getValue(LEVEL);
|
|
if (i < 8 && COMPOSTABLES.containsKey(stack.getItem())) {
|
|
if (i < 7 && !level.isClientSide) {
|
|
BlockState blockState = addItem(player, state, level, pos, stack);
|
|
level.levelEvent(1500, pos, state != blockState ? 1 : 0);
|
|
player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
|
|
stack.consume(1, player);
|
|
}
|
|
|
|
return ItemInteractionResult.sidedSuccess(level.isClientSide);
|
|
} else {
|
|
return super.useItemOn(stack, state, level, pos, player, hand, hitResult);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
|
|
int i = (Integer)state.getValue(LEVEL);
|
|
if (i == 8) {
|
|
extractProduce(player, state, level, pos);
|
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
|
} else {
|
|
return InteractionResult.PASS;
|
|
}
|
|
}
|
|
|
|
public static BlockState insertItem(Entity entity, BlockState state, ServerLevel level, ItemStack stack, BlockPos pos) {
|
|
int i = (Integer)state.getValue(LEVEL);
|
|
if (i < 7 && COMPOSTABLES.containsKey(stack.getItem())) {
|
|
BlockState blockState = addItem(entity, state, level, pos, stack);
|
|
stack.shrink(1);
|
|
return blockState;
|
|
} else {
|
|
return state;
|
|
}
|
|
}
|
|
|
|
public static BlockState extractProduce(Entity entity, BlockState state, Level level, BlockPos pos) {
|
|
if (!level.isClientSide) {
|
|
Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1.01, 0.5).offsetRandom(level.random, 0.7F);
|
|
ItemEntity itemEntity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), new ItemStack(Items.BONE_MEAL));
|
|
itemEntity.setDefaultPickUpDelay();
|
|
level.addFreshEntity(itemEntity);
|
|
}
|
|
|
|
BlockState blockState = empty(entity, state, level, pos);
|
|
level.playSound(null, pos, SoundEvents.COMPOSTER_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
return blockState;
|
|
}
|
|
|
|
static BlockState empty(@Nullable Entity entity, BlockState state, LevelAccessor level, BlockPos pos) {
|
|
BlockState blockState = state.setValue(LEVEL, 0);
|
|
level.setBlock(pos, blockState, 3);
|
|
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, blockState));
|
|
return blockState;
|
|
}
|
|
|
|
static BlockState addItem(@Nullable Entity entity, BlockState state, LevelAccessor level, BlockPos pos, ItemStack stack) {
|
|
int i = (Integer)state.getValue(LEVEL);
|
|
float f = COMPOSTABLES.getFloat(stack.getItem());
|
|
if ((i != 0 || !(f > 0.0F)) && !(level.getRandom().nextDouble() < f)) {
|
|
return state;
|
|
} else {
|
|
int j = i + 1;
|
|
BlockState blockState = state.setValue(LEVEL, j);
|
|
level.setBlock(pos, blockState, 3);
|
|
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, blockState));
|
|
if (j == 7) {
|
|
level.scheduleTick(pos, state.getBlock(), 20);
|
|
}
|
|
|
|
return blockState;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
|
if ((Integer)state.getValue(LEVEL) == 7) {
|
|
level.setBlock(pos, state.cycle(LEVEL), 3);
|
|
level.playSound(null, pos, SoundEvents.COMPOSTER_READY, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean hasAnalogOutputSignal(BlockState state) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) {
|
|
return (Integer)state.getValue(LEVEL);
|
|
}
|
|
|
|
@Override
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(LEVEL);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public WorldlyContainer getContainer(BlockState state, LevelAccessor level, BlockPos pos) {
|
|
int i = (Integer)state.getValue(LEVEL);
|
|
if (i == 8) {
|
|
return new ComposterBlock.OutputContainer(state, level, pos, new ItemStack(Items.BONE_MEAL));
|
|
} else {
|
|
return (WorldlyContainer)(i < 7 ? new ComposterBlock.InputContainer(state, level, pos) : new ComposterBlock.EmptyContainer());
|
|
}
|
|
}
|
|
|
|
static class EmptyContainer extends SimpleContainer implements WorldlyContainer {
|
|
public EmptyContainer() {
|
|
super(0);
|
|
}
|
|
|
|
@Override
|
|
public int[] getSlotsForFace(Direction side) {
|
|
return new int[0];
|
|
}
|
|
|
|
@Override
|
|
public boolean canPlaceItemThroughFace(int index, ItemStack itemStack, @Nullable Direction direction) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static class InputContainer extends SimpleContainer implements WorldlyContainer {
|
|
private final BlockState state;
|
|
private final LevelAccessor level;
|
|
private final BlockPos pos;
|
|
private boolean changed;
|
|
|
|
public InputContainer(BlockState state, LevelAccessor level, BlockPos pos) {
|
|
super(1);
|
|
this.state = state;
|
|
this.level = level;
|
|
this.pos = pos;
|
|
}
|
|
|
|
@Override
|
|
public int getMaxStackSize() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public int[] getSlotsForFace(Direction side) {
|
|
return side == Direction.UP ? new int[]{0} : new int[0];
|
|
}
|
|
|
|
@Override
|
|
public boolean canPlaceItemThroughFace(int index, ItemStack itemStack, @Nullable Direction direction) {
|
|
return !this.changed && direction == Direction.UP && ComposterBlock.COMPOSTABLES.containsKey(itemStack.getItem());
|
|
}
|
|
|
|
@Override
|
|
public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void setChanged() {
|
|
ItemStack itemStack = this.getItem(0);
|
|
if (!itemStack.isEmpty()) {
|
|
this.changed = true;
|
|
BlockState blockState = ComposterBlock.addItem(null, this.state, this.level, this.pos, itemStack);
|
|
this.level.levelEvent(1500, this.pos, blockState != this.state ? 1 : 0);
|
|
this.removeItemNoUpdate(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static class OutputContainer extends SimpleContainer implements WorldlyContainer {
|
|
private final BlockState state;
|
|
private final LevelAccessor level;
|
|
private final BlockPos pos;
|
|
private boolean changed;
|
|
|
|
public OutputContainer(BlockState state, LevelAccessor level, BlockPos pos, ItemStack stack) {
|
|
super(stack);
|
|
this.state = state;
|
|
this.level = level;
|
|
this.pos = pos;
|
|
}
|
|
|
|
@Override
|
|
public int getMaxStackSize() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public int[] getSlotsForFace(Direction side) {
|
|
return side == Direction.DOWN ? new int[]{0} : new int[0];
|
|
}
|
|
|
|
@Override
|
|
public boolean canPlaceItemThroughFace(int index, ItemStack itemStack, @Nullable Direction direction) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) {
|
|
return !this.changed && direction == Direction.DOWN && stack.is(Items.BONE_MEAL);
|
|
}
|
|
|
|
@Override
|
|
public void setChanged() {
|
|
ComposterBlock.empty(null, this.state, this.level, this.pos);
|
|
this.changed = true;
|
|
}
|
|
}
|
|
}
|