package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.cauldron.CauldronInteraction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.InsideBlockEffectType; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; 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.material.Fluid; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; public class LayeredCauldronBlock extends AbstractCauldronBlock { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( Biome.Precipitation.CODEC.fieldOf("precipitation").forGetter(layeredCauldronBlock -> layeredCauldronBlock.precipitationType), CauldronInteraction.CODEC.fieldOf("interactions").forGetter(layeredCauldronBlock -> layeredCauldronBlock.interactions), propertiesCodec() ) .apply(instance, LayeredCauldronBlock::new) ); public static final int MIN_FILL_LEVEL = 1; public static final int MAX_FILL_LEVEL = 3; public static final IntegerProperty LEVEL = BlockStateProperties.LEVEL_CAULDRON; private static final int BASE_CONTENT_HEIGHT = 6; private static final double HEIGHT_PER_LEVEL = 3.0; private static final VoxelShape[] FILLED_SHAPES = Util.make( () -> Block.boxes(2, i -> Shapes.or(AbstractCauldronBlock.SHAPE, Block.column(12.0, 4.0, getPixelContentHeight(i + 1)))) ); private final Biome.Precipitation precipitationType; @Override public MapCodec codec() { return CODEC; } public LayeredCauldronBlock(Biome.Precipitation precipitationType, CauldronInteraction.InteractionMap interactions, BlockBehaviour.Properties properties) { super(properties, interactions); this.precipitationType = precipitationType; this.registerDefaultState(this.stateDefinition.any().setValue(LEVEL, 1)); } @Override public boolean isFull(BlockState state) { return (Integer)state.getValue(LEVEL) == 3; } @Override protected boolean canReceiveStalactiteDrip(Fluid fluid) { return fluid == Fluids.WATER && this.precipitationType == Biome.Precipitation.RAIN; } @Override protected double getContentHeight(BlockState state) { return getPixelContentHeight((Integer)state.getValue(LEVEL)) / 16.0; } private static double getPixelContentHeight(int level) { return 6.0 + level * 3.0; } @Override protected VoxelShape getEntityInsideCollisionShape(BlockState state, BlockGetter level, BlockPos pos, Entity entity) { return FILLED_SHAPES[state.getValue(LEVEL) - 1]; } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier) { if (level instanceof ServerLevel serverLevel) { BlockPos blockPos = pos.immutable(); effectApplier.runBefore(InsideBlockEffectType.EXTINGUISH, entityx -> { if (entityx.isOnFire() && entityx.mayInteract(serverLevel, blockPos)) { this.handleEntityOnFireInside(state, level, blockPos); } }); } effectApplier.apply(InsideBlockEffectType.EXTINGUISH); } private void handleEntityOnFireInside(BlockState state, Level level, BlockPos pos) { if (this.precipitationType == Biome.Precipitation.SNOW) { lowerFillLevel(Blocks.WATER_CAULDRON.defaultBlockState().setValue(LEVEL, (Integer)state.getValue(LEVEL)), level, pos); } else { lowerFillLevel(state, level, pos); } } public static void lowerFillLevel(BlockState state, Level level, BlockPos pos) { int i = (Integer)state.getValue(LEVEL) - 1; BlockState blockState = i == 0 ? Blocks.CAULDRON.defaultBlockState() : state.setValue(LEVEL, i); level.setBlockAndUpdate(pos, blockState); level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); } @Override public void handlePrecipitation(BlockState state, Level level, BlockPos pos, Biome.Precipitation precipitation) { if (CauldronBlock.shouldHandlePrecipitation(level, precipitation) && (Integer)state.getValue(LEVEL) != 3 && precipitation == this.precipitationType) { BlockState blockState = state.cycle(LEVEL); level.setBlockAndUpdate(pos, blockState); level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); } } @Override protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) { return (Integer)state.getValue(LEVEL); } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(LEVEL); } @Override protected void receiveStalactiteDrip(BlockState state, Level level, BlockPos pos, Fluid fluid) { if (!this.isFull(state)) { BlockState blockState = state.setValue(LEVEL, (Integer)state.getValue(LEVEL) + 1); level.setBlockAndUpdate(pos, blockState); level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState)); level.levelEvent(1047, pos, 0); } } }