package net.minecraft.world.entity.ai.util; import com.google.common.annotations.VisibleForTesting; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.ToDoubleFunction; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class RandomPos { private static final int RANDOM_POS_ATTEMPTS = 10; /** * Gets a random position within a certain distance. */ public static BlockPos generateRandomDirection(RandomSource random, int horizontalDistance, int verticalDistance) { int i = random.nextInt(2 * horizontalDistance + 1) - horizontalDistance; int j = random.nextInt(2 * verticalDistance + 1) - verticalDistance; int k = random.nextInt(2 * horizontalDistance + 1) - horizontalDistance; return new BlockPos(i, j, k); } /** * @return a random (x, y, z) coordinate by picking a point (x, z), adding a random angle, up to a difference of {@code maxAngleDelta}. The y position is randomly chosen from the range {@code [y - yRange, y + yRange]}. Will be {@code null} if the chosen coordinate is outside a distance of {@code maxHorizontalDistance} from the origin. * * @param maxHorizontalDifference The maximum value in x and z, in absolute value, that could be returned. * @param yRange The range plus or minus the y position to be chosen * @param y The target y position * @param x The x offset to the target position * @param z The z offset to the target position * @param maxAngleDelta The maximum variance of the returned angle, from the base angle being a vector from (0, 0) to (x, z). */ @Nullable public static BlockPos generateRandomDirectionWithinRadians( RandomSource random, int maxHorizontalDifference, int yRange, int y, double x, double z, double maxAngleDelta ) { double d = Mth.atan2(z, x) - (float) (Math.PI / 2); double e = d + (2.0F * random.nextFloat() - 1.0F) * maxAngleDelta; double f = Math.sqrt(random.nextDouble()) * Mth.SQRT_OF_TWO * maxHorizontalDifference; double g = -f * Math.sin(e); double h = f * Math.cos(e); if (!(Math.abs(g) > maxHorizontalDifference) && !(Math.abs(h) > maxHorizontalDifference)) { int i = random.nextInt(2 * yRange + 1) - yRange + y; return BlockPos.containing(g, i, h); } else { return null; } } /** * @return the highest above position that is within the provided conditions */ @VisibleForTesting public static BlockPos moveUpOutOfSolid(BlockPos pos, int maxY, Predicate posPredicate) { if (!posPredicate.test(pos)) { return pos; } else { BlockPos blockPos = pos.above(); while (blockPos.getY() < maxY && posPredicate.test(blockPos)) { blockPos = blockPos.above(); } return blockPos; } } /** * Finds a position above based on the conditions. * * After it finds the position once, it will continue to move up until aboveSolidAmount is reached or the position is no longer valid */ @VisibleForTesting public static BlockPos moveUpToAboveSolid(BlockPos pos, int aboveSolidAmount, int maxY, Predicate posPredicate) { if (aboveSolidAmount < 0) { throw new IllegalArgumentException("aboveSolidAmount was " + aboveSolidAmount + ", expected >= 0"); } else if (!posPredicate.test(pos)) { return pos; } else { BlockPos blockPos = pos.above(); while (blockPos.getY() < maxY && posPredicate.test(blockPos)) { blockPos = blockPos.above(); } BlockPos blockPos2 = blockPos; while (blockPos2.getY() < maxY && blockPos2.getY() - blockPos.getY() < aboveSolidAmount) { BlockPos blockPos3 = blockPos2.above(); if (posPredicate.test(blockPos3)) { break; } blockPos2 = blockPos3; } return blockPos2; } } @Nullable public static Vec3 generateRandomPos(PathfinderMob mob, Supplier posSupplier) { return generateRandomPos(posSupplier, mob::getWalkTargetValue); } /** * Tries 10 times to maximize the return value of the position to double function based on the supplied position */ @Nullable public static Vec3 generateRandomPos(Supplier posSupplier, ToDoubleFunction toDoubleFunction) { double d = Double.NEGATIVE_INFINITY; BlockPos blockPos = null; for (int i = 0; i < 10; i++) { BlockPos blockPos2 = (BlockPos)posSupplier.get(); if (blockPos2 != null) { double e = toDoubleFunction.applyAsDouble(blockPos2); if (e > d) { d = e; blockPos = blockPos2; } } } return blockPos != null ? Vec3.atBottomCenterOf(blockPos) : null; } /** * @return a random position within range, only if the mob is currently restricted */ public static BlockPos generateRandomPosTowardDirection(PathfinderMob mob, int range, RandomSource random, BlockPos pos) { int i = pos.getX(); int j = pos.getZ(); if (mob.hasRestriction() && range > 1) { BlockPos blockPos = mob.getRestrictCenter(); if (mob.getX() > blockPos.getX()) { i -= random.nextInt(range / 2); } else { i += random.nextInt(range / 2); } if (mob.getZ() > blockPos.getZ()) { j -= random.nextInt(range / 2); } else { j += random.nextInt(range / 2); } } return BlockPos.containing(i + mob.getX(), pos.getY() + mob.getY(), j + mob.getZ()); } }