package net.minecraft.world.level.levelgen; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.LevelSimulatedReader; import net.minecraft.world.level.block.state.BlockState; /** * A representation of an integer valued interval, either bounded or unbounded. * While the class itself does not imply any coordinate in particular, this is practically used to represent a column in the Y direction. */ public abstract class Column { /** * @return A column of the closed interval [floor, ceiling]. */ public static Column.Range around(int floor, int ceiling) { return new Column.Range(floor - 1, ceiling + 1); } /** * @return A column of the open interval (floor, ceiling). */ public static Column.Range inside(int floor, int ceiling) { return new Column.Range(floor, ceiling); } /** * @return A column of the unbounded interval (-infinity, ceiling). */ public static Column below(int ceiling) { return new Column.Ray(ceiling, false); } /** * @return A column of the unbounded interval (-infinity, ceiling]. */ public static Column fromHighest(int ceiling) { return new Column.Ray(ceiling + 1, false); } /** * @return A column of the unbounded interval (floor, infinity). */ public static Column above(int floor) { return new Column.Ray(floor, true); } /** * @return A column of the unbounded interval [floor, infinity). */ public static Column fromLowest(int floor) { return new Column.Ray(floor - 1, true); } /** * @return A column of the unbounded interval (-infinity, infinity). */ public static Column line() { return Column.Line.INSTANCE; } public static Column create(OptionalInt floor, OptionalInt ceiling) { if (floor.isPresent() && ceiling.isPresent()) { return inside(floor.getAsInt(), ceiling.getAsInt()); } else if (floor.isPresent()) { return above(floor.getAsInt()); } else { return ceiling.isPresent() ? below(ceiling.getAsInt()) : line(); } } public abstract OptionalInt getCeiling(); public abstract OptionalInt getFloor(); public abstract OptionalInt getHeight(); public Column withFloor(OptionalInt floor) { return create(floor, this.getCeiling()); } public Column withCeiling(OptionalInt ceiling) { return create(this.getFloor(), ceiling); } /** * Scans for a column of states satisfying {@code columnPredicate}, up to a length of {@code maxDistance} from the origin, and ending with a state which satisfies {@code tipPredicate}. * @return A column representing the tips found. The column will be bounded if a tip was reached in the given direction, unbounded otherwise. */ public static Optional scan( LevelSimulatedReader level, BlockPos pos, int maxDistance, Predicate columnPredicate, Predicate tipPredicate ) { BlockPos.MutableBlockPos mutableBlockPos = pos.mutable(); if (!level.isStateAtPosition(pos, columnPredicate)) { return Optional.empty(); } else { int i = pos.getY(); OptionalInt optionalInt = scanDirection(level, maxDistance, columnPredicate, tipPredicate, mutableBlockPos, i, Direction.UP); OptionalInt optionalInt2 = scanDirection(level, maxDistance, columnPredicate, tipPredicate, mutableBlockPos, i, Direction.DOWN); return Optional.of(create(optionalInt2, optionalInt)); } } /** * Scans for a sequence of states in a given {@code direction}, up to a length of {@code maxDistance} which satisfy {@code columnPredicate}, and ending with a state which satisfies {@code tipPredicate}. * @return The y position of the tip, if found. */ private static OptionalInt scanDirection( LevelSimulatedReader level, int maxDistance, Predicate columnPredicate, Predicate tipPredicate, BlockPos.MutableBlockPos mutablePos, int startY, Direction direction ) { mutablePos.setY(startY); for (int i = 1; i < maxDistance && level.isStateAtPosition(mutablePos, columnPredicate); i++) { mutablePos.move(direction); } return level.isStateAtPosition(mutablePos, tipPredicate) ? OptionalInt.of(mutablePos.getY()) : OptionalInt.empty(); } public static final class Line extends Column { static final Column.Line INSTANCE = new Column.Line(); private Line() { } @Override public OptionalInt getCeiling() { return OptionalInt.empty(); } @Override public OptionalInt getFloor() { return OptionalInt.empty(); } @Override public OptionalInt getHeight() { return OptionalInt.empty(); } public String toString() { return "C(-)"; } } public static final class Range extends Column { private final int floor; private final int ceiling; protected Range(int floor, int ceiling) { this.floor = floor; this.ceiling = ceiling; if (this.height() < 0) { throw new IllegalArgumentException("Column of negative height: " + this); } } @Override public OptionalInt getCeiling() { return OptionalInt.of(this.ceiling); } @Override public OptionalInt getFloor() { return OptionalInt.of(this.floor); } @Override public OptionalInt getHeight() { return OptionalInt.of(this.height()); } public int ceiling() { return this.ceiling; } public int floor() { return this.floor; } public int height() { return this.ceiling - this.floor - 1; } public String toString() { return "C(" + this.ceiling + "-" + this.floor + ")"; } } public static final class Ray extends Column { private final int edge; private final boolean pointingUp; public Ray(int edge, boolean pointingUp) { this.edge = edge; this.pointingUp = pointingUp; } @Override public OptionalInt getCeiling() { return this.pointingUp ? OptionalInt.empty() : OptionalInt.of(this.edge); } @Override public OptionalInt getFloor() { return this.pointingUp ? OptionalInt.of(this.edge) : OptionalInt.empty(); } @Override public OptionalInt getHeight() { return OptionalInt.empty(); } public String toString() { return this.pointingUp ? "C(" + this.edge + "-)" : "C(-" + this.edge + ")"; } } }