package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import java.util.Optional; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.Plane; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.portal.PortalShape; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; public abstract class BaseFireBlock extends Block { private static final int SECONDS_ON_FIRE = 8; private static final int MIN_FIRE_TICKS_TO_ADD = 1; private static final int MAX_FIRE_TICKS_TO_ADD = 3; private final float fireDamage; protected static final float AABB_OFFSET = 1.0F; protected static final VoxelShape DOWN_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 1.0, 16.0); public BaseFireBlock(BlockBehaviour.Properties properties, float fireDamage) { super(properties); this.fireDamage = fireDamage; } @Override protected abstract MapCodec codec(); @Override public BlockState getStateForPlacement(BlockPlaceContext context) { return getState(context.getLevel(), context.getClickedPos()); } public static BlockState getState(BlockGetter reader, BlockPos pos) { BlockPos blockPos = pos.below(); BlockState blockState = reader.getBlockState(blockPos); return SoulFireBlock.canSurviveOnBlock(blockState) ? Blocks.SOUL_FIRE.defaultBlockState() : ((FireBlock)Blocks.FIRE).getStateForPlacement(reader, pos); } @Override protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { return DOWN_AABB; } @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { if (random.nextInt(24) == 0) { level.playLocalSound( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, SoundEvents.FIRE_AMBIENT, SoundSource.BLOCKS, 1.0F + random.nextFloat(), random.nextFloat() * 0.7F + 0.3F, false ); } BlockPos blockPos = pos.below(); BlockState blockState = level.getBlockState(blockPos); if (!this.canBurn(blockState) && !blockState.isFaceSturdy(level, blockPos, Direction.UP)) { if (this.canBurn(level.getBlockState(pos.west()))) { for (int i = 0; i < 2; i++) { double d = pos.getX() + random.nextDouble() * 0.1F; double e = pos.getY() + random.nextDouble(); double f = pos.getZ() + random.nextDouble(); level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } if (this.canBurn(level.getBlockState(pos.east()))) { for (int i = 0; i < 2; i++) { double d = pos.getX() + 1 - random.nextDouble() * 0.1F; double e = pos.getY() + random.nextDouble(); double f = pos.getZ() + random.nextDouble(); level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } if (this.canBurn(level.getBlockState(pos.north()))) { for (int i = 0; i < 2; i++) { double d = pos.getX() + random.nextDouble(); double e = pos.getY() + random.nextDouble(); double f = pos.getZ() + random.nextDouble() * 0.1F; level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } if (this.canBurn(level.getBlockState(pos.south()))) { for (int i = 0; i < 2; i++) { double d = pos.getX() + random.nextDouble(); double e = pos.getY() + random.nextDouble(); double f = pos.getZ() + 1 - random.nextDouble() * 0.1F; level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } if (this.canBurn(level.getBlockState(pos.above()))) { for (int i = 0; i < 2; i++) { double d = pos.getX() + random.nextDouble(); double e = pos.getY() + 1 - random.nextDouble() * 0.1F; double f = pos.getZ() + random.nextDouble(); level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } } else { for (int i = 0; i < 3; i++) { double d = pos.getX() + random.nextDouble(); double e = pos.getY() + random.nextDouble() * 0.5 + 0.5; double f = pos.getZ() + random.nextDouble(); level.addParticle(ParticleTypes.LARGE_SMOKE, d, e, f, 0.0, 0.0, 0.0); } } } protected abstract boolean canBurn(BlockState state); @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { if (!entity.fireImmune()) { if (entity.getRemainingFireTicks() < 0) { entity.setRemainingFireTicks(entity.getRemainingFireTicks() + 1); } else if (entity instanceof ServerPlayer) { int i = level.getRandom().nextInt(1, 3); entity.setRemainingFireTicks(entity.getRemainingFireTicks() + i); } if (entity.getRemainingFireTicks() >= 0) { entity.igniteForSeconds(8.0F); } } entity.hurt(level.damageSources().inFire(), this.fireDamage); super.entityInside(state, level, pos, entity); } @Override protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { if (!oldState.is(state.getBlock())) { if (inPortalDimension(level)) { Optional optional = PortalShape.findEmptyPortalShape(level, pos, Axis.X); if (optional.isPresent()) { ((PortalShape)optional.get()).createPortalBlocks(level); return; } } if (!state.canSurvive(level, pos)) { level.removeBlock(pos, false); } } } private static boolean inPortalDimension(Level level) { return level.dimension() == Level.OVERWORLD || level.dimension() == Level.NETHER; } @Override protected void spawnDestroyParticles(Level level, Player player, BlockPos pos, BlockState state) { } @Override public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { if (!level.isClientSide()) { level.levelEvent(null, 1009, pos, 0); } return super.playerWillDestroy(level, pos, state, player); } public static boolean canBePlacedAt(Level level, BlockPos pos, Direction direction) { BlockState blockState = level.getBlockState(pos); return !blockState.isAir() ? false : getState(level, pos).canSurvive(level, pos) || isPortal(level, pos, direction); } private static boolean isPortal(Level level, BlockPos pos, Direction direction) { if (!inPortalDimension(level)) { return false; } else { BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); boolean bl = false; for (Direction direction2 : Direction.values()) { if (level.getBlockState(mutableBlockPos.set(pos).move(direction2)).is(Blocks.OBSIDIAN)) { bl = true; break; } } if (!bl) { return false; } else { Axis axis = direction.getAxis().isHorizontal() ? direction.getCounterClockWise().getAxis() : Plane.HORIZONTAL.getRandomAxis(level.random); return PortalShape.findEmptyPortalShape(level, pos, axis).isPresent(); } } } }