minecraft-src/net/minecraft/world/entity/ai/util/RandomPos.java
2025-07-04 02:00:41 +03:00

155 lines
5.5 KiB
Java

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.core.Direction;
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<BlockPos> posPredicate) {
if (!posPredicate.test(pos)) {
return pos;
} else {
BlockPos.MutableBlockPos mutableBlockPos = pos.mutable().move(Direction.UP);
while (mutableBlockPos.getY() <= maxY && posPredicate.test(mutableBlockPos)) {
mutableBlockPos.move(Direction.UP);
}
return mutableBlockPos.immutable();
}
}
/**
* 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<BlockPos> posPredicate) {
if (aboveSolidAmount < 0) {
throw new IllegalArgumentException("aboveSolidAmount was " + aboveSolidAmount + ", expected >= 0");
} else if (!posPredicate.test(pos)) {
return pos;
} else {
BlockPos.MutableBlockPos mutableBlockPos = pos.mutable().move(Direction.UP);
while (mutableBlockPos.getY() <= maxY && posPredicate.test(mutableBlockPos)) {
mutableBlockPos.move(Direction.UP);
}
int i = mutableBlockPos.getY();
while (mutableBlockPos.getY() <= maxY && mutableBlockPos.getY() - i < aboveSolidAmount) {
mutableBlockPos.move(Direction.UP);
if (posPredicate.test(mutableBlockPos)) {
mutableBlockPos.move(Direction.DOWN);
break;
}
}
return mutableBlockPos.immutable();
}
}
@Nullable
public static Vec3 generateRandomPos(PathfinderMob mob, Supplier<BlockPos> 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<BlockPos> posSupplier, ToDoubleFunction<BlockPos> 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());
}
}