package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import java.util.Collection; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagKey; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; import net.minecraft.world.level.block.MultifaceSpreader.DefaultSpreaderConfig; import net.minecraft.world.level.block.MultifaceSpreader.SpreadType; 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.material.FluidState; import net.minecraft.world.level.material.Fluids; public class SculkVeinBlock extends MultifaceBlock implements SculkBehaviour, SimpleWaterloggedBlock { public static final MapCodec CODEC = simpleCodec(SculkVeinBlock::new); private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; private final MultifaceSpreader veinSpreader = new MultifaceSpreader(new SculkVeinBlock.SculkVeinSpreaderConfig(MultifaceSpreader.DEFAULT_SPREAD_ORDER)); private final MultifaceSpreader sameSpaceSpreader = new MultifaceSpreader(new SculkVeinBlock.SculkVeinSpreaderConfig(SpreadType.SAME_POSITION)); @Override public MapCodec codec() { return CODEC; } public SculkVeinBlock(BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState(this.defaultBlockState().setValue(WATERLOGGED, false)); } @Override public MultifaceSpreader getSpreader() { return this.veinSpreader; } public MultifaceSpreader getSameSpaceSpreader() { return this.sameSpaceSpreader; } public static boolean regrow(LevelAccessor level, BlockPos pos, BlockState state, Collection directions) { boolean bl = false; BlockState blockState = Blocks.SCULK_VEIN.defaultBlockState(); for (Direction direction : directions) { BlockPos blockPos = pos.relative(direction); if (canAttachTo(level, direction, blockPos, level.getBlockState(blockPos))) { blockState = blockState.setValue(getFaceProperty(direction), true); bl = true; } } if (!bl) { return false; } else { if (!state.getFluidState().isEmpty()) { blockState = blockState.setValue(WATERLOGGED, true); } level.setBlock(pos, blockState, 3); return true; } } @Override public void onDischarged(LevelAccessor level, BlockState state, BlockPos pos, RandomSource random) { if (state.is(this)) { for (Direction direction : DIRECTIONS) { BooleanProperty booleanProperty = getFaceProperty(direction); if ((Boolean)state.getValue(booleanProperty) && level.getBlockState(pos.relative(direction)).is(Blocks.SCULK)) { state = state.setValue(booleanProperty, false); } } if (!hasAnyFace(state)) { FluidState fluidState = level.getFluidState(pos); state = (fluidState.isEmpty() ? Blocks.AIR : Blocks.WATER).defaultBlockState(); } level.setBlock(pos, state, 3); SculkBehaviour.super.onDischarged(level, state, pos, random); } } @Override public int attemptUseCharge( SculkSpreader.ChargeCursor cursor, LevelAccessor level, BlockPos pos, RandomSource random, SculkSpreader spreader, boolean shouldConvertBlocks ) { if (shouldConvertBlocks && this.attemptPlaceSculk(spreader, level, cursor.getPos(), random)) { return cursor.getCharge() - 1; } else { return random.nextInt(spreader.chargeDecayRate()) == 0 ? Mth.floor(cursor.getCharge() * 0.5F) : cursor.getCharge(); } } private boolean attemptPlaceSculk(SculkSpreader spreader, LevelAccessor level, BlockPos pos, RandomSource random) { BlockState blockState = level.getBlockState(pos); TagKey tagKey = spreader.replaceableBlocks(); for (Direction direction : Direction.allShuffled(random)) { if (hasFace(blockState, direction)) { BlockPos blockPos = pos.relative(direction); BlockState blockState2 = level.getBlockState(blockPos); if (blockState2.is(tagKey)) { BlockState blockState3 = Blocks.SCULK.defaultBlockState(); level.setBlock(blockPos, blockState3, 3); Block.pushEntitiesUp(blockState2, blockState3, level, blockPos); level.playSound(null, blockPos, SoundEvents.SCULK_BLOCK_SPREAD, SoundSource.BLOCKS, 1.0F, 1.0F); this.veinSpreader.spreadAll(blockState3, level, blockPos, spreader.isWorldGeneration()); Direction direction2 = direction.getOpposite(); for (Direction direction3 : DIRECTIONS) { if (direction3 != direction2) { BlockPos blockPos2 = blockPos.relative(direction3); BlockState blockState4 = level.getBlockState(blockPos2); if (blockState4.is(this)) { this.onDischarged(level, blockState4, blockPos2, random); } } } return true; } } } return false; } public static boolean hasSubstrateAccess(LevelAccessor level, BlockState state, BlockPos pos) { if (!state.is(Blocks.SCULK_VEIN)) { return false; } else { for (Direction direction : DIRECTIONS) { if (hasFace(state, direction) && level.getBlockState(pos.relative(direction)).is(BlockTags.SCULK_REPLACEABLE)) { return true; } } return false; } } @Override protected BlockState updateShape( BlockState blockState, LevelReader levelReader, ScheduledTickAccess scheduledTickAccess, BlockPos blockPos, Direction direction, BlockPos blockPos2, BlockState blockState2, RandomSource randomSource ) { if ((Boolean)blockState.getValue(WATERLOGGED)) { scheduledTickAccess.scheduleTick(blockPos, Fluids.WATER, Fluids.WATER.getTickDelay(levelReader)); } return super.updateShape(blockState, levelReader, scheduledTickAccess, blockPos, direction, blockPos2, blockState2, randomSource); } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { super.createBlockStateDefinition(builder); builder.add(WATERLOGGED); } @Override protected boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { return !useContext.getItemInHand().is(Items.SCULK_VEIN) || super.canBeReplaced(state, useContext); } @Override protected FluidState getFluidState(BlockState state) { return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); } class SculkVeinSpreaderConfig extends DefaultSpreaderConfig { private final SpreadType[] spreadTypes; public SculkVeinSpreaderConfig(final SpreadType... spreadTypes) { super(SculkVeinBlock.this); this.spreadTypes = spreadTypes; } @Override public boolean stateCanBeReplaced(BlockGetter level, BlockPos pos, BlockPos spreadPos, Direction direction, BlockState state) { BlockState blockState = level.getBlockState(spreadPos.relative(direction)); if (!blockState.is(Blocks.SCULK) && !blockState.is(Blocks.SCULK_CATALYST) && !blockState.is(Blocks.MOVING_PISTON)) { if (pos.distManhattan(spreadPos) == 2) { BlockPos blockPos = pos.relative(direction.getOpposite()); if (level.getBlockState(blockPos).isFaceSturdy(level, blockPos, direction)) { return false; } } FluidState fluidState = state.getFluidState(); if (!fluidState.isEmpty() && !fluidState.is(Fluids.WATER)) { return false; } else { return state.is(BlockTags.FIRE) ? false : state.canBeReplaced() || super.stateCanBeReplaced(level, pos, spreadPos, direction, state); } } else { return false; } } @Override public SpreadType[] getSpreadTypes() { return this.spreadTypes; } @Override public boolean isOtherBlockValidAsSource(BlockState otherBlock) { return !otherBlock.is(Blocks.SCULK_VEIN); } } }