/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.datafixers.kinds.App * com.mojang.datafixers.kinds.Applicative * com.mojang.serialization.Codec * com.mojang.serialization.MapCodec * com.mojang.serialization.codecs.RecordCodecBuilder * org.jspecify.annotations.Nullable */ package net.minecraft.world.level.block; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.Applicative; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.SimpleParticleType; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stats; import net.minecraft.tags.BlockTags; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.RecipePropertySet; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SimpleWaterloggedBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.CampfireBlockEntity; 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.EnumProperty; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; 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.jspecify.annotations.Nullable; public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedBlock { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)Codec.BOOL.fieldOf("spawn_particles").forGetter(b -> b.spawnParticles), (App)Codec.intRange((int)0, (int)1000).fieldOf("fire_damage").forGetter(b -> b.fireDamage), CampfireBlock.propertiesCodec()).apply((Applicative)i, CampfireBlock::new)); public static final BooleanProperty LIT = BlockStateProperties.LIT; public static final BooleanProperty SIGNAL_FIRE = BlockStateProperties.SIGNAL_FIRE; public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; public static final EnumProperty FACING = BlockStateProperties.HORIZONTAL_FACING; private static final VoxelShape SHAPE = Block.column(16.0, 0.0, 7.0); private static final VoxelShape SHAPE_VIRTUAL_POST = Block.column(4.0, 0.0, 16.0); private static final int SMOKE_DISTANCE = 5; private final boolean spawnParticles; private final int fireDamage; public MapCodec codec() { return CODEC; } public CampfireBlock(boolean spawnParticles, int fireDamage, BlockBehaviour.Properties properties) { super(properties); this.spawnParticles = spawnParticles; this.fireDamage = fireDamage; this.registerDefaultState((BlockState)((BlockState)((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(LIT, true)).setValue(SIGNAL_FIRE, false)).setValue(WATERLOGGED, false)).setValue(FACING, Direction.NORTH)); } @Override protected InteractionResult useItemOn(ItemStack itemStack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { BlockEntity blockEntity = level.getBlockEntity(pos); if (blockEntity instanceof CampfireBlockEntity) { CampfireBlockEntity campfire = (CampfireBlockEntity)blockEntity; ItemStack itemInHand = player.getItemInHand(hand); if (level.recipeAccess().propertySet(RecipePropertySet.CAMPFIRE_INPUT).test(itemInHand)) { ServerLevel serverLevel; if (level instanceof ServerLevel && campfire.placeFood(serverLevel = (ServerLevel)level, player, itemInHand)) { player.awardStat(Stats.INTERACT_WITH_CAMPFIRE); return InteractionResult.SUCCESS_SERVER; } return InteractionResult.CONSUME; } } return InteractionResult.TRY_WITH_EMPTY_HAND; } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier, boolean isPrecise) { if (state.getValue(LIT).booleanValue() && entity instanceof LivingEntity) { entity.hurt(level.damageSources().campfire(), this.fireDamage); } super.entityInside(state, level, pos, entity, effectApplier, isPrecise); } @Override public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) { BlockPos pos; Level level = context.getLevel(); boolean replacedWater = level.getFluidState(pos = context.getClickedPos()).getType() == Fluids.WATER; return (BlockState)((BlockState)((BlockState)((BlockState)this.defaultBlockState().setValue(WATERLOGGED, replacedWater)).setValue(SIGNAL_FIRE, this.isSmokeSource(level.getBlockState(pos.below())))).setValue(LIT, !replacedWater)).setValue(FACING, context.getHorizontalDirection()); } @Override protected BlockState updateShape(BlockState state, LevelReader level, ScheduledTickAccess ticks, BlockPos pos, Direction directionToNeighbour, BlockPos neighbourPos, BlockState neighbourState, RandomSource random) { if (state.getValue(WATERLOGGED).booleanValue()) { ticks.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); } if (directionToNeighbour == Direction.DOWN) { return (BlockState)state.setValue(SIGNAL_FIRE, this.isSmokeSource(neighbourState)); } return super.updateShape(state, level, ticks, pos, directionToNeighbour, neighbourPos, neighbourState, random); } private boolean isSmokeSource(BlockState blockState) { return blockState.is(Blocks.HAY_BLOCK); } @Override protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { if (!state.getValue(LIT).booleanValue()) { return; } if (random.nextInt(10) == 0) { level.playLocalSound((double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5, SoundEvents.CAMPFIRE_CRACKLE, SoundSource.BLOCKS, 0.5f + random.nextFloat(), random.nextFloat() * 0.7f + 0.6f, false); } if (this.spawnParticles && random.nextInt(5) == 0) { for (int i = 0; i < random.nextInt(1) + 1; ++i) { level.addParticle(ParticleTypes.LAVA, (double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5, random.nextFloat() / 2.0f, 5.0E-5, random.nextFloat() / 2.0f); } } } public static void dowse(@Nullable Entity source, LevelAccessor level, BlockPos pos, BlockState state) { if (level.isClientSide()) { for (int j = 0; j < 20; ++j) { CampfireBlock.makeParticles((Level)level, pos, state.getValue(SIGNAL_FIRE), true); } } level.gameEvent(source, GameEvent.BLOCK_CHANGE, pos); } @Override public boolean placeLiquid(LevelAccessor level, BlockPos pos, BlockState state, FluidState fluidState) { if (!state.getValue(BlockStateProperties.WATERLOGGED).booleanValue() && fluidState.getType() == Fluids.WATER) { boolean isLit = state.getValue(LIT); if (isLit) { if (!level.isClientSide()) { level.playSound(null, pos, SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.BLOCKS, 1.0f, 1.0f); } CampfireBlock.dowse(null, level, pos, state); } level.setBlock(pos, (BlockState)((BlockState)state.setValue(WATERLOGGED, true)).setValue(LIT, false), 3); level.scheduleTick(pos, fluidState.getType(), fluidState.getType().getTickDelay(level)); return true; } return false; } @Override protected void onProjectileHit(Level level, BlockState state, BlockHitResult blockHit, Projectile projectile) { BlockPos pos = blockHit.getBlockPos(); if (level instanceof ServerLevel) { ServerLevel serverLevel = (ServerLevel)level; if (projectile.isOnFire() && projectile.mayInteract(serverLevel, pos) && !state.getValue(LIT).booleanValue() && !state.getValue(WATERLOGGED).booleanValue()) { level.setBlock(pos, (BlockState)state.setValue(BlockStateProperties.LIT, true), 11); } } } public static void makeParticles(Level level, BlockPos pos, boolean isSignalFire, boolean smoking) { RandomSource random = level.getRandom(); SimpleParticleType smokeParticle = isSignalFire ? ParticleTypes.CAMPFIRE_SIGNAL_SMOKE : ParticleTypes.CAMPFIRE_COSY_SMOKE; level.addAlwaysVisibleParticle(smokeParticle, true, (double)pos.getX() + 0.5 + random.nextDouble() / 3.0 * (double)(random.nextBoolean() ? 1 : -1), (double)pos.getY() + random.nextDouble() + random.nextDouble(), (double)pos.getZ() + 0.5 + random.nextDouble() / 3.0 * (double)(random.nextBoolean() ? 1 : -1), 0.0, 0.07, 0.0); if (smoking) { level.addParticle(ParticleTypes.SMOKE, (double)pos.getX() + 0.5 + random.nextDouble() / 4.0 * (double)(random.nextBoolean() ? 1 : -1), (double)pos.getY() + 0.4, (double)pos.getZ() + 0.5 + random.nextDouble() / 4.0 * (double)(random.nextBoolean() ? 1 : -1), 0.0, 0.005, 0.0); } } public static boolean isSmokeyPos(Level level, BlockPos pos) { for (int i = 1; i <= 5; ++i) { BlockPos posToCheck = pos.below(i); BlockState blockState = level.getBlockState(posToCheck); if (CampfireBlock.isLitCampfire(blockState)) { return true; } boolean smokeBlocked = Shapes.joinIsNotEmpty(SHAPE_VIRTUAL_POST, blockState.getCollisionShape(level, pos, CollisionContext.empty()), BooleanOp.AND); if (!smokeBlocked) continue; BlockState belowState = level.getBlockState(posToCheck.below()); return CampfireBlock.isLitCampfire(belowState); } return false; } public static boolean isLitCampfire(BlockState blockState) { return blockState.hasProperty(LIT) && blockState.is(BlockTags.CAMPFIRES) && blockState.getValue(LIT) != false; } @Override protected FluidState getFluidState(BlockState state) { if (state.getValue(WATERLOGGED).booleanValue()) { return Fluids.WATER.getSource(false); } return super.getFluidState(state); } @Override protected BlockState rotate(BlockState state, Rotation rotation) { return (BlockState)state.setValue(FACING, rotation.rotate(state.getValue(FACING))); } @Override protected BlockState mirror(BlockState state, Mirror mirror) { return state.rotate(mirror.getRotation(state.getValue(FACING))); } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(LIT, SIGNAL_FIRE, WATERLOGGED, FACING); } @Override public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) { return new CampfireBlockEntity(worldPosition, blockState); } @Override public @Nullable BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType type) { if (level instanceof ServerLevel) { ServerLevel serverLevel = (ServerLevel)level; if (blockState.getValue(LIT).booleanValue()) { RecipeManager.CachedCheck quickCheck = RecipeManager.createCheck(RecipeType.CAMPFIRE_COOKING); return CampfireBlock.createTickerHelper(type, BlockEntityType.CAMPFIRE, (innerLevel, pos, state, entity) -> CampfireBlockEntity.cookTick(serverLevel, pos, state, entity, quickCheck)); } return CampfireBlock.createTickerHelper(type, BlockEntityType.CAMPFIRE, CampfireBlockEntity::cooldownTick); } if (blockState.getValue(LIT).booleanValue()) { return CampfireBlock.createTickerHelper(type, BlockEntityType.CAMPFIRE, CampfireBlockEntity::particleTick); } return null; } @Override protected boolean isPathfindable(BlockState state, PathComputationType type) { return false; } public static boolean canLight(BlockState state) { return state.is(BlockTags.CAMPFIRES, s -> s.hasProperty(WATERLOGGED) && s.hasProperty(LIT)) && state.getValue(WATERLOGGED) == false && state.getValue(LIT) == false; } }