286 lines
14 KiB
Java
286 lines
14 KiB
Java
/*
|
|
* 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<CampfireBlock> 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<Direction> 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<CampfireBlock> 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<Block, BlockState> builder) {
|
|
builder.add(LIT, SIGNAL_FIRE, WATERLOGGED, FACING);
|
|
}
|
|
|
|
@Override
|
|
public BlockEntity newBlockEntity(BlockPos worldPosition, BlockState blockState) {
|
|
return new CampfireBlockEntity(worldPosition, blockState);
|
|
}
|
|
|
|
@Override
|
|
public <T extends BlockEntity> @Nullable BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> 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;
|
|
}
|
|
}
|
|
|