192 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.block;
 | |
| 
 | |
| import com.google.common.annotations.VisibleForTesting;
 | |
| import java.util.Optional;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Direction;
 | |
| import net.minecraft.util.RandomSource;
 | |
| import net.minecraft.world.level.BlockGetter;
 | |
| import net.minecraft.world.level.LevelAccessor;
 | |
| import net.minecraft.world.level.block.state.BlockState;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class MultifaceSpreader {
 | |
| 	public static final MultifaceSpreader.SpreadType[] DEFAULT_SPREAD_ORDER = new MultifaceSpreader.SpreadType[]{
 | |
| 		MultifaceSpreader.SpreadType.SAME_POSITION, MultifaceSpreader.SpreadType.SAME_PLANE, MultifaceSpreader.SpreadType.WRAP_AROUND
 | |
| 	};
 | |
| 	private final MultifaceSpreader.SpreadConfig config;
 | |
| 
 | |
| 	public MultifaceSpreader(MultifaceBlock block) {
 | |
| 		this(new MultifaceSpreader.DefaultSpreaderConfig(block));
 | |
| 	}
 | |
| 
 | |
| 	public MultifaceSpreader(MultifaceSpreader.SpreadConfig config) {
 | |
| 		this.config = config;
 | |
| 	}
 | |
| 
 | |
| 	public boolean canSpreadInAnyDirection(BlockState state, BlockGetter level, BlockPos pos, Direction spreadDirection) {
 | |
| 		return Direction.stream()
 | |
| 			.anyMatch(direction2 -> this.getSpreadFromFaceTowardDirection(state, level, pos, spreadDirection, direction2, this.config::canSpreadInto).isPresent());
 | |
| 	}
 | |
| 
 | |
| 	public Optional<MultifaceSpreader.SpreadPos> spreadFromRandomFaceTowardRandomDirection(
 | |
| 		BlockState state, LevelAccessor level, BlockPos pos, RandomSource random
 | |
| 	) {
 | |
| 		return (Optional<MultifaceSpreader.SpreadPos>)Direction.allShuffled(random)
 | |
| 			.stream()
 | |
| 			.filter(direction -> this.config.canSpreadFrom(state, direction))
 | |
| 			.map(direction -> this.spreadFromFaceTowardRandomDirection(state, level, pos, direction, random, false))
 | |
| 			.filter(Optional::isPresent)
 | |
| 			.findFirst()
 | |
| 			.orElse(Optional.empty());
 | |
| 	}
 | |
| 
 | |
| 	public long spreadAll(BlockState state, LevelAccessor level, BlockPos pos, boolean markForPostprocessing) {
 | |
| 		return (Long)Direction.stream()
 | |
| 			.filter(direction -> this.config.canSpreadFrom(state, direction))
 | |
| 			.map(direction -> this.spreadFromFaceTowardAllDirections(state, level, pos, direction, markForPostprocessing))
 | |
| 			.reduce(0L, Long::sum);
 | |
| 	}
 | |
| 
 | |
| 	public Optional<MultifaceSpreader.SpreadPos> spreadFromFaceTowardRandomDirection(
 | |
| 		BlockState state, LevelAccessor level, BlockPos pos, Direction spreadDirection, RandomSource random, boolean markForPostprocessing
 | |
| 	) {
 | |
| 		return (Optional<MultifaceSpreader.SpreadPos>)Direction.allShuffled(random)
 | |
| 			.stream()
 | |
| 			.map(direction2 -> this.spreadFromFaceTowardDirection(state, level, pos, spreadDirection, direction2, markForPostprocessing))
 | |
| 			.filter(Optional::isPresent)
 | |
| 			.findFirst()
 | |
| 			.orElse(Optional.empty());
 | |
| 	}
 | |
| 
 | |
| 	private long spreadFromFaceTowardAllDirections(BlockState state, LevelAccessor level, BlockPos pos, Direction spreadDirection, boolean markForPostprocessing) {
 | |
| 		return Direction.stream()
 | |
| 			.map(direction2 -> this.spreadFromFaceTowardDirection(state, level, pos, spreadDirection, direction2, markForPostprocessing))
 | |
| 			.filter(Optional::isPresent)
 | |
| 			.count();
 | |
| 	}
 | |
| 
 | |
| 	@VisibleForTesting
 | |
| 	public Optional<MultifaceSpreader.SpreadPos> spreadFromFaceTowardDirection(
 | |
| 		BlockState state, LevelAccessor level, BlockPos pos, Direction spreadDirection, Direction face, boolean markForPostprocessing
 | |
| 	) {
 | |
| 		return this.getSpreadFromFaceTowardDirection(state, level, pos, spreadDirection, face, this.config::canSpreadInto)
 | |
| 			.flatMap(spreadPos -> this.spreadToFace(level, spreadPos, markForPostprocessing));
 | |
| 	}
 | |
| 
 | |
| 	public Optional<MultifaceSpreader.SpreadPos> getSpreadFromFaceTowardDirection(
 | |
| 		BlockState state, BlockGetter level, BlockPos pos, Direction spreadDirection, Direction face, MultifaceSpreader.SpreadPredicate predicate
 | |
| 	) {
 | |
| 		if (face.getAxis() == spreadDirection.getAxis()) {
 | |
| 			return Optional.empty();
 | |
| 		} else if (this.config.isOtherBlockValidAsSource(state) || this.config.hasFace(state, spreadDirection) && !this.config.hasFace(state, face)) {
 | |
| 			for (MultifaceSpreader.SpreadType spreadType : this.config.getSpreadTypes()) {
 | |
| 				MultifaceSpreader.SpreadPos spreadPos = spreadType.getSpreadPos(pos, face, spreadDirection);
 | |
| 				if (predicate.test(level, pos, spreadPos)) {
 | |
| 					return Optional.of(spreadPos);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return Optional.empty();
 | |
| 		} else {
 | |
| 			return Optional.empty();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public Optional<MultifaceSpreader.SpreadPos> spreadToFace(LevelAccessor level, MultifaceSpreader.SpreadPos pos, boolean markForPostprocessing) {
 | |
| 		BlockState blockState = level.getBlockState(pos.pos());
 | |
| 		return this.config.placeBlock(level, pos, blockState, markForPostprocessing) ? Optional.of(pos) : Optional.empty();
 | |
| 	}
 | |
| 
 | |
| 	public static class DefaultSpreaderConfig implements MultifaceSpreader.SpreadConfig {
 | |
| 		protected MultifaceBlock block;
 | |
| 
 | |
| 		public DefaultSpreaderConfig(MultifaceBlock block) {
 | |
| 			this.block = block;
 | |
| 		}
 | |
| 
 | |
| 		@Nullable
 | |
| 		@Override
 | |
| 		public BlockState getStateForPlacement(BlockState currentState, BlockGetter level, BlockPos pos, Direction lookingDirection) {
 | |
| 			return this.block.getStateForPlacement(currentState, level, pos, lookingDirection);
 | |
| 		}
 | |
| 
 | |
| 		protected boolean stateCanBeReplaced(BlockGetter level, BlockPos pos, BlockPos spreadPos, Direction direction, BlockState state) {
 | |
| 			return state.isAir() || state.is(this.block) || state.is(Blocks.WATER) && state.getFluidState().isSource();
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public boolean canSpreadInto(BlockGetter level, BlockPos pos, MultifaceSpreader.SpreadPos spreadPos) {
 | |
| 			BlockState blockState = level.getBlockState(spreadPos.pos());
 | |
| 			return this.stateCanBeReplaced(level, pos, spreadPos.pos(), spreadPos.face(), blockState)
 | |
| 				&& this.block.isValidStateForPlacement(level, blockState, spreadPos.pos(), spreadPos.face());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public interface SpreadConfig {
 | |
| 		@Nullable
 | |
| 		BlockState getStateForPlacement(BlockState currentState, BlockGetter level, BlockPos pos, Direction lookingDirection);
 | |
| 
 | |
| 		boolean canSpreadInto(BlockGetter level, BlockPos pos, MultifaceSpreader.SpreadPos spreadPos);
 | |
| 
 | |
| 		default MultifaceSpreader.SpreadType[] getSpreadTypes() {
 | |
| 			return MultifaceSpreader.DEFAULT_SPREAD_ORDER;
 | |
| 		}
 | |
| 
 | |
| 		default boolean hasFace(BlockState state, Direction direction) {
 | |
| 			return MultifaceBlock.hasFace(state, direction);
 | |
| 		}
 | |
| 
 | |
| 		default boolean isOtherBlockValidAsSource(BlockState otherBlock) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		default boolean canSpreadFrom(BlockState state, Direction direction) {
 | |
| 			return this.isOtherBlockValidAsSource(state) || this.hasFace(state, direction);
 | |
| 		}
 | |
| 
 | |
| 		default boolean placeBlock(LevelAccessor level, MultifaceSpreader.SpreadPos pos, BlockState state, boolean markForPostprocessing) {
 | |
| 			BlockState blockState = this.getStateForPlacement(state, level, pos.pos(), pos.face());
 | |
| 			if (blockState != null) {
 | |
| 				if (markForPostprocessing) {
 | |
| 					level.getChunk(pos.pos()).markPosForPostprocessing(pos.pos());
 | |
| 				}
 | |
| 
 | |
| 				return level.setBlock(pos.pos(), blockState, 2);
 | |
| 			} else {
 | |
| 				return false;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public record SpreadPos(BlockPos pos, Direction face) {
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	public interface SpreadPredicate {
 | |
| 		boolean test(BlockGetter blockGetter, BlockPos blockPos, MultifaceSpreader.SpreadPos spreadPos);
 | |
| 	}
 | |
| 
 | |
| 	public static enum SpreadType {
 | |
| 		SAME_POSITION {
 | |
| 			@Override
 | |
| 			public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) {
 | |
| 				return new MultifaceSpreader.SpreadPos(pos, face);
 | |
| 			}
 | |
| 		},
 | |
| 		SAME_PLANE {
 | |
| 			@Override
 | |
| 			public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) {
 | |
| 				return new MultifaceSpreader.SpreadPos(pos.relative(face), spreadDirection);
 | |
| 			}
 | |
| 		},
 | |
| 		WRAP_AROUND {
 | |
| 			@Override
 | |
| 			public MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection) {
 | |
| 				return new MultifaceSpreader.SpreadPos(pos.relative(face).relative(spreadDirection), face.getOpposite());
 | |
| 			}
 | |
| 		};
 | |
| 
 | |
| 		public abstract MultifaceSpreader.SpreadPos getSpreadPos(BlockPos pos, Direction face, Direction spreadDirection);
 | |
| 	}
 | |
| }
 |