package com.leontg77.uhc.utils; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.TravelAgent; import org.bukkit.World.Environment; import org.bukkit.WorldBorder; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; /** * Location utilities class. * <p> * Contains location related methods. * * @author LeonTG77, with help from ghowden and D4mnX */ public class LocationUtils { private static BlockFace[] faces = new BlockFace[] { BlockFace.SELF, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH_EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH_WEST, BlockFace.NORTH_WEST}; /** * Check if the given block is nearby the given location. * * @param material the type of the block * @param location the location. * @return <code>True</code> if blocks nearby has the checked type, <code>false</code> otherwise. * * @author D4mnX */ public static boolean hasBlockNearby(Material material, Location location) { Block block = location.getBlock(); for (BlockFace face : faces) { if (block.getRelative(face).getType() == material) { return true; } } return false; } /** * Get the highest block at the given location. * * @param loc the location. * @return The highest block. */ public static Location getHighestBlock(Location loc) { int highest = loc.getWorld().getEnvironment() == Environment.NETHER ? 127 : 255; for (int i = highest; i >= 0; i--) { if (loc.getWorld().getBlockAt(loc.getBlockX(), i, loc.getBlockZ()).getType() != Material.AIR) { return loc.getWorld().getBlockAt(loc.getBlockX(), i, loc.getBlockZ()).getLocation(); } } return loc; } /** * Check if the given location is outside the border. * * @param loc the location. * @return True if it is, false otherwise. * * @author ghowden */ public static boolean isOutsideOfBorder(Location loc) { WorldBorder border = loc.getWorld().getWorldBorder(); double size = border.getSize(); double x = loc.getX() - border.getCenter().getX(); double z = loc.getZ() - border.getCenter().getZ(); return Math.abs(x) < size && Math.abs(z) < size; } /** * Finds a safe location inside the border. If the X or Z value is outside of the border they are set to be within * the border with the specified buffer. If no safe teleportable Y coordinate was found for the new location then * the Y location will be set to -1 and IS NOT SAFE FOR TELEPORTING. If the location is already inside the border * then it will be returned the without modification * * @param loc the location to check * @return a location within the border * * @author ghowden */ public static Location findSafeLocationInsideBorder(Location loc, int buffer, TravelAgent travel) { WorldBorder border = loc.getWorld().getWorldBorder(); Location centre = border.getCenter(); Location pos = loc.subtract(centre); double size = border.getSize() / 2; double bufferSize = size - buffer; double x = pos.getX(); double z = pos.getZ(); boolean changed = false; if (Math.abs(x) > size) { pos.setX(x > 0 ? bufferSize : -bufferSize); changed = true; } if (Math.abs(z) > size) { pos.setZ(z > 0 ? bufferSize : -bufferSize); changed = true; } if (!changed) { return loc; } pos.setY(highestTeleportableYAtLocation(pos.add(centre))); Location to = travel.findOrCreate(pos); if (!isOutsideOfBorder(to)) { pos = to; } return pos; } /** * Checks for the highest non-air block with 2 air blocks above it. * * @param location the location whose X,Z coordinates are used * @return -1 if no valid location found, otherwise coordinate with non-air Y coord with 2 air blocks above it * * @author ghowden */ public static int highestTeleportableYAtLocation(Location location) { Location startingLocation = location.clone(); startingLocation.setY(location.getWorld().getMaxHeight()); boolean above2WasAir = false; boolean aboveWasAir = false; Block currentBlock = startingLocation.getBlock(); while (currentBlock.getY() >= 0) { if (currentBlock.getType() != Material.AIR) { if (above2WasAir && aboveWasAir) { return currentBlock.getY(); } above2WasAir = aboveWasAir; aboveWasAir = false; } else { above2WasAir = aboveWasAir; aboveWasAir = true; } currentBlock = currentBlock.getRelative(BlockFace.DOWN); } return -1; } }