package org.bukkit.command.defaults;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Team;
public class SpreadPlayersCommand extends VanillaCommand {
private static final Random random = new Random();
public SpreadPlayersCommand() {
super("spreadplayers");
this.description = "Spreads players around a point";
this.usageMessage = "/spreadplayers <x> <z> <spreadDistance> <maxRange> <respectTeams true|false> <player ...>";
this.setPermission("bukkit.command.spreadplayers");
}
@Override
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
if (!testPermission(sender)) {
return true;
}
if (args.length < 6) {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return false;
}
final double x = getDouble(sender, args[0], -30000000, 30000000);
final double z = getDouble(sender, args[1], -30000000, 30000000);
final double distance = getDouble(sender, args[2]);
final double range = getDouble(sender, args[3]);
if (distance < 0.0D) {
sender.sendMessage(ChatColor.RED + "Distance is too small.");
return false;
}
if (range < distance + 1.0D) {
sender.sendMessage(ChatColor.RED + "Max range is too small.");
return false;
}
final String respectTeams = args[4];
boolean teams = false;
if (respectTeams.equalsIgnoreCase("true")) {
teams = true;
} else if (!respectTeams.equalsIgnoreCase("false")) {
sender.sendMessage(String.format(ChatColor.RED + "'%s' is not true or false", args[4]));
return false;
}
List<Player> players = Lists.newArrayList();
World world = null;
for (int i = 5; i < args.length; i++) {
Player player = Bukkit.getPlayerExact(args[i]);
if (player == null) {
continue;
}
if (world == null) {
world = player.getWorld();
}
players.add(player);
}
if (world == null) {
return true;
}
final double xRangeMin = x - range;
final double zRangeMin = z - range;
final double xRangeMax = x + range;
final double zRangeMax = z + range;
final int spreadSize = teams ? getTeams(players) : players.size();
final Location[] locations = getSpreadLocations(world, spreadSize, xRangeMin, zRangeMin, xRangeMax, zRangeMax);
final int rangeSpread = range(world, distance, xRangeMin, zRangeMin, xRangeMax, zRangeMax, locations);
if (rangeSpread == -1) {
sender.sendMessage(String.format("Could not spread %d %s around %s,%s (too many players for space - try using spread of at most %s)", spreadSize, teams ? "teams" : "players", x, z));
return false;
}
final double distanceSpread = spread(world, players, locations, teams);
sender.sendMessage(String.format("Succesfully spread %d %s around %s,%s", locations.length, teams ? "teams" : "players", x, z));
if (locations.length > 1) {
sender.sendMessage(String.format("(Average distance between %s is %s blocks apart after %s iterations)", teams ? "teams" : "players", String.format("%.2f", distanceSpread), rangeSpread));
}
return true;
}
private int range(World world, double distance, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax, Location[] locations) {
boolean flag = true;
double max;
int i;
for (i = 0; i < 10000 && flag; ++i) {
flag = false;
max = Float.MAX_VALUE;
Location loc1;
int j;
for (int k = 0; k < locations.length; ++k) {
Location loc2 = locations[k];
j = 0;
loc1 = new Location(world, 0, 0, 0);
for (int l = 0; l < locations.length; ++l) {
if (k != l) {
Location loc3 = locations[l];
double dis = loc2.distanceSquared(loc3);
max = Math.min(dis, max);
if (dis < distance) {
++j;
loc1.add(loc3.getX() - loc2.getX(), 0, 0);
loc1.add(loc3.getZ() - loc2.getZ(), 0, 0);
}
}
}
if (j > 0) {
loc2.setX(loc2.getX() / j);
loc2.setZ(loc2.getZ() / j);
double d7 = Math.sqrt(loc1.getX() * loc1.getX() + loc1.getZ() * loc1.getZ());
if (d7 > 0.0D) {
loc1.setX(loc1.getX() / d7);
loc2.add(-loc1.getX(), 0, -loc1.getZ());
} else {
double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin;
double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin;
loc2.setX(x);
loc2.setZ(z);
}
flag = true;
}
boolean swap = false;
if (loc2.getX() < xRangeMin) {
loc2.setX(xRangeMin);
swap = true;
} else if (loc2.getX() > xRangeMax) {
loc2.setX(xRangeMax);
swap = true;
}
if (loc2.getZ() < zRangeMin) {
loc2.setZ(zRangeMin);
swap = true;
} else if (loc2.getZ() > zRangeMax) {
loc2.setZ(zRangeMax);
swap = true;
}
if (swap) {
flag = true;
}
}
if (!flag) {
Location[] locs = locations;
int i1 = locations.length;
for (j = 0; j < i1; ++j) {
loc1 = locs[j];
if (world.getHighestBlockYAt(loc1) == 0) {
double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin;
double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin;
locations[i] = (new Location(world, x, 0, z));
loc1.setX(x);
loc1.setZ(z);
flag = true;
}
}
}
}
if (i >= 10000) {
return -1;
} else {
return i;
}
}
private double spread(World world, List<Player> list, Location[] locations, boolean teams) {
double distance = 0.0D;
int i = 0;
Map<Team, Location> hashmap = Maps.newHashMap();
for (int j = 0; j < list.size(); ++j) {
Player player = list.get(j);
Location location;
if (teams) {
Team team = player.getScoreboard().getPlayerTeam(player);
if (!hashmap.containsKey(team)) {
hashmap.put(team, locations[i++]);
}
location = hashmap.get(team);
} else {
location = locations[i++];
}
player.teleport(new Location(world, Math.floor(location.getX()) + 0.5D, world.getHighestBlockYAt((int) location.getX(), (int) location.getZ()), Math.floor(location.getZ()) + 0.5D));
double value = Double.MAX_VALUE;
for (int k = 0; k < locations.length; ++k) {
if (location != locations[k]) {
double d = location.distanceSquared(locations[k]);
value = Math.min(d, value);
}
}
distance += value;
}
distance /= list.size();
return distance;
}
private int getTeams(List<Player> players) {
Set<Team> teams = Sets.newHashSet();
for (Player player : players) {
teams.add(player.getScoreboard().getPlayerTeam(player));
}
return teams.size();
}
private Location[] getSpreadLocations(World world, int size, double xRangeMin, double zRangeMin, double xRangeMax, double zRangeMax) {
Location[] locations = new Location[size];
for (int i = 0; i < size; ++i) {
double x = xRangeMin >= xRangeMax ? xRangeMin : random.nextDouble() * (xRangeMax - xRangeMin) + xRangeMin;
double z = zRangeMin >= zRangeMax ? zRangeMin : random.nextDouble() * (zRangeMax - zRangeMin) + zRangeMin;
locations[i] = (new Location(world, x, 0, z));
}
return locations;
}
}