package net.minecraft.world.level.material; import java.util.Optional; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.FluidTags; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.InsideBlockEffectType; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition.Builder; import org.jetbrains.annotations.Nullable; public abstract class LavaFluid extends FlowingFluid { public static final float MIN_LEVEL_CUTOFF = 0.44444445F; @Override public Fluid getFlowing() { return Fluids.FLOWING_LAVA; } @Override public Fluid getSource() { return Fluids.LAVA; } @Override public Item getBucket() { return Items.LAVA_BUCKET; } @Override public void animateTick(Level level, BlockPos pos, FluidState state, RandomSource random) { BlockPos blockPos = pos.above(); if (level.getBlockState(blockPos).isAir() && !level.getBlockState(blockPos).isSolidRender()) { if (random.nextInt(100) == 0) { double d = pos.getX() + random.nextDouble(); double e = pos.getY() + 1.0; double f = pos.getZ() + random.nextDouble(); level.addParticle(ParticleTypes.LAVA, d, e, f, 0.0, 0.0, 0.0); level.playLocalSound(d, e, f, SoundEvents.LAVA_POP, SoundSource.AMBIENT, 0.2F + random.nextFloat() * 0.2F, 0.9F + random.nextFloat() * 0.15F, false); } if (random.nextInt(200) == 0) { level.playLocalSound( pos.getX(), pos.getY(), pos.getZ(), SoundEvents.LAVA_AMBIENT, SoundSource.AMBIENT, 0.2F + random.nextFloat() * 0.2F, 0.9F + random.nextFloat() * 0.15F, false ); } } } @Override public void randomTick(ServerLevel level, BlockPos pos, FluidState state, RandomSource random) { if (level.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { if (level.getGameRules().getBoolean(GameRules.RULE_ALLOWFIRETICKAWAYFROMPLAYERS) || level.anyPlayerCloseEnoughForSpawning(pos)) { int i = random.nextInt(3); if (i > 0) { BlockPos blockPos = pos; for (int j = 0; j < i; j++) { blockPos = blockPos.offset(random.nextInt(3) - 1, 1, random.nextInt(3) - 1); if (!level.isLoaded(blockPos)) { return; } BlockState blockState = level.getBlockState(blockPos); if (blockState.isAir()) { if (this.hasFlammableNeighbours(level, blockPos)) { level.setBlockAndUpdate(blockPos, BaseFireBlock.getState(level, blockPos)); return; } } else if (blockState.blocksMotion()) { return; } } } else { for (int k = 0; k < 3; k++) { BlockPos blockPos2 = pos.offset(random.nextInt(3) - 1, 0, random.nextInt(3) - 1); if (!level.isLoaded(blockPos2)) { return; } if (level.isEmptyBlock(blockPos2.above()) && this.isFlammable(level, blockPos2)) { level.setBlockAndUpdate(blockPos2.above(), BaseFireBlock.getState(level, blockPos2)); } } } } } } @Override protected void entityInside(Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier) { effectApplier.apply(InsideBlockEffectType.LAVA_IGNITE); effectApplier.runAfter(InsideBlockEffectType.LAVA_IGNITE, Entity::lavaHurt); } private boolean hasFlammableNeighbours(LevelReader level, BlockPos pos) { for (Direction direction : Direction.values()) { if (this.isFlammable(level, pos.relative(direction))) { return true; } } return false; } private boolean isFlammable(LevelReader level, BlockPos pos) { return level.isInsideBuildHeight(pos.getY()) && !level.hasChunkAt(pos) ? false : level.getBlockState(pos).ignitedByLava(); } @Nullable @Override public ParticleOptions getDripParticle() { return ParticleTypes.DRIPPING_LAVA; } @Override protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { this.fizz(level, pos); } @Override public int getSlopeFindDistance(LevelReader level) { return level.dimensionType().ultraWarm() ? 4 : 2; } @Override public BlockState createLegacyBlock(FluidState state) { return Blocks.LAVA.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state)); } @Override public boolean isSame(Fluid fluid) { return fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA; } @Override public int getDropOff(LevelReader level) { return level.dimensionType().ultraWarm() ? 1 : 2; } @Override public boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) { return state.getHeight(level, pos) >= 0.44444445F && fluid.is(FluidTags.WATER); } @Override public int getTickDelay(LevelReader level) { return level.dimensionType().ultraWarm() ? 10 : 30; } @Override public int getSpreadDelay(Level level, BlockPos pos, FluidState currentState, FluidState newState) { int i = this.getTickDelay(level); if (!currentState.isEmpty() && !newState.isEmpty() && !(Boolean)currentState.getValue(FALLING) && !(Boolean)newState.getValue(FALLING) && newState.getHeight(level, pos) > currentState.getHeight(level, pos) && level.getRandom().nextInt(4) != 0) { i *= 4; } return i; } private void fizz(LevelAccessor level, BlockPos pos) { level.levelEvent(1501, pos, 0); } @Override protected boolean canConvertToSource(ServerLevel level) { return level.getGameRules().getBoolean(GameRules.RULE_LAVA_SOURCE_CONVERSION); } @Override protected void spreadTo(LevelAccessor level, BlockPos pos, BlockState blockState, Direction direction, FluidState fluidState) { if (direction == Direction.DOWN) { FluidState fluidState2 = level.getFluidState(pos); if (this.is(FluidTags.LAVA) && fluidState2.is(FluidTags.WATER)) { if (blockState.getBlock() instanceof LiquidBlock) { level.setBlock(pos, Blocks.STONE.defaultBlockState(), 3); } this.fizz(level, pos); return; } } super.spreadTo(level, pos, blockState, direction, fluidState); } @Override protected boolean isRandomlyTicking() { return true; } @Override protected float getExplosionResistance() { return 100.0F; } @Override public Optional getPickupSound() { return Optional.of(SoundEvents.BUCKET_FILL_LAVA); } public static class Flowing extends LavaFluid { @Override protected void createFluidStateDefinition(Builder builder) { super.createFluidStateDefinition(builder); builder.add(LEVEL); } @Override public int getAmount(FluidState state) { return (Integer)state.getValue(LEVEL); } @Override public boolean isSource(FluidState state) { return false; } } public static class Source extends LavaFluid { @Override public int getAmount(FluidState state) { return 8; } @Override public boolean isSource(FluidState state) { return true; } } }