/*
* ReActions, Minecraft bukkit plugin
* (c)2012-2017, fromgate, fromgate@gmail.com
* http://dev.bukkit.org/server-mods/reactions/
*
* This file is part of ReActions.
*
* ReActions is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ReActions is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ReActions. If not, see <http://www.gnorg/licenses/>.
*
*/
package me.fromgate.reactions.util;
import me.fromgate.reactions.ReActions;
import me.fromgate.reactions.externals.RAWorldGuard;
import me.fromgate.reactions.util.message.M;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.util.Vector;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
public class Locator {
private final static Pattern FLOAT = Pattern.compile("-?[0-9]+\\.?[0-9]*");
private static Map<String, TpLoc> tports = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
/**
* Returns location defined by group of parameter:
*
* @param param - String that contains parameters:
* parameter1:value1 parameter2:value2 ... parameterN:valueN
* Parameters:
* <WorldName,X,Y,Z[,Yaw,Pitch]> - defaut, simplest way to define location
* loc:<WorldName,X,Y,Z[,Yaw,Pitch]> - same as previous
* <p>
* loc:<WorldName,X,Y,Z[,Yaw,Pitch]> radius:<Radius> - find random location around the defined block
* region:<RegionName> - find random location in provided region
* loc1:<WorldName,X,Y,Z> loc2:<WorldName,X,Y,Z> - find random location in area defined by too points
* <p>
* Additional parameters:
* land:true - forces to find location in point where player can stay (solid block with two blocks above it)
* add-vector:<X,Y,Z> - allows to modify result of locations selections. For examample,
* loc:world,10,10,10 add-vector:0,5,0 will point to ation world,10,15,10.
* @param defaultLocation - default location, used when definitions of locations is wrong or missed
* @return Location
*/
public static Location parseLocation(String param, Location defaultLocation) {
Param params = new Param(param, "loc");
return parseLocation(params, defaultLocation);
}
public static Location parseLocation(Param params, Location defaultLocation) {
Location location = null;
if (params.isParamsExists("loc")) {
String locStr = params.getParam("loc", "");
location = Locator.getTpLoc(locStr);
if (location == null) location = parseCoordinates(locStr);
}
boolean land = params.getParam("land", true);
if (params.isParamsExists("region")) {
location = getRegionLocation(params.getParam("region", ""), land);
location = copyYawPitch(location, defaultLocation);
}
if (params.isParamsExists("loc1", "loc2")) {
Location loc1 = parseCoordinates(params.getParam("loc1", ""));
Location loc2 = parseCoordinates(params.getParam("loc2", ""));
if (loc1 != null && loc2 != null) {
location = getCubeLocation(loc1, loc2, land);
location = copyYawPitch(location, defaultLocation);
}
}
if (params.isParamsExists("radius")) {
int radius = params.getParam("radius", -1);
if (radius > 0) {
location = getRadiusLocation(location == null ? defaultLocation : location, radius, land);
location = copyYawPitch(location, defaultLocation);
}
}
Vector vector = Locator.parseVector(params.getParam("add-vector", ""));
if (vector == null) vector = new Vector(0, 0, 0);
Location result = location == null ? defaultLocation : location;
if (result != null) result.add(vector);
return result;
}
public static Location copyYawPitch(Location targetLoc, Location sourceLoc) {
if (targetLoc == null || sourceLoc == null) return targetLoc;
targetLoc.setYaw(sourceLoc.getYaw());
targetLoc.setPitch(sourceLoc.getPitch());
return targetLoc;
}
public static Location getCubeLocation(Location loc1, Location loc2, boolean land) {
List<Location> minmax = new ArrayList<>();
minmax.add(new Location(loc1.getWorld(), Math.min(loc1.getBlockX(), loc2.getBlockX()),
Math.min(loc1.getBlockY(), loc2.getBlockY()),
Math.min(loc1.getBlockZ(), loc2.getBlockZ())));
minmax.add(new Location(loc1.getWorld(), Math.max(loc1.getBlockX(), loc2.getBlockX()),
Math.max(loc1.getBlockY(), loc2.getBlockY()),
Math.max(loc1.getBlockZ(), loc2.getBlockZ())));
return getMinMaxLocation(minmax, land);
}
public static Location getRegionLocation(String regionStr, boolean land) {
List<Location> minmax = RAWorldGuard.getRegionMinMaxLocations(regionStr);
if (minmax.isEmpty()) return null;
return getMinMaxLocation(minmax, land);
}
public static Location getRadiusLocation(Location center, int radius, boolean land) {
List<Location> locs = new ArrayList<>();
if (radius <= 16) {
for (int x = -radius; x <= radius; x++)
for (int y = -radius; y <= radius; y++)
for (int z = -radius; z <= radius; z++) {
Location loc = (new Location(center.getWorld(),
center.getBlockX() + x,
center.getBlockY() + y,
center.getBlockZ() + z)).add(0.5, 0.5, 0.5);
if (loc.getBlockY() < 0 || loc.getBlockY() >= loc.getWorld().getMaxHeight()) continue;
if (loc.distance(center) <= radius) locs.add(loc);
}
} else {
int x;
int y;
int z;
do {
x = Util.getRandomInt(radius * 2 + 1) - radius;
y = Util.getRandomInt(radius * 2 + 1) - radius;
z = Util.getRandomInt(radius * 2 + 1) - radius;
} while (radius < Math.sqrt(
(double) x * (double) x +
(double) z * (double) z +
(double) y * (double) y));
for (y = Math.max(center.getBlockY() - radius, 0); y <= Math.min(center.getBlockY() + radius, center.getWorld().getMaxHeight() - 1); y++) {
if (radius < Math.sqrt((double) x * (double) x + (double) z * (double) z + (double) y * (double) y))
locs.add(new Location(center.getWorld(), center.getBlockX() + x, y, center.getBlockZ() + z));
}
}
if (locs.isEmpty()) locs.add(center);
return getEmptyOrLandedLocations(locs, land);
}
public static Location parseCoordinates(String strloc) {
Location loc;
if (strloc.isEmpty()) return null;
String[] ln = strloc.split(",");
if (!((ln.length == 4) || (ln.length == 6))) return null;
World w = Bukkit.getWorld(ln[0]);
if (w == null) return null;
for (int i = 1; i < ln.length; i++) {
if (!FLOAT.matcher(ln[i]).matches()) return null;
}
loc = new Location(w, Double.parseDouble(ln[1]), Double.parseDouble(ln[2]), Double.parseDouble(ln[3]));
if (ln.length == 6) {
loc.setYaw(Float.parseFloat(ln[4]));
loc.setPitch(Float.parseFloat(ln[5]));
}
return loc;
}
public static Vector parseVector(String vectorStr) {
if (vectorStr.isEmpty()) return null;
String[] ln = vectorStr.split(",");
if (ln.length != 3) return null;
for (String s : ln) {
if (!FLOAT.matcher(s).matches()) return null;
}
return new Vector(Double.parseDouble(ln[0]), Double.parseDouble(ln[1]), Double.parseDouble(ln[2]));
}
private static boolean isEmptyLocation(Location loc) {
Block block = loc.getBlock();
if (!block.isEmpty()) return false;
return block.getRelative(BlockFace.UP).isEmpty();
}
private static boolean isLandedLocation(Location loc) {
Block block = loc.getBlock();
if (!block.isEmpty()) return false;
if (block.getRelative(BlockFace.DOWN).isEmpty()) return false;
return block.getRelative(BlockFace.UP).isEmpty();
}
private static Location getRandomLocation(List<Location> locs) {
if (locs.isEmpty()) return null;
return locs.get(Util.getRandomInt(locs.size()));
}
private static Location getEmptyOrLandedLocations(List<Location> locs, boolean land) {
List<Location> landLocs = new ArrayList<>();
for (Location loc : locs) {
if (land) {
if (isLandedLocation(loc)) landLocs.add(loc);
} else {
if (isEmptyLocation(loc)) landLocs.add(loc);
}
}
return landLocs.isEmpty() ? getRandomLocation(locs) : getRandomLocation(landLocs);
}
private static Location getMinMaxLocation(List<Location> minmax, boolean land) {
if (minmax.isEmpty()) return null;
int x = minmax.get(0).getBlockX() + Util.getRandomInt(minmax.get(1).getBlockX() - minmax.get(0).getBlockX() + 1);
int z = minmax.get(0).getBlockZ() + Util.getRandomInt(minmax.get(1).getBlockZ() - minmax.get(0).getBlockZ() + 1);
List<Location> locations = new ArrayList<>();
for (int y = minmax.get(0).getBlockY(); y <= minmax.get(1).getBlockY(); y++) {
locations.add(new Location(minmax.get(0).getWorld(), x, y, z));
}
return getEmptyOrLandedLocations(locations, land);
}
public static String locationToStringFormated(Location loc) {
if (loc == null) return "";
DecimalFormat fmt = new DecimalFormat("####0.##");
String lstr = loc.toString();
try {
lstr = "[" + loc.getWorld().getName() + "] " + fmt.format(loc.getX()) + ", " + fmt.format(loc.getY()) + ", " + fmt.format(loc.getZ());
} catch (Exception ignored) {
}
return lstr;
}
public static String locationToString(Block block) {
if (block == null) return "";
return block.getWorld().getName()
+ "," + block.getX()
+ "," + block.getY()
+ "," + block.getZ();
}
public static String locationToString(Location loc) {
if (loc == null) return "";
return loc.getWorld().getName() + "," +
trimDouble(loc.getX()) + "," +
trimDouble(loc.getY()) + "," +
trimDouble(loc.getZ()) + "," +
(float) trimDouble(loc.getYaw()) + "," +
(float) trimDouble(loc.getPitch());
}
public static double trimDouble(double d) {
int i = (int) (d * 1000);
return ((double) i) / 1000;
}
/////////////////////////////////
public static void saveLocs() {
try {
File f = new File(ReActions.instance.getDataFolder() + File.separator + "locations.yml");
if (f.exists()) f.delete();
if (tports.size() > 0) {
f.createNewFile();
YamlConfiguration lcs = new YamlConfiguration();
for (String key : tports.keySet()) {
lcs.set(key + ".world", tports.get(key).world);
lcs.set(key + ".x", tports.get(key).x);
lcs.set(key + ".y", tports.get(key).y);
lcs.set(key + ".z", tports.get(key).z);
lcs.set(key + ".yaw", tports.get(key).yaw);
lcs.set(key + ".pitch", tports.get(key).pitch);
}
lcs.save(f);
}
} catch (Exception ignore) {
}
}
public static void loadLocs() {
tports.clear();
try {
File f = new File(ReActions.instance.getDataFolder() + File.separator + "locations.yml");
if (f.exists()) {
YamlConfiguration lcs = new YamlConfiguration();
lcs.load(f);
for (String key : lcs.getKeys(false))
tports.put(key, new TpLoc(lcs.getString(key + ".world"),
lcs.getDouble(key + ".x"),
lcs.getDouble(key + ".y"),
lcs.getDouble(key + ".z"),
(float) lcs.getDouble(key + ".yaw"),
(float) lcs.getDouble(key + ".pitch")));
}
} catch (Exception ignore) {
}
}
public static boolean containsTpLoc(String locstr) {
return tports.containsKey(locstr);
}
public static Location getTpLoc(String locstr) {
if (tports.containsKey(locstr)) return tports.get(locstr).getLocation();
return null;
}
public static int sizeTpLoc() {
return tports.size();
}
public static boolean addTpLoc(String id, Location loc) {
if (id.isEmpty()) return false;
tports.put(id, new TpLoc(loc));
return true;
}
public static boolean removeTpLoc(String id) {
if (!tports.containsKey(id)) return false;
tports.remove(id);
return true;
}
public static void printLocList(CommandSender sender, int pageNum, int linesPerPage) {
List<String> locList = new ArrayList<>();
for (String loc : tports.keySet()) {
locList.add("&3" + loc + " &a" + tports.get(loc).toString());
}
M.printPage(sender, locList, M.MSG_LISTLOC, pageNum, linesPerPage, true);
}
}