package org.bukkit.command.defaults; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; 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; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; 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, "TODO")); // TODO: Add latest argument 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; } }