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.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.EntityTypeTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.InsideBlockEffectApplier; import net.minecraft.world.entity.InsideBlockEffectType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; 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.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.EntityCollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; public class PowderSnowBlock extends Block implements BucketPickup { public static final MapCodec CODEC = simpleCodec(PowderSnowBlock::new); private static final float HORIZONTAL_PARTICLE_MOMENTUM_FACTOR = 0.083333336F; private static final float IN_BLOCK_HORIZONTAL_SPEED_MULTIPLIER = 0.9F; private static final float IN_BLOCK_VERTICAL_SPEED_MULTIPLIER = 1.5F; private static final float NUM_BLOCKS_TO_FALL_INTO_BLOCK = 2.5F; private static final VoxelShape FALLING_COLLISION_SHAPE = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.9F, 1.0); private static final double MINIMUM_FALL_DISTANCE_FOR_SOUND = 4.0; private static final double MINIMUM_FALL_DISTANCE_FOR_BIG_SOUND = 7.0; @Override public MapCodec codec() { return CODEC; } public PowderSnowBlock(BlockBehaviour.Properties properties) { super(properties); } @Override protected boolean skipRendering(BlockState state, BlockState adjacentState, Direction direction) { return adjacentState.is(this) ? true : super.skipRendering(state, adjacentState, direction); } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity, InsideBlockEffectApplier effectApplier) { if (!(entity instanceof LivingEntity) || entity.getInBlockState().is(this)) { entity.makeStuckInBlock(state, new Vec3(0.9F, 1.5, 0.9F)); if (level.isClientSide) { RandomSource randomSource = level.getRandom(); boolean bl = entity.xOld != entity.getX() || entity.zOld != entity.getZ(); if (bl && randomSource.nextBoolean()) { level.addParticle( ParticleTypes.SNOWFLAKE, entity.getX(), pos.getY() + 1, entity.getZ(), Mth.randomBetween(randomSource, -1.0F, 1.0F) * 0.083333336F, 0.05F, Mth.randomBetween(randomSource, -1.0F, 1.0F) * 0.083333336F ); } } } BlockPos blockPos = pos.immutable(); effectApplier.runBefore( InsideBlockEffectType.EXTINGUISH, entityx -> { if (level instanceof ServerLevel serverLevel && entityx.isOnFire() && (serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || entityx instanceof Player) && entityx.mayInteract(serverLevel, blockPos)) { level.destroyBlock(blockPos, false); } } ); effectApplier.apply(InsideBlockEffectType.FREEZE); effectApplier.apply(InsideBlockEffectType.EXTINGUISH); } @Override public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, double fallDistance) { if (!(fallDistance < 4.0) && entity instanceof LivingEntity livingEntity) { LivingEntity.Fallsounds fallsounds = livingEntity.getFallSounds(); SoundEvent soundEvent = fallDistance < 7.0 ? fallsounds.small() : fallsounds.big(); entity.playSound(soundEvent, 1.0F, 1.0F); } } @Override protected VoxelShape getEntityInsideCollisionShape(BlockState state, BlockGetter level, BlockPos pos, Entity entity) { VoxelShape voxelShape = this.getCollisionShape(state, level, pos, CollisionContext.of(entity)); return voxelShape.isEmpty() ? Shapes.block() : voxelShape; } @Override protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { if (!context.isPlacement() && context instanceof EntityCollisionContext entityCollisionContext) { Entity entity = entityCollisionContext.getEntity(); if (entity != null) { if (entity.fallDistance > 2.5) { return FALLING_COLLISION_SHAPE; } boolean bl = entity instanceof FallingBlockEntity; if (bl || canEntityWalkOnPowderSnow(entity) && context.isAbove(Shapes.block(), pos, false) && !context.isDescending()) { return super.getCollisionShape(state, level, pos, context); } } } return Shapes.empty(); } @Override protected VoxelShape getVisualShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { return Shapes.empty(); } public static boolean canEntityWalkOnPowderSnow(Entity entity) { if (entity.getType().is(EntityTypeTags.POWDER_SNOW_WALKABLE_MOBS)) { return true; } else { return entity instanceof LivingEntity ? ((LivingEntity)entity).getItemBySlot(EquipmentSlot.FEET).is(Items.LEATHER_BOOTS) : false; } } @Override public ItemStack pickupBlock(@Nullable LivingEntity owner, LevelAccessor level, BlockPos pos, BlockState state) { level.setBlock(pos, Blocks.AIR.defaultBlockState(), 11); if (!level.isClientSide()) { level.levelEvent(2001, pos, Block.getId(state)); } return new ItemStack(Items.POWDER_SNOW_BUCKET); } @Override public Optional getPickupSound() { return Optional.of(SoundEvents.BUCKET_FILL_POWDER_SNOW); } @Override protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { return true; } }