PandaLib/pandalib-paper/src/main/java/fr/pandacube/lib/paper/util/LocationUtil.java

174 lines
5.4 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package fr.pandacube.lib.paper.util;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.WorldBorder;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.scheduler.BukkitTask;
import fr.pandacube.lib.util.RandomUtil;
import fr.pandacube.lib.paper.PandaLibPaper;
public class LocationUtil {
public static String conciseToString(Location loc) {
String world = loc.getWorld() == null ? "null" : loc.getWorld().getName();
return "(" + world + ", " + loc.getBlockX() + ", " + loc.getBlockY() + ", " + loc.getBlockZ() + ")";
}
/**
* Return a random secure location in the provided world, inside the current
* WorldBorder. Will be on the surface, for non-nether world, or below the roof of the nether world
* @param w the world in which to pick a location
* @param extraSecureCheck provides extra checks to determine location security
*/
public static CompletableFuture<Location> getRandomSecureLocation(World w, Predicate<Location> extraSecureCheck) {
WorldBorder wb = w.getWorldBorder();
Location minWorld = wb.getCenter().clone().add(-wb.getSize()/2, 0, -wb.getSize()/2);
Location maxWorld = wb.getCenter().clone().add(wb.getSize()/2, 0, wb.getSize()/2);
return getRandomSecureLocation(w, minWorld, maxWorld, extraSecureCheck);
}
private static final int maxTryBeforeCancelRandomLocation = 75;
public static CompletableFuture<Location> getRandomSecureLocation(World w, Location min, Location max, Predicate<Location> extraSecureCheck) {
CompletableFuture<Location> future = new CompletableFuture<>();
AtomicReference<BukkitTask> t = new AtomicReference<>();
AtomicInteger count = new AtomicInteger(0);
t.set(Bukkit.getScheduler().runTaskTimer(PandaLibPaper.getPlugin(), () -> {
count.incrementAndGet();
if (count.get() > maxTryBeforeCancelRandomLocation) {
future.complete(null);
t.get().cancel();
}
// generate a random (x,z) coordinate
Location ret = new Location(w,
RandomUtil.rand.nextInt(min.getBlockX(), max.getBlockX()) + 0.5,
w.getMaxHeight() - 1,
RandomUtil.rand.nextInt(min.getBlockZ(), max.getBlockZ()) + 0.5);
// find a secure y value
ret = getSecureLocationOrNull(ret);
if (ret == null)
// there is no secure y position for the provided (x,z) values
return;
if (extraSecureCheck != null && !extraSecureCheck.test(ret))
return; // extra checks didn't validate the location
// all good
future.complete(ret);
t.get().cancel();
}, 1, 1));
return future;
}
/**
*
* @param l the source location
* @return a secure location with the same X and Z coordinate as the
* provided location, but Y modified to ensure security for player
* who will be teleported to this location.
* May return null if it is impossible to find a secure location.
*/
public static Location getSecureLocationOrNull(Location l) {
l = l.clone();
l.setY(l.getWorld().getEnvironment() == Environment.NETHER ? 126 : l.getWorld().getMaxHeight());
Block b = l.getBlock();
while (b.getY() >= 0 && !currPosSafe(b))
b = b.getRelative(BlockFace.DOWN);
return currPosSafe(b) ? b.getLocation().add(0.5, 0, 0.5) : null;
}
public static boolean currPosSafe(Block b) {
return b.getY() >= b.getWorld().getMinHeight() + 1 && b.getY() <= b.getWorld().getMaxHeight()
&& isSecureFloor(b.getRelative(BlockFace.DOWN))
&& isAir(b)
&& isAir(b.getRelative(BlockFace.UP));
}
public static boolean isAir(Block b) { return b.getType() == Material.AIR; }
public static boolean isSecureFloor(Block b) { return !isAir(b) && !dangerousBlocks.contains(b.getType()); }
public static final Set<Material> dangerousBlocks = EnumSet.of(
Material.LAVA,
Material.WATER,
Material.COBWEB,
Material.MAGMA_BLOCK,
Material.CAMPFIRE,
Material.SOUL_CAMPFIRE,
Material.FIRE,
Material.SOUL_FIRE,
Material.WITHER_ROSE,
Material.END_PORTAL,
Material.NETHER_PORTAL,
Material.END_GATEWAY
);
/**
* Check if the {@link Location} l is inside the cuboid formed by the 2 others
* Locations min and max.
* @return true if l is inside the cuboid min-max
*/
public static boolean isIn(Location l, Location min, Location max) {
return (l.getWorld().equals(min.getWorld()) && l.getWorld().equals(max.getWorld()) && l.getX() >= min.getX()
&& l.getX() <= max.getX() && l.getY() >= min.getY() && l.getY() <= max.getY() && l.getZ() >= min.getZ()
&& l.getZ() <= max.getZ());
}
/**
* Return a new location based on the linear interpolation between p0 and p1, according to the value c.
* @param c between 0 and 1. If 0, it returns p0 and if 1, returns p1. Other finite numbers are allowed, but the returned location won't be part of the {@code [p0;p1]} segment.
* @return The location, linearly interpolated between p0 and p1 with the value c. The yaw and pitch in the returned location are those of p0.
* @throws IllegalArgumentException if the provided locations are not in the same world.
*/
public static Location lerp(Location p0, Location p1, float c) {
return p0.clone().add(p1.clone().subtract(p0).multiply(c));
}
}