minecraft-src/net/minecraft/world/level/portal/PortalShape.java
2025-07-04 02:49:36 +03:00

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;
}
}
}