package net.aufdemrand.denizen.utilities;
import net.aufdemrand.denizen.Settings;
import net.aufdemrand.denizen.nms.NMSHandler;
import net.aufdemrand.denizen.nms.interfaces.BlockHelper;
import net.aufdemrand.denizen.npc.traits.TriggerTrait;
import net.aufdemrand.denizen.objects.dNPC;
import net.aufdemrand.denizen.objects.dPlayer;
import net.aufdemrand.denizen.tags.BukkitTagContext;
import net.aufdemrand.denizencore.tags.TagManager;
import net.aufdemrand.denizencore.utilities.CoreUtilities;
import net.aufdemrand.denizencore.utilities.debugging.dB;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* This class has utility methods for various tasks.
*/
public class Utilities {
public static boolean isSafeFile(File f) {
if (Settings.allowStupids()) {
return true;
}
try {
String lown = CoreUtilities.toLowerCase(f.getCanonicalPath()).replace('\\', '/');
if (dB.verbose) {
dB.log("Checking file : " + lown);
}
if (lown.contains("denizen/config.yml")) {
return false;
}
if (lown.contains("denizen/scripts/")) {
return false;
}
if (lown.endsWith(".jar") || lown.endsWith(".java")) {
return false;
}
return true;
}
catch (Exception ex) {
dB.echoError(ex);
return false;
}
}
/**
* Gets a Location within a range that an entity can walk in.
*
* @param location the Location to check with
* @param range the range around the Location
* @return a random Location within range, or null if no Location within range is safe
*/
public static Location getWalkableLocationNear(Location location, int range) {
List<Location> locations = new ArrayList<Location>();
location = location.getBlock().getLocation();
// Loop through each location within the range
for (double x = -(range); x <= range; x++) {
for (double y = -(range); y <= range; y++) {
for (double z = -(range); z <= range; z++) {
// Add each block location within range
Location loc = location.clone().add(x, y, z);
if (checkLocation(location, loc, range) && isWalkable(loc)) {
locations.add(loc);
}
}
}
}
// No safe Locations found
if (locations.isEmpty()) {
return null;
}
// Return a random Location from the list
return locations.get(CoreUtilities.getRandom().nextInt(locations.size()));
}
// TODO: Javadocs, comments
//
public static boolean isWalkable(Location location) {
BlockHelper blockHelper = NMSHandler.getInstance().getBlockHelper();
return !blockHelper.isSafeBlock(location.clone().subtract(0, 1, 0).getBlock().getType())
&& blockHelper.isSafeBlock(location.getBlock().getType())
&& blockHelper.isSafeBlock(location.clone().add(0, 1, 0).getBlock().getType());
}
// TODO: Javadocs, comments
//
public static String[] wrapWords(String text, int width) {
StringBuilder sb = new StringBuilder(text);
int i = 0;
while (i + width < sb.length() && (i = sb.lastIndexOf(" ", i + width)) != -1) {
sb.replace(i, i + 1, "\n");
}
return sb.toString().split("\n");
}
/**
* @param player the player doing the talking
* @param npc the npc being talked to
* @param range the range, in blocks, that 'bystanders' will hear he chat
*/
public static void talkToNPC(String message, dPlayer player, dNPC npc, double range) {
String replacer = String.valueOf((char) 0x04);
// Get formats from Settings, and fill in <TEXT>
String talkFormat = Settings.chatToNpcFormat()
.replaceAll("(?i)<TEXT>", replacer);
String bystanderFormat = Settings.chatToNpcOverheardFormat()
.replaceAll("(?i)<TEXT>", replacer);
// Fill in tags // TODO: Debug option?
talkFormat = TagManager.tag(talkFormat, new BukkitTagContext(player, npc, false, null, true, null)).replace(replacer, message);
bystanderFormat = TagManager.tag(bystanderFormat, new BukkitTagContext(player, npc, false, null, true, null)).replace(replacer, message);
// Send message to player
player.getPlayerEntity().sendMessage(talkFormat);
// Send message to bystanders
for (Player target : Bukkit.getOnlinePlayers()) {
if (target != player.getPlayerEntity()) {
if (target.getWorld().equals(player.getPlayerEntity().getWorld())
&& target.getLocation().distance(player.getPlayerEntity().getLocation()) <= range) {
target.sendMessage(bystanderFormat);
}
}
}
}
public static int lastIndexOfUCL(String str) {
for (int i = str.length() - 1; i >= 0; i--) {
if (Character.isUpperCase(str.charAt(i))) {
return i;
}
}
return -1;
}
/**
* Checks if c is in between a and b, or equal to a or b.
*
* @param a first number
* @param b second number
* @param c number to check if between
* @return true if c is in between.
*/
public static boolean isBetween(double a, double b, double c) {
return b > a ? (c >= a && c < b) : (c >= b && c < a); // Cuboid's have to be compensated for weirdly
}
/**
* Finds the closest NPC to a particular location.
*
* @param location The location to find the closest NPC to.
* @param range The maximum range to look for the NPC.
* @return The closest NPC to the location, or null if no NPC was found
* within the range specified.
*/
public static dNPC getClosestNPC_ChatTrigger(Location location, int range) {
dNPC closestNPC = null;
double closestDistance = Math.pow(range, 2);
// TODO: Why is this manually iterating?
Iterator<dNPC> it = DenizenAPI.getSpawnedNPCs().iterator();
while (it.hasNext()) {
dNPC npc = it.next();
Location loc = npc.getLocation();
if (npc.getCitizen().hasTrait(TriggerTrait.class) && npc.getTriggerTrait().hasTrigger("CHAT") &&
loc.getWorld().equals(location.getWorld())
&& loc.distanceSquared(location) < closestDistance) {
closestNPC = npc;
closestDistance = npc.getLocation().distanceSquared(location);
}
}
return closestNPC;
}
/**
* Checks entity's location against a Location (with leeway). Should be faster than
* bukkit's built in Location.distance(Location) since there's no sqrt math.
* <p/>
* Thanks chainsol :)
*
* @return true if within the specified location, false otherwise.
*/
public static boolean checkLocation(LivingEntity entity, Location theLocation, double theLeeway) {
if (entity.getWorld() != theLocation.getWorld()) {
return false;
}
Location entityLocation = entity.getLocation();
if (Math.abs(entityLocation.getX() - theLocation.getX())
> theLeeway) {
return false;
}
if (Math.abs(entityLocation.getY() - theLocation.getY())
> theLeeway) {
return false;
}
if (Math.abs(entityLocation.getZ() - theLocation.getZ())
> theLeeway) {
return false;
}
return true;
}
/**
* Checks entity's location against a Location (with leeway). Should be faster than
* bukkit's built in Location.distance(Location) since there's no sqrt math.
*
* @return true if within the specified location, false otherwise.
*/
public static boolean checkLocation(Location baseLocation, Location theLocation, double theLeeway) {
if (!baseLocation.getWorld().getName().equals(theLocation.getWorld().getName())) {
return false;
}
return baseLocation.distanceSquared(theLocation) < theLeeway * theLeeway;
}
/**
* Set the lines on a sign to the strings in a string array
*
* @param sign The sign
* @param lines The string array
*/
public static void setSignLines(Sign sign, String[] lines) {
for (int n = 0; n < 4; n++) {
sign.setLine(n, lines[n]);
}
sign.update();
}
/**
* Make a wall sign attach itself to an available surface
*
* @param signState The sign's blockState
*/
public static void setSignRotation(BlockState signState) {
BlockFace[] blockFaces = {BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH};
for (BlockFace blockFace : blockFaces) {
Block block = signState.getBlock().getRelative(blockFace);
if ((block.getType() != Material.AIR)
&& block.getType() != Material.SIGN_POST
&& block.getType() != Material.WALL_SIGN) {
((org.bukkit.material.Sign) signState.getData())
.setFacingDirection(blockFace.getOppositeFace());
signState.update();
}
}
}
// TODO: Javadocs, comments
//
public static void setSignRotation(BlockState signState, String direction) {
BlockFace[] blockFaces = {BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH};
for (BlockFace blockFace : blockFaces) {
if (blockFace.name().startsWith(direction.toUpperCase().substring(0, 1))) {
((org.bukkit.material.Sign) signState.getData())
.setFacingDirection(blockFace);
}
}
signState.update();
}
/**
* Check if a block location equals another location.
*
* @param block The block location to check for.
* @param location The location to check against.
* @return Whether or not the block location equals the location.
*/
public static boolean isBlock(Location block, Location location) {
if (!block.getWorld().getName().equals(location.getWorld().getName())) {
return false;
}
if (Math.abs(block.getBlockX() - location.getBlockX())
> 0) {
return false;
}
if (Math.abs(block.getBlockY() - location.getBlockY())
> 0) {
return false;
}
if (Math.abs(block.getBlockZ() - location.getBlockZ())
> 0) {
return false;
}
return true;
}
/**
* Extract a file from a zip or jar.
*
* @param jarFile The zip/jar file to use
* @param fileName Which file to extract
* @param destDir Where to extract it to
*/
public static void extractFile(File jarFile, String fileName, String destDir) {
java.util.jar.JarFile jar = null;
try {
jar = new java.util.jar.JarFile(jarFile);
java.util.Enumeration myEnum = jar.entries();
while (myEnum.hasMoreElements()) {
java.util.jar.JarEntry file = (java.util.jar.JarEntry) myEnum.nextElement();
if (file.getName().equalsIgnoreCase(fileName)) {
java.io.File f = new java.io.File(destDir + "/" + file.getName());
if (file.isDirectory()) {
continue;
}
java.io.InputStream is = jar.getInputStream(file);
java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
while (is.available() > 0) {
fos.write(is.read());
}
fos.close();
is.close();
return;
}
}
dB.echoError(fileName + " not found in the jar!");
}
catch (IOException e) {
dB.echoError(e);
}
finally {
if (jar != null) {
try {
jar.close();
}
catch (IOException e) {
dB.echoError(e);
}
}
}
}
private final static String colors = "0123456789abcdefklmnorABCDEFKLMNOR";
public static String generateRandomColors(int count) {
String ret = "";
for (int i = 0; i < count; i++) {
ret += String.valueOf(ChatColor.COLOR_CHAR) + colors.charAt(CoreUtilities.getRandom().nextInt(34));
}
return ret;
}
}