223 lines
8.6 KiB
Java
223 lines
8.6 KiB
Java
package net.minecraft.world.level.portal;
|
|
|
|
import java.util.Optional;
|
|
import java.util.function.Predicate;
|
|
import net.minecraft.BlockUtil.FoundRectangle;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityDimensions;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.NetherPortalBlock;
|
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.apache.commons.lang3.mutable.MutableInt;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class PortalShape {
|
|
private static final int MIN_WIDTH = 2;
|
|
public static final int MAX_WIDTH = 21;
|
|
private static final int MIN_HEIGHT = 3;
|
|
public static final int MAX_HEIGHT = 21;
|
|
private static final BlockBehaviour.StatePredicate FRAME = (blockState, blockGetter, blockPos) -> blockState.is(Blocks.OBSIDIAN);
|
|
private static final float SAFE_TRAVEL_MAX_ENTITY_XY = 4.0F;
|
|
private static final double SAFE_TRAVEL_MAX_VERTICAL_DELTA = 1.0;
|
|
private final Direction.Axis axis;
|
|
private final Direction rightDir;
|
|
private final int numPortalBlocks;
|
|
private final BlockPos bottomLeft;
|
|
private final int height;
|
|
private final int width;
|
|
|
|
private PortalShape(Direction.Axis axis, int numPortalBlocks, Direction rightDir, BlockPos bottomLeft, int width, int height) {
|
|
this.axis = axis;
|
|
this.numPortalBlocks = numPortalBlocks;
|
|
this.rightDir = rightDir;
|
|
this.bottomLeft = bottomLeft;
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
public static Optional<PortalShape> findEmptyPortalShape(LevelAccessor level, BlockPos bottomLeft, Direction.Axis axis) {
|
|
return findPortalShape(level, bottomLeft, portalShape -> portalShape.isValid() && portalShape.numPortalBlocks == 0, axis);
|
|
}
|
|
|
|
public static Optional<PortalShape> findPortalShape(LevelAccessor level, BlockPos bottomLeft, Predicate<PortalShape> predicate, Direction.Axis axis) {
|
|
Optional<PortalShape> optional = Optional.of(findAnyShape(level, bottomLeft, axis)).filter(predicate);
|
|
if (optional.isPresent()) {
|
|
return optional;
|
|
} else {
|
|
Direction.Axis axis2 = axis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
|
|
return Optional.of(findAnyShape(level, bottomLeft, axis2)).filter(predicate);
|
|
}
|
|
}
|
|
|
|
public static PortalShape findAnyShape(BlockGetter level, BlockPos bottomLeft, Direction.Axis axis) {
|
|
Direction direction = axis == Direction.Axis.X ? Direction.WEST : Direction.SOUTH;
|
|
BlockPos blockPos = calculateBottomLeft(level, direction, bottomLeft);
|
|
if (blockPos == null) {
|
|
return new PortalShape(axis, 0, direction, bottomLeft, 0, 0);
|
|
} else {
|
|
int i = calculateWidth(level, blockPos, direction);
|
|
if (i == 0) {
|
|
return new PortalShape(axis, 0, direction, blockPos, 0, 0);
|
|
} else {
|
|
MutableInt mutableInt = new MutableInt();
|
|
int j = calculateHeight(level, blockPos, direction, i, mutableInt);
|
|
return new PortalShape(axis, mutableInt.getValue(), direction, blockPos, i, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static BlockPos calculateBottomLeft(BlockGetter level, Direction direction, BlockPos pos) {
|
|
int i = Math.max(level.getMinY(), pos.getY() - 21);
|
|
|
|
while (pos.getY() > i && isEmpty(level.getBlockState(pos.below()))) {
|
|
pos = pos.below();
|
|
}
|
|
|
|
Direction direction2 = direction.getOpposite();
|
|
int j = getDistanceUntilEdgeAboveFrame(level, pos, direction2) - 1;
|
|
return j < 0 ? null : pos.relative(direction2, j);
|
|
}
|
|
|
|
private static int calculateWidth(BlockGetter level, BlockPos bottomLeft, Direction direction) {
|
|
int i = getDistanceUntilEdgeAboveFrame(level, bottomLeft, direction);
|
|
return i >= 2 && i <= 21 ? i : 0;
|
|
}
|
|
|
|
private static int getDistanceUntilEdgeAboveFrame(BlockGetter level, BlockPos pos, Direction direction) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
|
|
for (int i = 0; i <= 21; i++) {
|
|
mutableBlockPos.set(pos).move(direction, i);
|
|
BlockState blockState = level.getBlockState(mutableBlockPos);
|
|
if (!isEmpty(blockState)) {
|
|
if (FRAME.test(blockState, level, mutableBlockPos)) {
|
|
return i;
|
|
}
|
|
break;
|
|
}
|
|
|
|
BlockState blockState2 = level.getBlockState(mutableBlockPos.move(Direction.DOWN));
|
|
if (!FRAME.test(blockState2, level, mutableBlockPos)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static int calculateHeight(BlockGetter level, BlockPos pos, Direction direction, int width, MutableInt portalBlocks) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
int i = getDistanceUntilTop(level, pos, direction, mutableBlockPos, width, portalBlocks);
|
|
return i >= 3 && i <= 21 && hasTopFrame(level, pos, direction, mutableBlockPos, width, i) ? i : 0;
|
|
}
|
|
|
|
private static boolean hasTopFrame(BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, int distanceUntilTop) {
|
|
for (int i = 0; i < width; i++) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = checkPos.set(pos).move(Direction.UP, distanceUntilTop).move(direction, i);
|
|
if (!FRAME.test(level.getBlockState(mutableBlockPos), level, mutableBlockPos)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static int getDistanceUntilTop(
|
|
BlockGetter level, BlockPos pos, Direction direction, BlockPos.MutableBlockPos checkPos, int width, MutableInt portalBlocks
|
|
) {
|
|
for (int i = 0; i < 21; i++) {
|
|
checkPos.set(pos).move(Direction.UP, i).move(direction, -1);
|
|
if (!FRAME.test(level.getBlockState(checkPos), level, checkPos)) {
|
|
return i;
|
|
}
|
|
|
|
checkPos.set(pos).move(Direction.UP, i).move(direction, width);
|
|
if (!FRAME.test(level.getBlockState(checkPos), level, checkPos)) {
|
|
return i;
|
|
}
|
|
|
|
for (int j = 0; j < width; j++) {
|
|
checkPos.set(pos).move(Direction.UP, i).move(direction, j);
|
|
BlockState blockState = level.getBlockState(checkPos);
|
|
if (!isEmpty(blockState)) {
|
|
return i;
|
|
}
|
|
|
|
if (blockState.is(Blocks.NETHER_PORTAL)) {
|
|
portalBlocks.increment();
|
|
}
|
|
}
|
|
}
|
|
|
|
return 21;
|
|
}
|
|
|
|
private static boolean isEmpty(BlockState state) {
|
|
return state.isAir() || state.is(BlockTags.FIRE) || state.is(Blocks.NETHER_PORTAL);
|
|
}
|
|
|
|
public boolean isValid() {
|
|
return this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21;
|
|
}
|
|
|
|
public void createPortalBlocks(LevelAccessor level) {
|
|
BlockState blockState = Blocks.NETHER_PORTAL.defaultBlockState().setValue(NetherPortalBlock.AXIS, this.axis);
|
|
BlockPos.betweenClosed(this.bottomLeft, this.bottomLeft.relative(Direction.UP, this.height - 1).relative(this.rightDir, this.width - 1))
|
|
.forEach(blockPos -> level.setBlock(blockPos, blockState, 18));
|
|
}
|
|
|
|
public boolean isComplete() {
|
|
return this.isValid() && this.numPortalBlocks == this.width * this.height;
|
|
}
|
|
|
|
public static Vec3 getRelativePosition(FoundRectangle foundRectangle, Direction.Axis axis, Vec3 pos, EntityDimensions entityDimensions) {
|
|
double d = (double)foundRectangle.axis1Size - entityDimensions.width();
|
|
double e = (double)foundRectangle.axis2Size - entityDimensions.height();
|
|
BlockPos blockPos = foundRectangle.minCorner;
|
|
double g;
|
|
if (d > 0.0) {
|
|
double f = blockPos.get(axis) + entityDimensions.width() / 2.0;
|
|
g = Mth.clamp(Mth.inverseLerp(pos.get(axis) - f, 0.0, d), 0.0, 1.0);
|
|
} else {
|
|
g = 0.5;
|
|
}
|
|
|
|
double f;
|
|
if (e > 0.0) {
|
|
Direction.Axis axis2 = Direction.Axis.Y;
|
|
f = Mth.clamp(Mth.inverseLerp(pos.get(axis2) - blockPos.get(axis2), 0.0, e), 0.0, 1.0);
|
|
} else {
|
|
f = 0.0;
|
|
}
|
|
|
|
Direction.Axis axis2 = axis == Direction.Axis.X ? Direction.Axis.Z : Direction.Axis.X;
|
|
double h = pos.get(axis2) - (blockPos.get(axis2) + 0.5);
|
|
return new Vec3(g, f, h);
|
|
}
|
|
|
|
public static Vec3 findCollisionFreePosition(Vec3 pos, ServerLevel level, Entity entity, EntityDimensions dimensions) {
|
|
if (!(dimensions.width() > 4.0F) && !(dimensions.height() > 4.0F)) {
|
|
double d = dimensions.height() / 2.0;
|
|
Vec3 vec3 = pos.add(0.0, d, 0.0);
|
|
VoxelShape voxelShape = Shapes.create(AABB.ofSize(vec3, dimensions.width(), 0.0, dimensions.width()).expandTowards(0.0, 1.0, 0.0).inflate(1.0E-6));
|
|
Optional<Vec3> optional = level.findFreePosition(entity, voxelShape, vec3, dimensions.width(), dimensions.height(), dimensions.width());
|
|
Optional<Vec3> optional2 = optional.map(vec3x -> vec3x.subtract(0.0, d, 0.0));
|
|
return (Vec3)optional2.orElse(pos);
|
|
} else {
|
|
return pos;
|
|
}
|
|
}
|
|
}
|