minecraft-src/net/minecraft/world/level/levelgen/Column.java
2025-07-04 01:41:11 +03:00

227 lines
6.1 KiB
Java

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<Column> scan(
LevelSimulatedReader level, BlockPos pos, int maxDistance, Predicate<BlockState> columnPredicate, Predicate<BlockState> 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<BlockState> columnPredicate,
Predicate<BlockState> 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 + ")";
}
}
}